diff options
Diffstat (limited to '')
322 files changed, 62892 insertions, 0 deletions
@@ -0,0 +1,13 @@ +Eric Biederman <ebiederm@xmission.com> +Albert Herranz +Jesse Barnes <jbarnes@sgi.com> +Khalid Aziz <khalid@gonehiking.org> +Hariprasad Nellitheertha <hari@in.ibm.com> +Tim Deegan <tjd21@cl.cam.ac.uk> +And others... + +Currently maintained by +P: Simon Horman +M: horms@verge.net.au +L: kexec@lists.infradead.org + @@ -0,0 +1,341 @@ + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) 19yy <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. @@ -0,0 +1,77 @@ +Installation +============ + + +Checkout from git +----------------- + +If you use the tarball, you can just skip that section. If you checked out the +kexec-tools from git repository, you have to build the Autoconf-generated +file yourself with running + + ./bootstrap + +You have to install "autoconf" for that. After that, you should have a file +called "configure" in the top-level directory of kexec-tools, and you can +continue with the next step. + + +General +------- + +You should check first if your favourite Linux distribution doesn't offer a +package. If that package is recent enough, you should consider using that +package instead. + +However, if you want to install kexec-tools from sources + + (1) Run ./configure [--prefix=/usr/local] [options]... + e.g. + + ./configure --prefix=/usr/local + + For a full list of options run + + ./configure --help + + (2) Run make + + make + + (3) To install kexec-tools + After following the above instructions to build-kexec tools, run + + make install + + +Static compilation +------------------ + +To compile kexec-tools statically (i.e., independent of libraries that are +present on the system), use + + LDFLAGS=-static ./configure <options> + +instead. + + +Cross compilation +----------------- + +Because kexec is also used in the embedded world, cross-compilation is +supported. You cannot only set "CC", "LD", "CCFLAGS" and "LDFLAGS" environment +variables for the ./configure call as usual, you can also set + + TARGET_CC -- the C compiler used for files that should run on the + target + TARGET_CCFLAGS -- compilation flags for $TARGET_CC + TARGET_LD -- linker used for files that should run on the + target + +Because the build process also requires files that must be executed during the +build, you also need a host compiler, only the target compiler is not enough. + + BUILD_CC -- the C compiler used for files that should run on the + host (the system that builds the kexec-tools) + BUILD_CFLAGS -- compilation flags for $BUILD_CC + diff --git a/Makefile.in b/Makefile.in new file mode 100644 index 0000000..09bbd5c --- /dev/null +++ b/Makefile.in @@ -0,0 +1,396 @@ +# Hey Emacs this is a -*- makefile-*- + +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ + +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ + + +# The target architecture +ARCH = @ARCH@ +SUBARCH = @SUBARCH@ +OBJDIR = @OBJDIR@ +target = @target@ +host = @host@ + +# Compiler for building kexec +CC = @CC@ +CPP = @CPP@ +LD = @LD@ +AS = @AS@ +OBJCOPY = @OBJCOPY@ +AR = @AR@ + +# C compiler for binaries to run during the build +BUILD_CC = @BUILD_CC@ +BUILD_CFLAGS = @BUILD_CFLAGS@ +TARGET_CC = @TARGET_CC@ +TARGET_LD = @TARGET_LD@ +TARGET_CFLAGS = @TARGET_CFLAGS@ + + +# Base compiler flags. These are extended by the subcomponent-Makefiles +# where necessary. +CPPFLAGS = @CPPFLAGS@ -I$(srcdir)/include -I$(srcdir)/util_lib/include \ + -Iinclude/ $($(ARCH)_CPPFLAGS) +CFLAGS = @CFLAGS@ -fno-strict-aliasing -Wall -Wstrict-prototypes +PURGATORY_EXTRA_CFLAGS = @PURGATORY_EXTRA_CFLAGS@ +ASFLAGS = @ASFLAGS@ $($(ARCH)_ASFLAGS) +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ + +# Utilities called by the makefiles +INSTALL = @INSTALL@ +CHMOD = @CHMOD@ +CD = cd +MKDIR = @MKDIR@ +RM = @RM@ +CP = @CP@ +LN = @LN@ +TAR = @TAR@ +RPMBUILD = @RPMBUILD@ +SED = @SED@ +FIND = @FIND@ +XARGS = @XARGS@ +DIRNAME = @DIRNAME@ +STRIP = @STRIP@ + + +pkgdatadir = $(datadir)/$(PACKAGE_NAME) +pkglibdir = $(libdir)/$(PACKAGE_NAME) +pkgincludedir = $(includedir)/$(PACKAGE_NAME) + +# You can specify DESTDIR on the command line to do a add +# a prefix to the install so it doesn't really happen +# Useful for building binary packages +DESTDIR = + +srcdir = @srcdir@ +VPATH = .:$(srcdir) + +# install paths +BUILD_PREFIX:=build +SBINDIR=$(BUILD_PREFIX)/sbin +BINDIR=$(BUILD_PREFIX)/bin +LIBEXECDIR=$(BUILD_PREFIX)/libexec +DATADIR=$(BUILD_PREFIX)/share +SYSCONFDIR=$(BUILD_PREFIX)/etc +SHAREDSTATEDIR=$(BUILD_PREFIX)/com +LOCALSTATEDIR=$(BUILD_PREFIX)/var +LIBDIR=$(BUILD_PREFIX)/lib +INFODIR=$(BUILD_PREFIX)/info +MANDIR=$(BUILD_PREFIX)/man +MAN1DIR=$(MANDIR)/man1 +MAN2DIR=$(MANDIR)/man2 +MAN3DIR=$(MANDIR)/man3 +MAN4DIR=$(MANDIR)/man4 +MAN5DIR=$(MANDIR)/man5 +MAN6DIR=$(MANDIR)/man6 +MAN7DIR=$(MANDIR)/man7 +MAN8DIR=$(MANDIR)/man8 +INCLUDEDIR=$(BUILD_PREFIX)/include + +PKGDATADIR=$(DATADIR)/$(PACKAGE_NAME) +PKGLIBDIR=$(LIBDIR)/$(PACKAGE_NAME) +PKGINCLUDEIR=$(INCLUDEDIR)/$(PACKAGE_NAME) + +ALLOWED_UNINSTALL_DIRS := \ + $(SBINDIR) \ + $(sbindir) $(bindir) $(libexecdir) $(datadir) $(sysconfdir) \ + $(sharedstatedir) $(localstatedir) $(libdir) $(infodir) \ + $(mandir)/man1 $(mandir)/man2 $(mandir)/man3 $(mandir)/man4 \ + $(mandir)/man5 $(mandir)/man6 $(mandir)/man7 $(mandir)/man8 \ + $(includedir) $(pkgdatadir) $(pkglibdir) $(pkgincludedir) + +all: targets + +# generic build rules +%.o: %.c + @$(MKDIR) -p $(@D) + $(COMPILE.c) -MD -o $@ $< + +%.o: %.S + @$(MKDIR) -p $(@D) + $(COMPILE.S) -MD -o $@ $< + +# collect objects to be removed in 'make clean' +clean = + +dist = AUTHORS COPYING INSTALL News TODO Makefile.in configure.ac configure \ + kexec-tools.spec.in config/ + +# utility function for converting a list of files (with extension) to +# file.o (or file.d) format. +objify = $(addsuffix .o, $(basename $(1))) +depify = $(addsuffix .d, $(basename $(1))) + +# Documentation +# +include $(srcdir)/doc/Makefile + +# Headers +# +include $(srcdir)/include/Makefile + +# Utility function library +# +include $(srcdir)/util_lib/Makefile + +# +# Stand alone utilities +# +include $(srcdir)/util/Makefile + +# +# purgatory (code between kernels) +# +include $(srcdir)/purgatory/Makefile + +# +# kexec (linux booting linux) +# +include $(srcdir)/kexec/Makefile + +# vmcore-dmesg (read dmesg from a vmcore) +# +include $(srcdir)/vmcore-dmesg/Makefile + +# +# kexec_test (test program) +# +include $(srcdir)/kexec_test/Makefile + +SPEC=$(PACKAGE_NAME).spec +GENERATED_SRCS:= $(SPEC) +TARBALL=$(PACKAGE_NAME)-$(PACKAGE_VERSION).tar +TARBALL.gz=$(TARBALL).gz +SRCS:= $(dist) +PSRCS:=$(foreach s, $(SRCS), $(PACKAGE_NAME)-$(PACKAGE_VERSION)/$(s)) +PGSRCS:=$(foreach s, $(GENERATED_SRCS), $(PACKAGE_NAME)-$(PACKAGE_VERSION)/$(s)) + +MAN_PAGES:=$(KEXEC_MANPAGE) $(VMCORE_DMESG_MANPAGE) +BINARIES_i386:=$(KEXEC_TEST) +BINARIES_x86_64:=$(KEXEC_TEST) +BINARIES:=$(KEXEC) $(VMCORE_DMESG) $(BINARIES_$(ARCH)) + +UNINSTALL_KDUMP = $(sbindir)/kdump +UNINSTALL_KDUMP_MANPAGE = $(mandir)/man8/kdump.8 +UNINSTALL_KEXEC = $(sbindir)/kexec +UNINSTALL_KEXEC_MANPAGE = $(mandir)/man8/kexec.8 +UNINSTALL_KEXEC_TEST = $(pkglibdir)/kexec_test +UNINSTALL_VMCORE_DMESG = $(sbindir)/vmcore-dmesg +UNINSTALL_VMCORE_DMESG_MANPAGE = $(mandir)/man8/vmcore-dmesg.8 + +TARGETS:=$(BINARIES) $(MAN_PAGES) +targets: $(TARGETS) + +UNINSTALL_TARGETS_i386:=$(UNINSTALL_KEXEC_TEST) +UNINSTALL_TARGETS_x86_64:=$(UNINSTALL_KEXEC_TEST) +UNINSTALL_TARGETS:=$(UNINSTALL_KDUMP) $(UNINSTALL_KDUMP_MANPAGE) \ + $(UNINSTALL_KEXEC) $(UNINSTALL_KEXEC_MANPAGE) \ + $(UNINSTALL_VMCORE_DMESG) $(UNINSTALL_VMCORE_DMESG_MANPAGE) \ + $(UNINSTALL_TARGETS_$(ARCH)) +uninstall-targets: $(UNINSTALL_TARGETS) + +Makefile: Makefile.in config.status + ./config.status + +configure: configure.ac + cd $(srcdir) && autoheader && autoconf && rm -rf autom4te.cache + +dist: tarball + +tarball: $(TARBALL.gz) + +$(TARBALL): $(SRCS) $(GENERATED_SRCS) + [ ! -d $(PACKAGE_NAME)-$(PACKAGE_VERSION) ] || \ + $(CHMOD) u+w $(PACKAGE_NAME)-$(PACKAGE_VERSION) + $(RM) -rf $(PACKAGE_NAME)-$(PACKAGE_VERSION) + $(MKDIR) $(PACKAGE_NAME)-$(PACKAGE_VERSION) + $(TAR) -c $(SRCS) $(GENERATED_SRCS) | \ + $(TAR) -C $(PACKAGE_NAME)-$(PACKAGE_VERSION) -x + $(TAR) -cf $@ $(PSRCS) + $(TAR) -rf $@ $(PGSRCS) + $(RM) -rf $(PACKAGE_NAME)-$(PACKAGE_VERSION) + +$(TARBALL.gz): $(TARBALL) + gzip -c < $^ > $@ + +rpm: $(TARBALL.gz) + unset MAKEFLAGS MAKELEVEL; + $(RPMBUILD) -ta $(TARBALL.gz) + +$(SPEC): kexec-tools.spec.in Makefile + $(SED) -e 's,^Version: $$,Version: $(PACKAGE_VERSION),' $< > $@ + +echo:: + @echo ARCH=$(ARCH) + @echo BINARIES=$(BINARIES) + @echo TARGETS=$(TARGETS) + @echo UNINSTALL_TARGETS=$(UNINSTALL_TARGETS) + @echo CC=$(CC) + @echo AR=$(AR) + @echo LD=$(LD) + @echo STRIP=$(STRIP) + +clean: + $(RM) -f $(clean) + $(RM) -rf build + $(RM) -f $(TARBALL) $(TARBALL.gz) + +distclean: dist-clean + +dist-clean: clean + $(RM) -f config.log config.status config.cache Makefile include/config.h + $(RM) -f include/config.h.in configure $(SPEC) + $(RM) -rf autom4te.cache + +distuninstallcheck: + FOUND=$$(find $(distuninstallcheck_dir) ! -type d) && \ + [ -z "$$FOUND" ] || { \ + echo "Unexpeced files found after uninstall:" && \ + echo "$$FOUND" && \ + exit 1; } + +distcheck: $(TARBALL) + [ ! -d $(PACKAGE_NAME)-$(PACKAGE_VERSION) ] || \ + $(CHMOD) u+w $(PACKAGE_NAME)-$(PACKAGE_VERSION) + $(RM) -rf $(PACKAGE_NAME)-$(PACKAGE_VERSION) + $(TAR) -xf $(TARBALL) + $(MKDIR) $(PACKAGE_NAME)-$(PACKAGE_VERSION)/_build \ + $(PACKAGE_NAME)-$(PACKAGE_VERSION)/_build/sub \ + $(PACKAGE_NAME)-$(PACKAGE_VERSION)/_inst \ + $(PACKAGE_NAME)-$(PACKAGE_VERSION)/_dest + $(CHMOD) a-w $(PACKAGE_NAME)-$(PACKAGE_VERSION) + test -d $(PACKAGE_NAME)-$(PACKAGE_VERSION)/_build + INSTALL_BASE=$$($(CD) $(PACKAGE_NAME)-$(PACKAGE_VERSION)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,') &&\ + DESTDIR="$(PACKAGE_NAME)-$(PACKAGE_VERSION)/_dest" && \ + $(CD) $(PACKAGE_NAME)-$(PACKAGE_VERSION)/_build/sub && \ + ../../configure \ + $(DISTCHECK_CONFIGURE_FLAGS) \ + --srcdir=../.. --prefix="$$INSTALL_BASE" && \ + $(MAKE) $(AM_MAKEFLAGS) && \ + $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$DESTDIR" install && \ + $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$DESTDIR" uninstall && \ + $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$DESTDIR" \ + distuninstallcheck + $(CHMOD) u+w $(PACKAGE_NAME)-$(PACKAGE_VERSION) + $(RM) -rf $(PACKAGE_NAME)-$(PACKAGE_VERSION) + +install: $(TARGETS) + for file in $(TARGETS) ; do \ + if test `$(DIRNAME) $$file` = "$(SBINDIR)" ; then \ + $(MKDIR) -p $(DESTDIR)/$(sbindir)/; \ + $(INSTALL) -m 755 $$file $(DESTDIR)/$(sbindir)/; \ + fi; \ + if test `$(DIRNAME) $$file` = "$(BINDIR)" ; then \ + $(MKDIR) -p $(DESTDIR)/$(bindir)/; \ + $(INSTALL) -m 755 $$file $(DESTDIR)/$(bindir)/; \ + fi; \ + if test `$(DIRNAME) $$file` = "$(LIBEXECDIR)" ; then \ + $(MKDIR) -p $(DESTDIR)/$(libexecdir)/; \ + $(INSTALL) -m 755 $$file $(DESTDIR)/$(libexecdir)/; \ + fi; \ + if test `$(DIRNAME) $$file` = "$(DATADIR)" ; then \ + $(MKDIR) -p $(DESTDIR)/$(datadir)/; \ + $(INSTALL) -m 644 $$file $(DESTDIR)/$(datadir)/; \ + fi; \ + if test `$(DIRNAME) $$file` = "$(SYSCONFDIR)" ; then \ + $(MKDIR) -p $(DESTDIR)/$(sysconfdir)/; \ + $(INSTALL) -m 644 $$file $(DESTDIR)/$(sysconfdir)/; \ + fi; \ + if test `$(DIRNAME) $$file` = "$(SHAREDSTATEDIR)" ; then \ + $(MKDIR) -p $(DESTDIR)/$(sharedstatedir)/; \ + $(INSTALL) -m 644 $$file $(DESTDIR)/$(sharedstatedir)/; \ + fi; \ + if test `$(DIRNAME) $$file` = "$(LOCALSTATEDIR)" ; then \ + $(MKDIR) -p $(DESTDIR)/$(localstatedir)/; \ + $(INSTALL) -m 644 $$file $(DESTDIR)/$(localstatedir)/; \ + fi; \ + if test `$(DIRNAME) $$file` = "$(LIBDIR)" ; then \ + $(MKDIR) -p $(DESTDIR)/$(libdir)/; \ + $(INSTALL) -m 644 $$file $(DESTDIR)/$(libdir)/; \ + fi; \ + if test `$(DIRNAME) $$file` = "$(INFODIR)" ; then \ + $(MKDIR) -p $(DESTDIR)/$(infodir)/; \ + $(INSTALL) -m 644 $$file $(DESTDIR)/$(infodir)/; \ + fi; \ + if test `$(DIRNAME) $$file` = "$(MAN1DIR)" ; then \ + $(MKDIR) -p $(DESTDIR)/$(mandir)/man1; \ + $(INSTALL) -m 644 $$file $(DESTDIR)/$(mandir)/man1; \ + fi; \ + if test `$(DIRNAME) $$file` = "$(MAN2DIR)" ; then \ + $(MKDIR) -p $(DESTDIR)/$(mandir)/man2; \ + $(INSTALL) -m 644 $$file $(DESTDIR)/$(mandir)/man2; \ + fi; \ + if test `$(DIRNAME) $$file` = "$(MAN3DIR)" ; then \ + $(MKDIR) -p $(DESTDIR)/$(mandir)/man3/; \ + $(INSTALL) -m 644 $$file $(DESTDIR)/$(mandir)/man3/; \ + fi; \ + if test `$(DIRNAME) $$file` = "$(MAN4DIR)" ; then \ + $(MKDIR) -p $(DESTDIR)/$(mandir)/man4/; \ + $(INSTALL) -m 644 $$file $(DESTDIR)/$(mandir)/man4/; \ + fi; \ + if test `$(DIRNAME) $$file` = "$(MAN5DIR)" ; then \ + $(MKDIR) -p $(DESTDIR)/$(mandir)/man5/; \ + $(INSTALL) -m 644 $$file $(DESTDIR)/$(mandir)/man5/; \ + fi; \ + if test `$(DIRNAME) $$file` = "$(MAN6DIR)" ; then \ + $(MKDIR) -p $(DESTDIR)/$(mandir)/man6/; \ + $(INSTALL) -m 644 $$file $(DESTDIR)/$(mandir)/man6/; \ + fi; \ + if test `$(DIRNAME) $$file` = "$(MAN7DIR)" ; then \ + $(MKDIR) -p $(DESTDIR)/$(mandir)/man7/; \ + $(INSTALL) -m 644 $$file $(DESTDIR)/$(mandir)/man7/; \ + fi; \ + if test `$(DIRNAME) $$file` = "$(MAN8DIR)" ; then \ + $(MKDIR) -p $(DESTDIR)/$(mandir)/man8/; \ + $(INSTALL) -m 644 $$file $(DESTDIR)/$(mandir)/man8/; \ + fi; \ + if test `$(DIRNAME) $$file` = "$(INCLUDEDIR)" ; then \ + $(MKDIR) -p $(DESTDIR)/$(includedir)/; \ + $(INSTALL) -m 644 $$file $(DESTDIR)/$(includedir)/; \ + fi; \ + if test `$(DIRNAME) $$file` = "$(PKGDATADIR)" ; then \ + $(MKDIR) -p $(DESTDIR)/$(pkgdatadir)/; \ + $(INSTALL) -m 644 $$file $(DESTDIR)/$(pkgdatadir)/; \ + fi; \ + if test `$(DIRNAME) $$file` = "$(PKGLIBDIR)" ; then \ + $(MKDIR) -p $(DESTDIR)/$(pkglibdir)/; \ + $(INSTALL) -m 644 $$file $(DESTDIR)/$(pkglibdir)/; \ + fi; \ + if test `$(DIRNAME) $$file` = "$(PKGINCLUDEDIR)" ; then \ + $(MKDIR) -p $(DESTDIR)/$(pkgincludedir)/; \ + $(INSTALL) -m 644 $$file $(DESTDIR)/$(pkgincludedir)/; \ + fi; \ + done + +uninstall: + UINSTALL_LIST='$(UNINSTALL_TARGETS)'; \ + for file in $$UINSTALL_LIST ; do \ + DIRS='$(ALLOWED_UNINSTALL_DIRS)'; \ + for dir in $$DIRS ; do \ + if test `$(DIRNAME) $$file` = "$$dir" ; then \ + rm -rf $(DESTDIR)/$$file ; \ + break ; \ + fi; \ + done; \ + done + +.PHONY: echo install uninstall distuninstallcheck all targets \ + uninstall-targets clean dist-clean distclean distcheck \ + maintainer-clean maintainerclean dist tarball rpm @@ -0,0 +1,73 @@ +* 2.0 + - In purgatory added -fno-zero-initialized-in-bss to prevent issues with + recent versions of gcc + - Add an option to configure to disable zlib support + - Add mismatched architecture support + - Updated the x86 architecture help + - Updated the x86_64 architecture help + - Fixed bzImage support + - Added support for finding either the highest or lowest usable window. + - Change the version number to 2.0 to reflect the major change in + the code base. 1.99 was effectively the release canidate. +* 1.99 + - Rearchitect so the code is maintainable. + - Add multiboot support + - Add ia64 support + - Add beoboot image support + - Create generic elf loader code. + - Created the relocated shared object purgatory to hold + the code that runs between kernels. + - Added a configure script + - Added an rpm target + - Added kexec on panic support + - Initial stab at adding documentation + - Added loader support for ET_DYN objects +* 1.98 + - Add mysteriously dropped changes to make x86_64 work + - Update the distclean target to remove *.orig and *~ files +* 1.97 + - Add support for cross compiling x86_64 +* 1.96 + - add x86_64 support + - add support for linux style arguments to the elf32-x86 loader + - disable clearing of cr4 on x86 +* 1.95 + - add kexec-zImage-ppc64.c source file + - GameCube/PPC32 sync'ed to 1.94 + - use syscall() to call sys_kexec_load() and reboot() + - add kexec-syscall.h, remove kexec-syscall.c + - makefiles know about ARCH-es + - add noifdown kexec option (Albert Herranz) +* 1.94 + - revert a bad 1.92 change (not setting optind & opterr for subsequent + calls to getopt_long()) +* 1.93 + - restored "shutdown" functionality; + - more help/usage text clarification; + - add GPLv2 license to source files (with permission from Eric Biederman) +* 1.92 + - my_kexec(): call kexec() only one time; + - add "unload" option; + - fix some compiler warnings about "<var> might be used uninitialized"; + - commented out shutdown capability since it was unreachable; +* 1.91 + - fix "-t" option: strcmp() was inverted (Albert Herranz) + - check specified kernel image file for file type (Albert Herranz) +* 1.9 + - change reboot function to return type long (was int) + - use kexec reserved syscall numbers (in Linux 2.6.6-mm3) +* 1.8 + - Fixed bug where ramdisk wasn't loaded when specified + - Memory information is now read from /proc/iomem. + Information that is not needed is ignored. +* 1.7 + - Update to new tentative syscall number.... +* 1.6 + - Redo all of the command line arguments. + - Use the 32-bit kernel entry point. + - Work around a failure to clear %cr4. +* 1.5 + - Port to a new kernel interface (Hopefully the final one). + - Start working on setting up legacy hardware + - Add --load and --exec options so the parts can be done at different times. +### @@ -0,0 +1,11 @@ +- x86 handle x86 vmlinux parameter header allocation issues. + There is a bug where it can get stomped but the current code + does not allow us much flexibility in what we do with it. +- Restore enough state that DOS/arbitrary BIOS calls can be run on some + platforms. Currently disk-related calls are quite likely to blow up. +- x86 filling in other kernel parameters. +- Merge reboot via kexec functionality into /sbin/reboot +- Improve the documentation +- Add support for loading a boot sector +- Autobuilding of initramfs +### diff --git a/config/config.guess b/config/config.guess new file mode 100755 index 0000000..c626f7a --- /dev/null +++ b/config/config.guess @@ -0,0 +1,1688 @@ +#! /bin/sh +# Attempt to guess a canonical system name. +# Copyright 1992-2020 Free Software Foundation, Inc. + +timestamp='2020-09-19' + +# This file 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 3 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, see <https://www.gnu.org/licenses/>. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that +# program. This Exception is an additional permission under section 7 +# of the GNU General Public License, version 3 ("GPLv3"). +# +# Originally written by Per Bothner; maintained since 2000 by Ben Elliston. +# +# You can get the latest version of this script from: +# https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess +# +# Please send patches to <config-patches@gnu.org>. + + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] + +Output the configuration name of the system \`$me' is run on. + +Options: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to <config-patches@gnu.org>." + +version="\ +GNU config.guess ($timestamp) + +Originally written by Per Bothner. +Copyright 1992-2020 Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit ;; + --version | -v ) + echo "$version" ; exit ;; + --help | --h* | -h ) + echo "$usage"; exit ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" >&2 + exit 1 ;; + * ) + break ;; + esac +done + +if test $# != 0; then + echo "$me: too many arguments$help" >&2 + exit 1 +fi + +# CC_FOR_BUILD -- compiler used by this script. Note that the use of a +# compiler to aid in system detection is discouraged as it requires +# temporary files to be created and, as you can see below, it is a +# headache to deal with in a portable fashion. + +# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still +# use `HOST_CC' if defined, but it is deprecated. + +# Portable tmp directory creation inspired by the Autoconf team. + +tmp= +# shellcheck disable=SC2172 +trap 'test -z "$tmp" || rm -fr "$tmp"' 0 1 2 13 15 + +set_cc_for_build() { + # prevent multiple calls if $tmp is already set + test "$tmp" && return 0 + : "${TMPDIR=/tmp}" + # shellcheck disable=SC2039 + { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || + { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir "$tmp" 2>/dev/null) ; } || + { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir "$tmp" 2>/dev/null) && echo "Warning: creating insecure temp directory" >&2 ; } || + { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } + dummy=$tmp/dummy + case ${CC_FOR_BUILD-},${HOST_CC-},${CC-} in + ,,) echo "int x;" > "$dummy.c" + for driver in cc gcc c89 c99 ; do + if ($driver -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then + CC_FOR_BUILD="$driver" + break + fi + done + if test x"$CC_FOR_BUILD" = x ; then + CC_FOR_BUILD=no_compiler_found + fi + ;; + ,,*) CC_FOR_BUILD=$CC ;; + ,*,*) CC_FOR_BUILD=$HOST_CC ;; + esac +} + +# This is needed to find uname on a Pyramid OSx when run in the BSD universe. +# (ghazi@noc.rutgers.edu 1994-08-24) +if test -f /.attbin/uname ; then + PATH=$PATH:/.attbin ; export PATH +fi + +UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown +UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown +UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown +UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown + +case "$UNAME_SYSTEM" in +Linux|GNU|GNU/*) + # If the system lacks a compiler, then just pick glibc. + # We could probably try harder. + LIBC=gnu + + set_cc_for_build + cat <<-EOF > "$dummy.c" + #include <features.h> + #if defined(__UCLIBC__) + LIBC=uclibc + #elif defined(__dietlibc__) + LIBC=dietlibc + #else + #include <stdarg.h> + #ifdef __DEFINED_va_list + LIBC=musl + #else + LIBC=gnu + #endif + #endif + EOF + eval "`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'`" + ;; +esac + +# Note: order is significant - the case branches are not exclusive. + +case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in + *:NetBSD:*:*) + # NetBSD (nbsd) targets should (where applicable) match one or + # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, + # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently + # switched to ELF, *-*-netbsd* would select the old + # object file format. This provides both forward + # compatibility and a consistent mechanism for selecting the + # object file format. + # + # Note: NetBSD doesn't particularly care about the vendor + # portion of the name. We always set it to "unknown". + sysctl="sysctl -n hw.machine_arch" + UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \ + "/sbin/$sysctl" 2>/dev/null || \ + "/usr/sbin/$sysctl" 2>/dev/null || \ + echo unknown)` + case "$UNAME_MACHINE_ARCH" in + armeb) machine=armeb-unknown ;; + arm*) machine=arm-unknown ;; + sh3el) machine=shl-unknown ;; + sh3eb) machine=sh-unknown ;; + sh5el) machine=sh5le-unknown ;; + earmv*) + arch=`echo "$UNAME_MACHINE_ARCH" | sed -e 's,^e\(armv[0-9]\).*$,\1,'` + endian=`echo "$UNAME_MACHINE_ARCH" | sed -ne 's,^.*\(eb\)$,\1,p'` + machine="${arch}${endian}"-unknown + ;; + *) machine="$UNAME_MACHINE_ARCH"-unknown ;; + esac + # The Operating System including object format, if it has switched + # to ELF recently (or will in the future) and ABI. + case "$UNAME_MACHINE_ARCH" in + earm*) + os=netbsdelf + ;; + arm*|i386|m68k|ns32k|sh3*|sparc|vax) + set_cc_for_build + if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ELF__ + then + # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). + # Return netbsd for either. FIX? + os=netbsd + else + os=netbsdelf + fi + ;; + *) + os=netbsd + ;; + esac + # Determine ABI tags. + case "$UNAME_MACHINE_ARCH" in + earm*) + expr='s/^earmv[0-9]/-eabi/;s/eb$//' + abi=`echo "$UNAME_MACHINE_ARCH" | sed -e "$expr"` + ;; + esac + # The OS release + # Debian GNU/NetBSD machines have a different userland, and + # thus, need a distinct triplet. However, they do not need + # kernel version information, so it can be replaced with a + # suitable tag, in the style of linux-gnu. + case "$UNAME_VERSION" in + Debian*) + release='-gnu' + ;; + *) + release=`echo "$UNAME_RELEASE" | sed -e 's/[-_].*//' | cut -d. -f1,2` + ;; + esac + # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: + # contains redundant information, the shorter form: + # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. + echo "$machine-${os}${release}${abi-}" + exit ;; + *:Bitrig:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` + echo "$UNAME_MACHINE_ARCH"-unknown-bitrig"$UNAME_RELEASE" + exit ;; + *:OpenBSD:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` + echo "$UNAME_MACHINE_ARCH"-unknown-openbsd"$UNAME_RELEASE" + exit ;; + *:LibertyBSD:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'` + echo "$UNAME_MACHINE_ARCH"-unknown-libertybsd"$UNAME_RELEASE" + exit ;; + *:MidnightBSD:*:*) + echo "$UNAME_MACHINE"-unknown-midnightbsd"$UNAME_RELEASE" + exit ;; + *:ekkoBSD:*:*) + echo "$UNAME_MACHINE"-unknown-ekkobsd"$UNAME_RELEASE" + exit ;; + *:SolidBSD:*:*) + echo "$UNAME_MACHINE"-unknown-solidbsd"$UNAME_RELEASE" + exit ;; + *:OS108:*:*) + echo "$UNAME_MACHINE"-unknown-os108_"$UNAME_RELEASE" + exit ;; + macppc:MirBSD:*:*) + echo powerpc-unknown-mirbsd"$UNAME_RELEASE" + exit ;; + *:MirBSD:*:*) + echo "$UNAME_MACHINE"-unknown-mirbsd"$UNAME_RELEASE" + exit ;; + *:Sortix:*:*) + echo "$UNAME_MACHINE"-unknown-sortix + exit ;; + *:Twizzler:*:*) + echo "$UNAME_MACHINE"-unknown-twizzler + exit ;; + *:Redox:*:*) + echo "$UNAME_MACHINE"-unknown-redox + exit ;; + mips:OSF1:*.*) + echo mips-dec-osf1 + exit ;; + alpha:OSF1:*:*) + case $UNAME_RELEASE in + *4.0) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` + ;; + *5.*) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` + ;; + esac + # According to Compaq, /usr/sbin/psrinfo has been available on + # OSF/1 and Tru64 systems produced since 1995. I hope that + # covers most systems running today. This code pipes the CPU + # types through head -n 1, so we only detect the type of CPU 0. + ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` + case "$ALPHA_CPU_TYPE" in + "EV4 (21064)") + UNAME_MACHINE=alpha ;; + "EV4.5 (21064)") + UNAME_MACHINE=alpha ;; + "LCA4 (21066/21068)") + UNAME_MACHINE=alpha ;; + "EV5 (21164)") + UNAME_MACHINE=alphaev5 ;; + "EV5.6 (21164A)") + UNAME_MACHINE=alphaev56 ;; + "EV5.6 (21164PC)") + UNAME_MACHINE=alphapca56 ;; + "EV5.7 (21164PC)") + UNAME_MACHINE=alphapca57 ;; + "EV6 (21264)") + UNAME_MACHINE=alphaev6 ;; + "EV6.7 (21264A)") + UNAME_MACHINE=alphaev67 ;; + "EV6.8CB (21264C)") + UNAME_MACHINE=alphaev68 ;; + "EV6.8AL (21264B)") + UNAME_MACHINE=alphaev68 ;; + "EV6.8CX (21264D)") + UNAME_MACHINE=alphaev68 ;; + "EV6.9A (21264/EV69A)") + UNAME_MACHINE=alphaev69 ;; + "EV7 (21364)") + UNAME_MACHINE=alphaev7 ;; + "EV7.9 (21364A)") + UNAME_MACHINE=alphaev79 ;; + esac + # A Pn.n version is a patched version. + # A Vn.n version is a released version. + # A Tn.n version is a released field test version. + # A Xn.n version is an unreleased experimental baselevel. + # 1.2 uses "1.2" for uname -r. + echo "$UNAME_MACHINE"-dec-osf"`echo "$UNAME_RELEASE" | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`" + # Reset EXIT trap before exiting to avoid spurious non-zero exit code. + exitcode=$? + trap '' 0 + exit $exitcode ;; + Amiga*:UNIX_System_V:4.0:*) + echo m68k-unknown-sysv4 + exit ;; + *:[Aa]miga[Oo][Ss]:*:*) + echo "$UNAME_MACHINE"-unknown-amigaos + exit ;; + *:[Mm]orph[Oo][Ss]:*:*) + echo "$UNAME_MACHINE"-unknown-morphos + exit ;; + *:OS/390:*:*) + echo i370-ibm-openedition + exit ;; + *:z/VM:*:*) + echo s390-ibm-zvmoe + exit ;; + *:OS400:*:*) + echo powerpc-ibm-os400 + exit ;; + arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) + echo arm-acorn-riscix"$UNAME_RELEASE" + exit ;; + arm*:riscos:*:*|arm*:RISCOS:*:*) + echo arm-unknown-riscos + exit ;; + SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) + echo hppa1.1-hitachi-hiuxmpp + exit ;; + Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) + # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. + if test "`(/bin/universe) 2>/dev/null`" = att ; then + echo pyramid-pyramid-sysv3 + else + echo pyramid-pyramid-bsd + fi + exit ;; + NILE*:*:*:dcosx) + echo pyramid-pyramid-svr4 + exit ;; + DRS?6000:unix:4.0:6*) + echo sparc-icl-nx6 + exit ;; + DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) + case `/usr/bin/uname -p` in + sparc) echo sparc-icl-nx7; exit ;; + esac ;; + s390x:SunOS:*:*) + echo "$UNAME_MACHINE"-ibm-solaris2"`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`" + exit ;; + sun4H:SunOS:5.*:*) + echo sparc-hal-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" + exit ;; + sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) + echo sparc-sun-solaris2"`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`" + exit ;; + i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) + echo i386-pc-auroraux"$UNAME_RELEASE" + exit ;; + i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) + set_cc_for_build + SUN_ARCH=i386 + # If there is a compiler, see if it is configured for 64-bit objects. + # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. + # This test works for both compilers. + if test "$CC_FOR_BUILD" != no_compiler_found; then + if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + SUN_ARCH=x86_64 + fi + fi + echo "$SUN_ARCH"-pc-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" + exit ;; + sun4*:SunOS:6*:*) + # According to config.sub, this is the proper way to canonicalize + # SunOS6. Hard to guess exactly what SunOS6 will be like, but + # it's likely to be more like Solaris than SunOS4. + echo sparc-sun-solaris3"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" + exit ;; + sun4*:SunOS:*:*) + case "`/usr/bin/arch -k`" in + Series*|S4*) + UNAME_RELEASE=`uname -v` + ;; + esac + # Japanese Language versions have a version number like `4.1.3-JL'. + echo sparc-sun-sunos"`echo "$UNAME_RELEASE"|sed -e 's/-/_/'`" + exit ;; + sun3*:SunOS:*:*) + echo m68k-sun-sunos"$UNAME_RELEASE" + exit ;; + sun*:*:4.2BSD:*) + UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` + test "x$UNAME_RELEASE" = x && UNAME_RELEASE=3 + case "`/bin/arch`" in + sun3) + echo m68k-sun-sunos"$UNAME_RELEASE" + ;; + sun4) + echo sparc-sun-sunos"$UNAME_RELEASE" + ;; + esac + exit ;; + aushp:SunOS:*:*) + echo sparc-auspex-sunos"$UNAME_RELEASE" + exit ;; + # The situation for MiNT is a little confusing. The machine name + # can be virtually everything (everything which is not + # "atarist" or "atariste" at least should have a processor + # > m68000). The system name ranges from "MiNT" over "FreeMiNT" + # to the lowercase version "mint" (or "freemint"). Finally + # the system name "TOS" denotes a system which is actually not + # MiNT. But MiNT is downward compatible to TOS, so this should + # be no problem. + atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint"$UNAME_RELEASE" + exit ;; + atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint"$UNAME_RELEASE" + exit ;; + *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) + echo m68k-atari-mint"$UNAME_RELEASE" + exit ;; + milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) + echo m68k-milan-mint"$UNAME_RELEASE" + exit ;; + hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) + echo m68k-hades-mint"$UNAME_RELEASE" + exit ;; + *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) + echo m68k-unknown-mint"$UNAME_RELEASE" + exit ;; + m68k:machten:*:*) + echo m68k-apple-machten"$UNAME_RELEASE" + exit ;; + powerpc:machten:*:*) + echo powerpc-apple-machten"$UNAME_RELEASE" + exit ;; + RISC*:Mach:*:*) + echo mips-dec-mach_bsd4.3 + exit ;; + RISC*:ULTRIX:*:*) + echo mips-dec-ultrix"$UNAME_RELEASE" + exit ;; + VAX*:ULTRIX*:*:*) + echo vax-dec-ultrix"$UNAME_RELEASE" + exit ;; + 2020:CLIX:*:* | 2430:CLIX:*:*) + echo clipper-intergraph-clix"$UNAME_RELEASE" + exit ;; + mips:*:*:UMIPS | mips:*:*:RISCos) + set_cc_for_build + sed 's/^ //' << EOF > "$dummy.c" +#ifdef __cplusplus +#include <stdio.h> /* for printf() prototype */ + int main (int argc, char *argv[]) { +#else + int main (argc, argv) int argc; char *argv[]; { +#endif + #if defined (host_mips) && defined (MIPSEB) + #if defined (SYSTYPE_SYSV) + printf ("mips-mips-riscos%ssysv\\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_SVR4) + printf ("mips-mips-riscos%ssvr4\\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) + printf ("mips-mips-riscos%sbsd\\n", argv[1]); exit (0); + #endif + #endif + exit (-1); + } +EOF + $CC_FOR_BUILD -o "$dummy" "$dummy.c" && + dummyarg=`echo "$UNAME_RELEASE" | sed -n 's/\([0-9]*\).*/\1/p'` && + SYSTEM_NAME=`"$dummy" "$dummyarg"` && + { echo "$SYSTEM_NAME"; exit; } + echo mips-mips-riscos"$UNAME_RELEASE" + exit ;; + Motorola:PowerMAX_OS:*:*) + echo powerpc-motorola-powermax + exit ;; + Motorola:*:4.3:PL8-*) + echo powerpc-harris-powermax + exit ;; + Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) + echo powerpc-harris-powermax + exit ;; + Night_Hawk:Power_UNIX:*:*) + echo powerpc-harris-powerunix + exit ;; + m88k:CX/UX:7*:*) + echo m88k-harris-cxux7 + exit ;; + m88k:*:4*:R4*) + echo m88k-motorola-sysv4 + exit ;; + m88k:*:3*:R3*) + echo m88k-motorola-sysv3 + exit ;; + AViiON:dgux:*:*) + # DG/UX returns AViiON for all architectures + UNAME_PROCESSOR=`/usr/bin/uname -p` + if test "$UNAME_PROCESSOR" = mc88100 || test "$UNAME_PROCESSOR" = mc88110 + then + if test "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx || \ + test "$TARGET_BINARY_INTERFACE"x = x + then + echo m88k-dg-dgux"$UNAME_RELEASE" + else + echo m88k-dg-dguxbcs"$UNAME_RELEASE" + fi + else + echo i586-dg-dgux"$UNAME_RELEASE" + fi + exit ;; + M88*:DolphinOS:*:*) # DolphinOS (SVR3) + echo m88k-dolphin-sysv3 + exit ;; + M88*:*:R3*:*) + # Delta 88k system running SVR3 + echo m88k-motorola-sysv3 + exit ;; + XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) + echo m88k-tektronix-sysv3 + exit ;; + Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) + echo m68k-tektronix-bsd + exit ;; + *:IRIX*:*:*) + echo mips-sgi-irix"`echo "$UNAME_RELEASE"|sed -e 's/-/_/g'`" + exit ;; + ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. + echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id + exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' + i*86:AIX:*:*) + echo i386-ibm-aix + exit ;; + ia64:AIX:*:*) + if test -x /usr/bin/oslevel ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV="$UNAME_VERSION.$UNAME_RELEASE" + fi + echo "$UNAME_MACHINE"-ibm-aix"$IBM_REV" + exit ;; + *:AIX:2:3) + if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then + set_cc_for_build + sed 's/^ //' << EOF > "$dummy.c" + #include <sys/systemcfg.h> + + main() + { + if (!__power_pc()) + exit(1); + puts("powerpc-ibm-aix3.2.5"); + exit(0); + } +EOF + if $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` + then + echo "$SYSTEM_NAME" + else + echo rs6000-ibm-aix3.2.5 + fi + elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then + echo rs6000-ibm-aix3.2.4 + else + echo rs6000-ibm-aix3.2 + fi + exit ;; + *:AIX:*:[4567]) + IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` + if /usr/sbin/lsattr -El "$IBM_CPU_ID" | grep ' POWER' >/dev/null 2>&1; then + IBM_ARCH=rs6000 + else + IBM_ARCH=powerpc + fi + if test -x /usr/bin/lslpp ; then + IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc | + awk -F: '{ print $3 }' | sed s/[0-9]*$/0/` + else + IBM_REV="$UNAME_VERSION.$UNAME_RELEASE" + fi + echo "$IBM_ARCH"-ibm-aix"$IBM_REV" + exit ;; + *:AIX:*:*) + echo rs6000-ibm-aix + exit ;; + ibmrt:4.4BSD:*|romp-ibm:4.4BSD:*) + echo romp-ibm-bsd4.4 + exit ;; + ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and + echo romp-ibm-bsd"$UNAME_RELEASE" # 4.3 with uname added to + exit ;; # report: romp-ibm BSD 4.3 + *:BOSX:*:*) + echo rs6000-bull-bosx + exit ;; + DPX/2?00:B.O.S.:*:*) + echo m68k-bull-sysv3 + exit ;; + 9000/[34]??:4.3bsd:1.*:*) + echo m68k-hp-bsd + exit ;; + hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) + echo m68k-hp-bsd4.4 + exit ;; + 9000/[34678]??:HP-UX:*:*) + HPUX_REV=`echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//'` + case "$UNAME_MACHINE" in + 9000/31?) HP_ARCH=m68000 ;; + 9000/[34]??) HP_ARCH=m68k ;; + 9000/[678][0-9][0-9]) + if test -x /usr/bin/getconf; then + sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` + sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` + case "$sc_cpu_version" in + 523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0 + 528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1 + 532) # CPU_PA_RISC2_0 + case "$sc_kernel_bits" in + 32) HP_ARCH=hppa2.0n ;; + 64) HP_ARCH=hppa2.0w ;; + '') HP_ARCH=hppa2.0 ;; # HP-UX 10.20 + esac ;; + esac + fi + if test "$HP_ARCH" = ""; then + set_cc_for_build + sed 's/^ //' << EOF > "$dummy.c" + + #define _HPUX_SOURCE + #include <stdlib.h> + #include <unistd.h> + + int main () + { + #if defined(_SC_KERNEL_BITS) + long bits = sysconf(_SC_KERNEL_BITS); + #endif + long cpu = sysconf (_SC_CPU_VERSION); + + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1"); break; + case CPU_PA_RISC2_0: + #if defined(_SC_KERNEL_BITS) + switch (bits) + { + case 64: puts ("hppa2.0w"); break; + case 32: puts ("hppa2.0n"); break; + default: puts ("hppa2.0"); break; + } break; + #else /* !defined(_SC_KERNEL_BITS) */ + puts ("hppa2.0"); break; + #endif + default: puts ("hppa1.0"); break; + } + exit (0); + } +EOF + (CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null) && HP_ARCH=`"$dummy"` + test -z "$HP_ARCH" && HP_ARCH=hppa + fi ;; + esac + if test "$HP_ARCH" = hppa2.0w + then + set_cc_for_build + + # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating + # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler + # generating 64-bit code. GNU and HP use different nomenclature: + # + # $ CC_FOR_BUILD=cc ./config.guess + # => hppa2.0w-hp-hpux11.23 + # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess + # => hppa64-hp-hpux11.23 + + if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | + grep -q __LP64__ + then + HP_ARCH=hppa2.0w + else + HP_ARCH=hppa64 + fi + fi + echo "$HP_ARCH"-hp-hpux"$HPUX_REV" + exit ;; + ia64:HP-UX:*:*) + HPUX_REV=`echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//'` + echo ia64-hp-hpux"$HPUX_REV" + exit ;; + 3050*:HI-UX:*:*) + set_cc_for_build + sed 's/^ //' << EOF > "$dummy.c" + #include <unistd.h> + int + main () + { + long cpu = sysconf (_SC_CPU_VERSION); + /* The order matters, because CPU_IS_HP_MC68K erroneously returns + true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct + results, however. */ + if (CPU_IS_PA_RISC (cpu)) + { + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; + case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; + default: puts ("hppa-hitachi-hiuxwe2"); break; + } + } + else if (CPU_IS_HP_MC68K (cpu)) + puts ("m68k-hitachi-hiuxwe2"); + else puts ("unknown-hitachi-hiuxwe2"); + exit (0); + } +EOF + $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` && + { echo "$SYSTEM_NAME"; exit; } + echo unknown-hitachi-hiuxwe2 + exit ;; + 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:*) + echo hppa1.1-hp-bsd + exit ;; + 9000/8??:4.3bsd:*:*) + echo hppa1.0-hp-bsd + exit ;; + *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) + echo hppa1.0-hp-mpeix + exit ;; + hp7??:OSF1:*:* | hp8?[79]:OSF1:*:*) + echo hppa1.1-hp-osf + exit ;; + hp8??:OSF1:*:*) + echo hppa1.0-hp-osf + exit ;; + i*86:OSF1:*:*) + if test -x /usr/sbin/sysversion ; then + echo "$UNAME_MACHINE"-unknown-osf1mk + else + echo "$UNAME_MACHINE"-unknown-osf1 + fi + exit ;; + parisc*:Lites*:*:*) + echo hppa1.1-hp-lites + exit ;; + C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) + echo c1-convex-bsd + exit ;; + C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit ;; + C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) + echo c34-convex-bsd + exit ;; + C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) + echo c38-convex-bsd + exit ;; + C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) + echo c4-convex-bsd + exit ;; + CRAY*Y-MP:*:*:*) + echo ymp-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*[A-Z]90:*:*:*) + echo "$UNAME_MACHINE"-cray-unicos"$UNAME_RELEASE" \ + | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ + -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ + -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*TS:*:*:*) + echo t90-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*T3E:*:*:*) + echo alphaev5-cray-unicosmk"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*SV1:*:*:*) + echo sv1-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' + exit ;; + *:UNICOS/mp:*:*) + echo craynv-cray-unicosmp"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' + exit ;; + F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) + FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` + FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` + FUJITSU_REL=`echo "$UNAME_RELEASE" | sed -e 's/ /_/'` + echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit ;; + 5000:UNIX_System_V:4.*:*) + FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` + FUJITSU_REL=`echo "$UNAME_RELEASE" | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'` + echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit ;; + i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) + echo "$UNAME_MACHINE"-pc-bsdi"$UNAME_RELEASE" + exit ;; + sparc*:BSD/OS:*:*) + echo sparc-unknown-bsdi"$UNAME_RELEASE" + exit ;; + *:BSD/OS:*:*) + echo "$UNAME_MACHINE"-unknown-bsdi"$UNAME_RELEASE" + exit ;; + arm:FreeBSD:*:*) + UNAME_PROCESSOR=`uname -p` + set_cc_for_build + if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_PCS_VFP + then + echo "${UNAME_PROCESSOR}"-unknown-freebsd"`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`"-gnueabi + else + echo "${UNAME_PROCESSOR}"-unknown-freebsd"`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`"-gnueabihf + fi + exit ;; + *:FreeBSD:*:*) + UNAME_PROCESSOR=`/usr/bin/uname -p` + case "$UNAME_PROCESSOR" in + amd64) + UNAME_PROCESSOR=x86_64 ;; + i386) + UNAME_PROCESSOR=i586 ;; + esac + echo "$UNAME_PROCESSOR"-unknown-freebsd"`echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`" + exit ;; + i*:CYGWIN*:*) + echo "$UNAME_MACHINE"-pc-cygwin + exit ;; + *:MINGW64*:*) + echo "$UNAME_MACHINE"-pc-mingw64 + exit ;; + *:MINGW*:*) + echo "$UNAME_MACHINE"-pc-mingw32 + exit ;; + *:MSYS*:*) + echo "$UNAME_MACHINE"-pc-msys + exit ;; + i*:PW*:*) + echo "$UNAME_MACHINE"-pc-pw32 + exit ;; + *:Interix*:*) + case "$UNAME_MACHINE" in + x86) + echo i586-pc-interix"$UNAME_RELEASE" + exit ;; + authenticamd | genuineintel | EM64T) + echo x86_64-unknown-interix"$UNAME_RELEASE" + exit ;; + IA64) + echo ia64-unknown-interix"$UNAME_RELEASE" + exit ;; + esac ;; + i*:UWIN*:*) + echo "$UNAME_MACHINE"-pc-uwin + exit ;; + amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) + echo x86_64-pc-cygwin + exit ;; + prep*:SunOS:5.*:*) + echo powerpcle-unknown-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" + exit ;; + *:GNU:*:*) + # the GNU system + echo "`echo "$UNAME_MACHINE"|sed -e 's,[-/].*$,,'`-unknown-$LIBC`echo "$UNAME_RELEASE"|sed -e 's,/.*$,,'`" + exit ;; + *:GNU/*:*:*) + # other systems with GNU libc and userland + echo "$UNAME_MACHINE-unknown-`echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"``echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`-$LIBC" + exit ;; + *:Minix:*:*) + echo "$UNAME_MACHINE"-unknown-minix + exit ;; + aarch64:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + aarch64_be:Linux:*:*) + UNAME_MACHINE=aarch64_be + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + alpha:Linux:*:*) + case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' /proc/cpuinfo 2>/dev/null` in + EV5) UNAME_MACHINE=alphaev5 ;; + EV56) UNAME_MACHINE=alphaev56 ;; + PCA56) UNAME_MACHINE=alphapca56 ;; + PCA57) UNAME_MACHINE=alphapca56 ;; + EV6) UNAME_MACHINE=alphaev6 ;; + EV67) UNAME_MACHINE=alphaev67 ;; + EV68*) UNAME_MACHINE=alphaev68 ;; + esac + objdump --private-headers /bin/sh | grep -q ld.so.1 + if test "$?" = 0 ; then LIBC=gnulibc1 ; fi + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + arc:Linux:*:* | arceb:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + arm*:Linux:*:*) + set_cc_for_build + if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_EABI__ + then + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + else + if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_PCS_VFP + then + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"eabi + else + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"eabihf + fi + fi + exit ;; + avr32*:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + cris:Linux:*:*) + echo "$UNAME_MACHINE"-axis-linux-"$LIBC" + exit ;; + crisv32:Linux:*:*) + echo "$UNAME_MACHINE"-axis-linux-"$LIBC" + exit ;; + e2k:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + frv:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + hexagon:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + i*86:Linux:*:*) + echo "$UNAME_MACHINE"-pc-linux-"$LIBC" + exit ;; + ia64:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + k1om:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + loongarch32:Linux:*:* | loongarch64:Linux:*:* | loongarchx32:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + m32r*:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + m68*:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + mips:Linux:*:* | mips64:Linux:*:*) + set_cc_for_build + IS_GLIBC=0 + test x"${LIBC}" = xgnu && IS_GLIBC=1 + sed 's/^ //' << EOF > "$dummy.c" + #undef CPU + #undef mips + #undef mipsel + #undef mips64 + #undef mips64el + #if ${IS_GLIBC} && defined(_ABI64) + LIBCABI=gnuabi64 + #else + #if ${IS_GLIBC} && defined(_ABIN32) + LIBCABI=gnuabin32 + #else + LIBCABI=${LIBC} + #endif + #endif + + #if ${IS_GLIBC} && defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6 + CPU=mipsisa64r6 + #else + #if ${IS_GLIBC} && !defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6 + CPU=mipsisa32r6 + #else + #if defined(__mips64) + CPU=mips64 + #else + CPU=mips + #endif + #endif + #endif + + #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) + MIPS_ENDIAN=el + #else + #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) + MIPS_ENDIAN= + #else + MIPS_ENDIAN= + #endif + #endif +EOF + eval "`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^CPU\|^MIPS_ENDIAN\|^LIBCABI'`" + test "x$CPU" != x && { echo "$CPU${MIPS_ENDIAN}-unknown-linux-$LIBCABI"; exit; } + ;; + mips64el:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + openrisc*:Linux:*:*) + echo or1k-unknown-linux-"$LIBC" + exit ;; + or32:Linux:*:* | or1k*:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + padre:Linux:*:*) + echo sparc-unknown-linux-"$LIBC" + exit ;; + parisc64:Linux:*:* | hppa64:Linux:*:*) + echo hppa64-unknown-linux-"$LIBC" + exit ;; + parisc:Linux:*:* | hppa:Linux:*:*) + # Look for CPU level + case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in + PA7*) echo hppa1.1-unknown-linux-"$LIBC" ;; + PA8*) echo hppa2.0-unknown-linux-"$LIBC" ;; + *) echo hppa-unknown-linux-"$LIBC" ;; + esac + exit ;; + ppc64:Linux:*:*) + echo powerpc64-unknown-linux-"$LIBC" + exit ;; + ppc:Linux:*:*) + echo powerpc-unknown-linux-"$LIBC" + exit ;; + ppc64le:Linux:*:*) + echo powerpc64le-unknown-linux-"$LIBC" + exit ;; + ppcle:Linux:*:*) + echo powerpcle-unknown-linux-"$LIBC" + exit ;; + riscv32:Linux:*:* | riscv64:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + s390:Linux:*:* | s390x:Linux:*:*) + echo "$UNAME_MACHINE"-ibm-linux-"$LIBC" + exit ;; + sh64*:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + sh*:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + sparc:Linux:*:* | sparc64:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + tile*:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + vax:Linux:*:*) + echo "$UNAME_MACHINE"-dec-linux-"$LIBC" + exit ;; + x86_64:Linux:*:*) + set_cc_for_build + LIBCABI=$LIBC + if test "$CC_FOR_BUILD" != no_compiler_found; then + if (echo '#ifdef __ILP32__'; echo IS_X32; echo '#endif') | \ + (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_X32 >/dev/null + then + LIBCABI="$LIBC"x32 + fi + fi + echo "$UNAME_MACHINE"-pc-linux-"$LIBCABI" + exit ;; + xtensa*:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + i*86:DYNIX/ptx:4*:*) + # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. + # earlier versions are messed up and put the nodename in both + # sysname and nodename. + echo i386-sequent-sysv4 + exit ;; + i*86:UNIX_SV:4.2MP:2.*) + # Unixware is an offshoot of SVR4, but it has its own version + # number series starting with 2... + # I am not positive that other SVR4 systems won't match this, + # I just have to hope. -- rms. + # Use sysv4.2uw... so that sysv4* matches it. + echo "$UNAME_MACHINE"-pc-sysv4.2uw"$UNAME_VERSION" + exit ;; + i*86:OS/2:*:*) + # If we were able to find `uname', then EMX Unix compatibility + # is probably installed. + echo "$UNAME_MACHINE"-pc-os2-emx + exit ;; + i*86:XTS-300:*:STOP) + echo "$UNAME_MACHINE"-unknown-stop + exit ;; + i*86:atheos:*:*) + echo "$UNAME_MACHINE"-unknown-atheos + exit ;; + i*86:syllable:*:*) + echo "$UNAME_MACHINE"-pc-syllable + exit ;; + i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) + echo i386-unknown-lynxos"$UNAME_RELEASE" + exit ;; + i*86:*DOS:*:*) + echo "$UNAME_MACHINE"-pc-msdosdjgpp + exit ;; + i*86:*:4.*:*) + UNAME_REL=`echo "$UNAME_RELEASE" | sed 's/\/MP$//'` + if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then + echo "$UNAME_MACHINE"-univel-sysv"$UNAME_REL" + else + echo "$UNAME_MACHINE"-pc-sysv"$UNAME_REL" + fi + exit ;; + i*86:*:5:[678]*) + # UnixWare 7.x, OpenUNIX and OpenServer 6. + case `/bin/uname -X | grep "^Machine"` in + *486*) UNAME_MACHINE=i486 ;; + *Pentium) UNAME_MACHINE=i586 ;; + *Pent*|*Celeron) UNAME_MACHINE=i686 ;; + esac + echo "$UNAME_MACHINE-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}" + exit ;; + i*86:*:3.2:*) + if test -f /usr/options/cb.name; then + UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name` + echo "$UNAME_MACHINE"-pc-isc"$UNAME_REL" + elif /bin/uname -X 2>/dev/null >/dev/null ; then + UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` + (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 + (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ + && UNAME_MACHINE=i586 + (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ + && UNAME_MACHINE=i686 + (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ + && UNAME_MACHINE=i686 + echo "$UNAME_MACHINE"-pc-sco"$UNAME_REL" + else + echo "$UNAME_MACHINE"-pc-sysv32 + fi + exit ;; + pc:*:*:*) + # Left here for compatibility: + # uname -m prints for DJGPP always 'pc', but it prints nothing about + # the processor, so we play safe by assuming i586. + # Note: whatever this is, it MUST be the same as what config.sub + # prints for the "djgpp" host, or else GDB configure will decide that + # this is a cross-build. + echo i586-pc-msdosdjgpp + exit ;; + Intel:Mach:3*:*) + echo i386-pc-mach3 + exit ;; + paragon:*:*:*) + echo i860-intel-osf1 + exit ;; + i860:*:4.*:*) # i860-SVR4 + if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then + echo i860-stardent-sysv"$UNAME_RELEASE" # Stardent Vistra i860-SVR4 + else # Add other i860-SVR4 vendors below as they are discovered. + echo i860-unknown-sysv"$UNAME_RELEASE" # Unknown i860-SVR4 + fi + exit ;; + mini*:CTIX:SYS*5:*) + # "miniframe" + echo m68010-convergent-sysv + exit ;; + mc68k:UNIX:SYSTEM5:3.51m) + echo m68k-convergent-sysv + exit ;; + M680?0:D-NIX:5.3:*) + echo m68k-diab-dnix + exit ;; + M68*:*:R3V[5678]*:*) + test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; + 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) + OS_REL='' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; + 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4; exit; } ;; + NCR*:*:4.2:* | MPRAS*:*:4.2:*) + OS_REL='.3' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } + /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ + && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; + m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) + echo m68k-unknown-lynxos"$UNAME_RELEASE" + exit ;; + mc68030:UNIX_System_V:4.*:*) + echo m68k-atari-sysv4 + exit ;; + TSUNAMI:LynxOS:2.*:*) + echo sparc-unknown-lynxos"$UNAME_RELEASE" + exit ;; + rs6000:LynxOS:2.*:*) + echo rs6000-unknown-lynxos"$UNAME_RELEASE" + exit ;; + PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) + echo powerpc-unknown-lynxos"$UNAME_RELEASE" + exit ;; + SM[BE]S:UNIX_SV:*:*) + echo mips-dde-sysv"$UNAME_RELEASE" + exit ;; + RM*:ReliantUNIX-*:*:*) + echo mips-sni-sysv4 + exit ;; + RM*:SINIX-*:*:*) + echo mips-sni-sysv4 + exit ;; + *:SINIX-*:*:*) + if uname -p 2>/dev/null >/dev/null ; then + UNAME_MACHINE=`(uname -p) 2>/dev/null` + echo "$UNAME_MACHINE"-sni-sysv4 + else + echo ns32k-sni-sysv + fi + exit ;; + PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort + # says <Richard.M.Bartel@ccMail.Census.GOV> + echo i586-unisys-sysv4 + exit ;; + *:UNIX_System_V:4*:FTX*) + # From Gerald Hewes <hewes@openmarket.com>. + # How about differentiating between stratus architectures? -djm + echo hppa1.1-stratus-sysv4 + exit ;; + *:*:*:FTX*) + # From seanf@swdc.stratus.com. + echo i860-stratus-sysv4 + exit ;; + i*86:VOS:*:*) + # From Paul.Green@stratus.com. + echo "$UNAME_MACHINE"-stratus-vos + exit ;; + *:VOS:*:*) + # From Paul.Green@stratus.com. + echo hppa1.1-stratus-vos + exit ;; + mc68*:A/UX:*:*) + echo m68k-apple-aux"$UNAME_RELEASE" + exit ;; + news*:NEWS-OS:6*:*) + echo mips-sony-newsos6 + exit ;; + R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) + if test -d /usr/nec; then + echo mips-nec-sysv"$UNAME_RELEASE" + else + echo mips-unknown-sysv"$UNAME_RELEASE" + fi + exit ;; + BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. + echo powerpc-be-beos + exit ;; + BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. + echo powerpc-apple-beos + exit ;; + BePC:BeOS:*:*) # BeOS running on Intel PC compatible. + echo i586-pc-beos + exit ;; + BePC:Haiku:*:*) # Haiku running on Intel PC compatible. + echo i586-pc-haiku + exit ;; + x86_64:Haiku:*:*) + echo x86_64-unknown-haiku + exit ;; + SX-4:SUPER-UX:*:*) + echo sx4-nec-superux"$UNAME_RELEASE" + exit ;; + SX-5:SUPER-UX:*:*) + echo sx5-nec-superux"$UNAME_RELEASE" + exit ;; + SX-6:SUPER-UX:*:*) + echo sx6-nec-superux"$UNAME_RELEASE" + exit ;; + SX-7:SUPER-UX:*:*) + echo sx7-nec-superux"$UNAME_RELEASE" + exit ;; + SX-8:SUPER-UX:*:*) + echo sx8-nec-superux"$UNAME_RELEASE" + exit ;; + SX-8R:SUPER-UX:*:*) + echo sx8r-nec-superux"$UNAME_RELEASE" + exit ;; + SX-ACE:SUPER-UX:*:*) + echo sxace-nec-superux"$UNAME_RELEASE" + exit ;; + Power*:Rhapsody:*:*) + echo powerpc-apple-rhapsody"$UNAME_RELEASE" + exit ;; + *:Rhapsody:*:*) + echo "$UNAME_MACHINE"-apple-rhapsody"$UNAME_RELEASE" + exit ;; + arm64:Darwin:*:*) + echo aarch64-apple-darwin"$UNAME_RELEASE" + exit ;; + *:Darwin:*:*) + UNAME_PROCESSOR=`uname -p` + case $UNAME_PROCESSOR in + unknown) UNAME_PROCESSOR=powerpc ;; + esac + if command -v xcode-select > /dev/null 2> /dev/null && \ + ! xcode-select --print-path > /dev/null 2> /dev/null ; then + # Avoid executing cc if there is no toolchain installed as + # cc will be a stub that puts up a graphical alert + # prompting the user to install developer tools. + CC_FOR_BUILD=no_compiler_found + else + set_cc_for_build + fi + if test "$CC_FOR_BUILD" != no_compiler_found; then + if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + case $UNAME_PROCESSOR in + i386) UNAME_PROCESSOR=x86_64 ;; + powerpc) UNAME_PROCESSOR=powerpc64 ;; + esac + fi + # On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc + if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \ + (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_PPC >/dev/null + then + UNAME_PROCESSOR=powerpc + fi + elif test "$UNAME_PROCESSOR" = i386 ; then + # uname -m returns i386 or x86_64 + UNAME_PROCESSOR=$UNAME_MACHINE + fi + echo "$UNAME_PROCESSOR"-apple-darwin"$UNAME_RELEASE" + exit ;; + *:procnto*:*:* | *:QNX:[0123456789]*:*) + UNAME_PROCESSOR=`uname -p` + if test "$UNAME_PROCESSOR" = x86; then + UNAME_PROCESSOR=i386 + UNAME_MACHINE=pc + fi + echo "$UNAME_PROCESSOR"-"$UNAME_MACHINE"-nto-qnx"$UNAME_RELEASE" + exit ;; + *:QNX:*:4*) + echo i386-pc-qnx + exit ;; + NEO-*:NONSTOP_KERNEL:*:*) + echo neo-tandem-nsk"$UNAME_RELEASE" + exit ;; + NSE-*:NONSTOP_KERNEL:*:*) + echo nse-tandem-nsk"$UNAME_RELEASE" + exit ;; + NSR-*:NONSTOP_KERNEL:*:*) + echo nsr-tandem-nsk"$UNAME_RELEASE" + exit ;; + NSV-*:NONSTOP_KERNEL:*:*) + echo nsv-tandem-nsk"$UNAME_RELEASE" + exit ;; + NSX-*:NONSTOP_KERNEL:*:*) + echo nsx-tandem-nsk"$UNAME_RELEASE" + exit ;; + *:NonStop-UX:*:*) + echo mips-compaq-nonstopux + exit ;; + BS2000:POSIX*:*:*) + echo bs2000-siemens-sysv + exit ;; + DS/*:UNIX_System_V:*:*) + echo "$UNAME_MACHINE"-"$UNAME_SYSTEM"-"$UNAME_RELEASE" + exit ;; + *:Plan9:*:*) + # "uname -m" is not consistent, so use $cputype instead. 386 + # is converted to i386 for consistency with other x86 + # operating systems. + # shellcheck disable=SC2154 + if test "$cputype" = 386; then + UNAME_MACHINE=i386 + else + UNAME_MACHINE="$cputype" + fi + echo "$UNAME_MACHINE"-unknown-plan9 + exit ;; + *:TOPS-10:*:*) + echo pdp10-unknown-tops10 + exit ;; + *:TENEX:*:*) + echo pdp10-unknown-tenex + exit ;; + KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) + echo pdp10-dec-tops20 + exit ;; + XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) + echo pdp10-xkl-tops20 + exit ;; + *:TOPS-20:*:*) + echo pdp10-unknown-tops20 + exit ;; + *:ITS:*:*) + echo pdp10-unknown-its + exit ;; + SEI:*:*:SEIUX) + echo mips-sei-seiux"$UNAME_RELEASE" + exit ;; + *:DragonFly:*:*) + echo "$UNAME_MACHINE"-unknown-dragonfly"`echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`" + exit ;; + *:*VMS:*:*) + UNAME_MACHINE=`(uname -p) 2>/dev/null` + case "$UNAME_MACHINE" in + A*) echo alpha-dec-vms ; exit ;; + I*) echo ia64-dec-vms ; exit ;; + V*) echo vax-dec-vms ; exit ;; + esac ;; + *:XENIX:*:SysV) + echo i386-pc-xenix + exit ;; + i*86:skyos:*:*) + echo "$UNAME_MACHINE"-pc-skyos"`echo "$UNAME_RELEASE" | sed -e 's/ .*$//'`" + exit ;; + i*86:rdos:*:*) + echo "$UNAME_MACHINE"-pc-rdos + exit ;; + i*86:AROS:*:*) + echo "$UNAME_MACHINE"-pc-aros + exit ;; + x86_64:VMkernel:*:*) + echo "$UNAME_MACHINE"-unknown-esx + exit ;; + amd64:Isilon\ OneFS:*:*) + echo x86_64-unknown-onefs + exit ;; + *:Unleashed:*:*) + echo "$UNAME_MACHINE"-unknown-unleashed"$UNAME_RELEASE" + exit ;; +esac + +# No uname command or uname output not recognized. +set_cc_for_build +cat > "$dummy.c" <<EOF +#ifdef _SEQUENT_ +#include <sys/types.h> +#include <sys/utsname.h> +#endif +#if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__) +#if defined (vax) || defined (__vax) || defined (__vax__) || defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__) +#include <signal.h> +#if defined(_SIZE_T_) || defined(SIGLOST) +#include <sys/utsname.h> +#endif +#endif +#endif +main () +{ +#if defined (sony) +#if defined (MIPSEB) + /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, + I don't know.... */ + printf ("mips-sony-bsd\n"); exit (0); +#else +#include <sys/param.h> + printf ("m68k-sony-newsos%s\n", +#ifdef NEWSOS4 + "4" +#else + "" +#endif + ); exit (0); +#endif +#endif + +#if defined (NeXT) +#if !defined (__ARCHITECTURE__) +#define __ARCHITECTURE__ "m68k" +#endif + int version; + version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; + if (version < 4) + printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); + else + printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); + exit (0); +#endif + +#if defined (MULTIMAX) || defined (n16) +#if defined (UMAXV) + printf ("ns32k-encore-sysv\n"); exit (0); +#else +#if defined (CMU) + printf ("ns32k-encore-mach\n"); exit (0); +#else + printf ("ns32k-encore-bsd\n"); exit (0); +#endif +#endif +#endif + +#if defined (__386BSD__) + printf ("i386-pc-bsd\n"); exit (0); +#endif + +#if defined (sequent) +#if defined (i386) + printf ("i386-sequent-dynix\n"); exit (0); +#endif +#if defined (ns32000) + printf ("ns32k-sequent-dynix\n"); exit (0); +#endif +#endif + +#if defined (_SEQUENT_) + struct utsname un; + + uname(&un); + if (strncmp(un.version, "V2", 2) == 0) { + printf ("i386-sequent-ptx2\n"); exit (0); + } + if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ + printf ("i386-sequent-ptx1\n"); exit (0); + } + printf ("i386-sequent-ptx\n"); exit (0); +#endif + +#if defined (vax) +#if !defined (ultrix) +#include <sys/param.h> +#if defined (BSD) +#if BSD == 43 + printf ("vax-dec-bsd4.3\n"); exit (0); +#else +#if BSD == 199006 + printf ("vax-dec-bsd4.3reno\n"); exit (0); +#else + printf ("vax-dec-bsd\n"); exit (0); +#endif +#endif +#else + printf ("vax-dec-bsd\n"); exit (0); +#endif +#else +#if defined(_SIZE_T_) || defined(SIGLOST) + struct utsname un; + uname (&un); + printf ("vax-dec-ultrix%s\n", un.release); exit (0); +#else + printf ("vax-dec-ultrix\n"); exit (0); +#endif +#endif +#endif +#if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__) +#if defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__) +#if defined(_SIZE_T_) || defined(SIGLOST) + struct utsname *un; + uname (&un); + printf ("mips-dec-ultrix%s\n", un.release); exit (0); +#else + printf ("mips-dec-ultrix\n"); exit (0); +#endif +#endif +#endif + +#if defined (alliant) && defined (i860) + printf ("i860-alliant-bsd\n"); exit (0); +#endif + + exit (1); +} +EOF + +$CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null && SYSTEM_NAME=`$dummy` && + { echo "$SYSTEM_NAME"; exit; } + +# Apollos put the system type in the environment. +test -d /usr/apollo && { echo "$ISP-apollo-$SYSTYPE"; exit; } + +echo "$0: unable to guess system type" >&2 + +case "$UNAME_MACHINE:$UNAME_SYSTEM" in + mips:Linux | mips64:Linux) + # If we got here on MIPS GNU/Linux, output extra information. + cat >&2 <<EOF + +NOTE: MIPS GNU/Linux systems require a C compiler to fully recognize +the system type. Please install a C compiler and try again. +EOF + ;; +esac + +cat >&2 <<EOF + +This script (version $timestamp), has failed to recognize the +operating system you are using. If your script is old, overwrite *all* +copies of config.guess and config.sub with the latest versions from: + + https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess +and + https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub +EOF + +year=`echo $timestamp | sed 's,-.*,,'` +# shellcheck disable=SC2003 +if test "`expr "\`date +%Y\`" - "$year"`" -lt 3 ; then + cat >&2 <<EOF + +If $0 has already been updated, send the following data and any +information you think might be pertinent to config-patches@gnu.org to +provide the necessary information to handle your system. + +config.guess timestamp = $timestamp + +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null` + +hostinfo = `(hostinfo) 2>/dev/null` +/bin/universe = `(/bin/universe) 2>/dev/null` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` +/bin/arch = `(/bin/arch) 2>/dev/null` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` + +UNAME_MACHINE = "$UNAME_MACHINE" +UNAME_RELEASE = "$UNAME_RELEASE" +UNAME_SYSTEM = "$UNAME_SYSTEM" +UNAME_VERSION = "$UNAME_VERSION" +EOF +fi + +exit 1 + +# Local variables: +# eval: (add-hook 'before-save-hook 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/config/config.sub b/config/config.sub new file mode 100755 index 0000000..dae00e6 --- /dev/null +++ b/config/config.sub @@ -0,0 +1,1848 @@ +#! /bin/sh +# Configuration validation subroutine script. +# Copyright 1992-2020 Free Software Foundation, Inc. + +timestamp='2020-09-08' + +# This file 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 3 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, see <https://www.gnu.org/licenses/>. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that +# program. This Exception is an additional permission under section 7 +# of the GNU General Public License, version 3 ("GPLv3"). + + +# Please send patches to <config-patches@gnu.org>. +# +# Configuration subroutine to validate and canonicalize a configuration type. +# Supply the specified configuration type as an argument. +# If it is invalid, we print an error message on stderr and exit with code 1. +# Otherwise, we print the canonical config type on stdout and succeed. + +# You can get the latest version of this script from: +# https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub + +# This file is supposed to be the same for all GNU packages +# and recognize all the CPU types, system types and aliases +# that are meaningful with *any* GNU software. +# Each package is responsible for reporting which valid configurations +# it does not support. The user should be able to distinguish +# a failure to support a valid configuration from a meaningless +# configuration. + +# The goal of this file is to map all the various variations of a given +# machine specification into a single specification in the form: +# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM +# or in some cases, the newer four-part form: +# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM +# It is wrong to echo any other type of specification. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS + +Canonicalize a configuration name. + +Options: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to <config-patches@gnu.org>." + +version="\ +GNU config.sub ($timestamp) + +Copyright 1992-2020 Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit ;; + --version | -v ) + echo "$version" ; exit ;; + --help | --h* | -h ) + echo "$usage"; exit ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" >&2 + exit 1 ;; + + *local*) + # First pass through any local machine types. + echo "$1" + exit ;; + + * ) + break ;; + esac +done + +case $# in + 0) echo "$me: missing argument$help" >&2 + exit 1;; + 1) ;; + *) echo "$me: too many arguments$help" >&2 + exit 1;; +esac + +# Split fields of configuration type +# shellcheck disable=SC2162 +IFS="-" read field1 field2 field3 field4 <<EOF +$1 +EOF + +# Separate into logical components for further validation +case $1 in + *-*-*-*-*) + echo Invalid configuration \`"$1"\': more than four components >&2 + exit 1 + ;; + *-*-*-*) + basic_machine=$field1-$field2 + basic_os=$field3-$field4 + ;; + *-*-*) + # Ambiguous whether COMPANY is present, or skipped and KERNEL-OS is two + # parts + maybe_os=$field2-$field3 + case $maybe_os in + nto-qnx* | linux-* | uclinux-uclibc* \ + | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* \ + | netbsd*-eabi* | kopensolaris*-gnu* | cloudabi*-eabi* \ + | storm-chaos* | os2-emx* | rtmk-nova*) + basic_machine=$field1 + basic_os=$maybe_os + ;; + android-linux) + basic_machine=$field1-unknown + basic_os=linux-android + ;; + *) + basic_machine=$field1-$field2 + basic_os=$field3 + ;; + esac + ;; + *-*) + # A lone config we happen to match not fitting any pattern + case $field1-$field2 in + decstation-3100) + basic_machine=mips-dec + basic_os= + ;; + *-*) + # Second component is usually, but not always the OS + case $field2 in + # Prevent following clause from handling this valid os + sun*os*) + basic_machine=$field1 + basic_os=$field2 + ;; + # Manufacturers + dec* | mips* | sequent* | encore* | pc533* | sgi* | sony* \ + | att* | 7300* | 3300* | delta* | motorola* | sun[234]* \ + | unicom* | ibm* | next | hp | isi* | apollo | altos* \ + | convergent* | ncr* | news | 32* | 3600* | 3100* \ + | hitachi* | c[123]* | convex* | sun | crds | omron* | dg \ + | ultra | tti* | harris | dolphin | highlevel | gould \ + | cbm | ns | masscomp | apple | axis | knuth | cray \ + | microblaze* | sim | cisco \ + | oki | wec | wrs | winbond) + basic_machine=$field1-$field2 + basic_os= + ;; + *) + basic_machine=$field1 + basic_os=$field2 + ;; + esac + ;; + esac + ;; + *) + # Convert single-component short-hands not valid as part of + # multi-component configurations. + case $field1 in + 386bsd) + basic_machine=i386-pc + basic_os=bsd + ;; + a29khif) + basic_machine=a29k-amd + basic_os=udi + ;; + adobe68k) + basic_machine=m68010-adobe + basic_os=scout + ;; + alliant) + basic_machine=fx80-alliant + basic_os= + ;; + altos | altos3068) + basic_machine=m68k-altos + basic_os= + ;; + am29k) + basic_machine=a29k-none + basic_os=bsd + ;; + amdahl) + basic_machine=580-amdahl + basic_os=sysv + ;; + amiga) + basic_machine=m68k-unknown + basic_os= + ;; + amigaos | amigados) + basic_machine=m68k-unknown + basic_os=amigaos + ;; + amigaunix | amix) + basic_machine=m68k-unknown + basic_os=sysv4 + ;; + apollo68) + basic_machine=m68k-apollo + basic_os=sysv + ;; + apollo68bsd) + basic_machine=m68k-apollo + basic_os=bsd + ;; + aros) + basic_machine=i386-pc + basic_os=aros + ;; + aux) + basic_machine=m68k-apple + basic_os=aux + ;; + balance) + basic_machine=ns32k-sequent + basic_os=dynix + ;; + blackfin) + basic_machine=bfin-unknown + basic_os=linux + ;; + cegcc) + basic_machine=arm-unknown + basic_os=cegcc + ;; + convex-c1) + basic_machine=c1-convex + basic_os=bsd + ;; + convex-c2) + basic_machine=c2-convex + basic_os=bsd + ;; + convex-c32) + basic_machine=c32-convex + basic_os=bsd + ;; + convex-c34) + basic_machine=c34-convex + basic_os=bsd + ;; + convex-c38) + basic_machine=c38-convex + basic_os=bsd + ;; + cray) + basic_machine=j90-cray + basic_os=unicos + ;; + crds | unos) + basic_machine=m68k-crds + basic_os= + ;; + da30) + basic_machine=m68k-da30 + basic_os= + ;; + decstation | pmax | pmin | dec3100 | decstatn) + basic_machine=mips-dec + basic_os= + ;; + delta88) + basic_machine=m88k-motorola + basic_os=sysv3 + ;; + dicos) + basic_machine=i686-pc + basic_os=dicos + ;; + djgpp) + basic_machine=i586-pc + basic_os=msdosdjgpp + ;; + ebmon29k) + basic_machine=a29k-amd + basic_os=ebmon + ;; + es1800 | OSE68k | ose68k | ose | OSE) + basic_machine=m68k-ericsson + basic_os=ose + ;; + gmicro) + basic_machine=tron-gmicro + basic_os=sysv + ;; + go32) + basic_machine=i386-pc + basic_os=go32 + ;; + h8300hms) + basic_machine=h8300-hitachi + basic_os=hms + ;; + h8300xray) + basic_machine=h8300-hitachi + basic_os=xray + ;; + h8500hms) + basic_machine=h8500-hitachi + basic_os=hms + ;; + harris) + basic_machine=m88k-harris + basic_os=sysv3 + ;; + hp300 | hp300hpux) + basic_machine=m68k-hp + basic_os=hpux + ;; + hp300bsd) + basic_machine=m68k-hp + basic_os=bsd + ;; + hppaosf) + basic_machine=hppa1.1-hp + basic_os=osf + ;; + hppro) + basic_machine=hppa1.1-hp + basic_os=proelf + ;; + i386mach) + basic_machine=i386-mach + basic_os=mach + ;; + isi68 | isi) + basic_machine=m68k-isi + basic_os=sysv + ;; + m68knommu) + basic_machine=m68k-unknown + basic_os=linux + ;; + magnum | m3230) + basic_machine=mips-mips + basic_os=sysv + ;; + merlin) + basic_machine=ns32k-utek + basic_os=sysv + ;; + mingw64) + basic_machine=x86_64-pc + basic_os=mingw64 + ;; + mingw32) + basic_machine=i686-pc + basic_os=mingw32 + ;; + mingw32ce) + basic_machine=arm-unknown + basic_os=mingw32ce + ;; + monitor) + basic_machine=m68k-rom68k + basic_os=coff + ;; + morphos) + basic_machine=powerpc-unknown + basic_os=morphos + ;; + moxiebox) + basic_machine=moxie-unknown + basic_os=moxiebox + ;; + msdos) + basic_machine=i386-pc + basic_os=msdos + ;; + msys) + basic_machine=i686-pc + basic_os=msys + ;; + mvs) + basic_machine=i370-ibm + basic_os=mvs + ;; + nacl) + basic_machine=le32-unknown + basic_os=nacl + ;; + ncr3000) + basic_machine=i486-ncr + basic_os=sysv4 + ;; + netbsd386) + basic_machine=i386-pc + basic_os=netbsd + ;; + netwinder) + basic_machine=armv4l-rebel + basic_os=linux + ;; + news | news700 | news800 | news900) + basic_machine=m68k-sony + basic_os=newsos + ;; + news1000) + basic_machine=m68030-sony + basic_os=newsos + ;; + necv70) + basic_machine=v70-nec + basic_os=sysv + ;; + nh3000) + basic_machine=m68k-harris + basic_os=cxux + ;; + nh[45]000) + basic_machine=m88k-harris + basic_os=cxux + ;; + nindy960) + basic_machine=i960-intel + basic_os=nindy + ;; + mon960) + basic_machine=i960-intel + basic_os=mon960 + ;; + nonstopux) + basic_machine=mips-compaq + basic_os=nonstopux + ;; + os400) + basic_machine=powerpc-ibm + basic_os=os400 + ;; + OSE68000 | ose68000) + basic_machine=m68000-ericsson + basic_os=ose + ;; + os68k) + basic_machine=m68k-none + basic_os=os68k + ;; + paragon) + basic_machine=i860-intel + basic_os=osf + ;; + parisc) + basic_machine=hppa-unknown + basic_os=linux + ;; + psp) + basic_machine=mipsallegrexel-sony + basic_os=psp + ;; + pw32) + basic_machine=i586-unknown + basic_os=pw32 + ;; + rdos | rdos64) + basic_machine=x86_64-pc + basic_os=rdos + ;; + rdos32) + basic_machine=i386-pc + basic_os=rdos + ;; + rom68k) + basic_machine=m68k-rom68k + basic_os=coff + ;; + sa29200) + basic_machine=a29k-amd + basic_os=udi + ;; + sei) + basic_machine=mips-sei + basic_os=seiux + ;; + sequent) + basic_machine=i386-sequent + basic_os= + ;; + sps7) + basic_machine=m68k-bull + basic_os=sysv2 + ;; + st2000) + basic_machine=m68k-tandem + basic_os= + ;; + stratus) + basic_machine=i860-stratus + basic_os=sysv4 + ;; + sun2) + basic_machine=m68000-sun + basic_os= + ;; + sun2os3) + basic_machine=m68000-sun + basic_os=sunos3 + ;; + sun2os4) + basic_machine=m68000-sun + basic_os=sunos4 + ;; + sun3) + basic_machine=m68k-sun + basic_os= + ;; + sun3os3) + basic_machine=m68k-sun + basic_os=sunos3 + ;; + sun3os4) + basic_machine=m68k-sun + basic_os=sunos4 + ;; + sun4) + basic_machine=sparc-sun + basic_os= + ;; + sun4os3) + basic_machine=sparc-sun + basic_os=sunos3 + ;; + sun4os4) + basic_machine=sparc-sun + basic_os=sunos4 + ;; + sun4sol2) + basic_machine=sparc-sun + basic_os=solaris2 + ;; + sun386 | sun386i | roadrunner) + basic_machine=i386-sun + basic_os= + ;; + sv1) + basic_machine=sv1-cray + basic_os=unicos + ;; + symmetry) + basic_machine=i386-sequent + basic_os=dynix + ;; + t3e) + basic_machine=alphaev5-cray + basic_os=unicos + ;; + t90) + basic_machine=t90-cray + basic_os=unicos + ;; + toad1) + basic_machine=pdp10-xkl + basic_os=tops20 + ;; + tpf) + basic_machine=s390x-ibm + basic_os=tpf + ;; + udi29k) + basic_machine=a29k-amd + basic_os=udi + ;; + ultra3) + basic_machine=a29k-nyu + basic_os=sym1 + ;; + v810 | necv810) + basic_machine=v810-nec + basic_os=none + ;; + vaxv) + basic_machine=vax-dec + basic_os=sysv + ;; + vms) + basic_machine=vax-dec + basic_os=vms + ;; + vsta) + basic_machine=i386-pc + basic_os=vsta + ;; + vxworks960) + basic_machine=i960-wrs + basic_os=vxworks + ;; + vxworks68) + basic_machine=m68k-wrs + basic_os=vxworks + ;; + vxworks29k) + basic_machine=a29k-wrs + basic_os=vxworks + ;; + xbox) + basic_machine=i686-pc + basic_os=mingw32 + ;; + ymp) + basic_machine=ymp-cray + basic_os=unicos + ;; + *) + basic_machine=$1 + basic_os= + ;; + esac + ;; +esac + +# Decode 1-component or ad-hoc basic machines +case $basic_machine in + # Here we handle the default manufacturer of certain CPU types. It is in + # some cases the only manufacturer, in others, it is the most popular. + w89k) + cpu=hppa1.1 + vendor=winbond + ;; + op50n) + cpu=hppa1.1 + vendor=oki + ;; + op60c) + cpu=hppa1.1 + vendor=oki + ;; + ibm*) + cpu=i370 + vendor=ibm + ;; + orion105) + cpu=clipper + vendor=highlevel + ;; + mac | mpw | mac-mpw) + cpu=m68k + vendor=apple + ;; + pmac | pmac-mpw) + cpu=powerpc + vendor=apple + ;; + + # Recognize the various machine names and aliases which stand + # for a CPU type and a company and sometimes even an OS. + 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) + cpu=m68000 + vendor=att + ;; + 3b*) + cpu=we32k + vendor=att + ;; + bluegene*) + cpu=powerpc + vendor=ibm + basic_os=cnk + ;; + decsystem10* | dec10*) + cpu=pdp10 + vendor=dec + basic_os=tops10 + ;; + decsystem20* | dec20*) + cpu=pdp10 + vendor=dec + basic_os=tops20 + ;; + delta | 3300 | motorola-3300 | motorola-delta \ + | 3300-motorola | delta-motorola) + cpu=m68k + vendor=motorola + ;; + dpx2*) + cpu=m68k + vendor=bull + basic_os=sysv3 + ;; + encore | umax | mmax) + cpu=ns32k + vendor=encore + ;; + elxsi) + cpu=elxsi + vendor=elxsi + basic_os=${basic_os:-bsd} + ;; + fx2800) + cpu=i860 + vendor=alliant + ;; + genix) + cpu=ns32k + vendor=ns + ;; + h3050r* | hiux*) + cpu=hppa1.1 + vendor=hitachi + basic_os=hiuxwe2 + ;; + hp3k9[0-9][0-9] | hp9[0-9][0-9]) + cpu=hppa1.0 + vendor=hp + ;; + hp9k2[0-9][0-9] | hp9k31[0-9]) + cpu=m68000 + vendor=hp + ;; + hp9k3[2-9][0-9]) + cpu=m68k + vendor=hp + ;; + hp9k6[0-9][0-9] | hp6[0-9][0-9]) + cpu=hppa1.0 + vendor=hp + ;; + hp9k7[0-79][0-9] | hp7[0-79][0-9]) + cpu=hppa1.1 + vendor=hp + ;; + hp9k78[0-9] | hp78[0-9]) + # FIXME: really hppa2.0-hp + cpu=hppa1.1 + vendor=hp + ;; + hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) + # FIXME: really hppa2.0-hp + cpu=hppa1.1 + vendor=hp + ;; + hp9k8[0-9][13679] | hp8[0-9][13679]) + cpu=hppa1.1 + vendor=hp + ;; + hp9k8[0-9][0-9] | hp8[0-9][0-9]) + cpu=hppa1.0 + vendor=hp + ;; + i*86v32) + cpu=`echo "$1" | sed -e 's/86.*/86/'` + vendor=pc + basic_os=sysv32 + ;; + i*86v4*) + cpu=`echo "$1" | sed -e 's/86.*/86/'` + vendor=pc + basic_os=sysv4 + ;; + i*86v) + cpu=`echo "$1" | sed -e 's/86.*/86/'` + vendor=pc + basic_os=sysv + ;; + i*86sol2) + cpu=`echo "$1" | sed -e 's/86.*/86/'` + vendor=pc + basic_os=solaris2 + ;; + j90 | j90-cray) + cpu=j90 + vendor=cray + basic_os=${basic_os:-unicos} + ;; + iris | iris4d) + cpu=mips + vendor=sgi + case $basic_os in + irix*) + ;; + *) + basic_os=irix4 + ;; + esac + ;; + miniframe) + cpu=m68000 + vendor=convergent + ;; + *mint | mint[0-9]* | *MiNT | *MiNT[0-9]*) + cpu=m68k + vendor=atari + basic_os=mint + ;; + news-3600 | risc-news) + cpu=mips + vendor=sony + basic_os=newsos + ;; + next | m*-next) + cpu=m68k + vendor=next + case $basic_os in + openstep*) + ;; + nextstep*) + ;; + ns2*) + basic_os=nextstep2 + ;; + *) + basic_os=nextstep3 + ;; + esac + ;; + np1) + cpu=np1 + vendor=gould + ;; + op50n-* | op60c-*) + cpu=hppa1.1 + vendor=oki + basic_os=proelf + ;; + pa-hitachi) + cpu=hppa1.1 + vendor=hitachi + basic_os=hiuxwe2 + ;; + pbd) + cpu=sparc + vendor=tti + ;; + pbb) + cpu=m68k + vendor=tti + ;; + pc532) + cpu=ns32k + vendor=pc532 + ;; + pn) + cpu=pn + vendor=gould + ;; + power) + cpu=power + vendor=ibm + ;; + ps2) + cpu=i386 + vendor=ibm + ;; + rm[46]00) + cpu=mips + vendor=siemens + ;; + rtpc | rtpc-*) + cpu=romp + vendor=ibm + ;; + sde) + cpu=mipsisa32 + vendor=sde + basic_os=${basic_os:-elf} + ;; + simso-wrs) + cpu=sparclite + vendor=wrs + basic_os=vxworks + ;; + tower | tower-32) + cpu=m68k + vendor=ncr + ;; + vpp*|vx|vx-*) + cpu=f301 + vendor=fujitsu + ;; + w65) + cpu=w65 + vendor=wdc + ;; + w89k-*) + cpu=hppa1.1 + vendor=winbond + basic_os=proelf + ;; + none) + cpu=none + vendor=none + ;; + leon|leon[3-9]) + cpu=sparc + vendor=$basic_machine + ;; + leon-*|leon[3-9]-*) + cpu=sparc + vendor=`echo "$basic_machine" | sed 's/-.*//'` + ;; + + *-*) + # shellcheck disable=SC2162 + IFS="-" read cpu vendor <<EOF +$basic_machine +EOF + ;; + # We use `pc' rather than `unknown' + # because (1) that's what they normally are, and + # (2) the word "unknown" tends to confuse beginning users. + i*86 | x86_64) + cpu=$basic_machine + vendor=pc + ;; + # These rules are duplicated from below for sake of the special case above; + # i.e. things that normalized to x86 arches should also default to "pc" + pc98) + cpu=i386 + vendor=pc + ;; + x64 | amd64) + cpu=x86_64 + vendor=pc + ;; + # Recognize the basic CPU types without company name. + *) + cpu=$basic_machine + vendor=unknown + ;; +esac + +unset -v basic_machine + +# Decode basic machines in the full and proper CPU-Company form. +case $cpu-$vendor in + # Here we handle the default manufacturer of certain CPU types in canonical form. It is in + # some cases the only manufacturer, in others, it is the most popular. + craynv-unknown) + vendor=cray + basic_os=${basic_os:-unicosmp} + ;; + c90-unknown | c90-cray) + vendor=cray + basic_os=${Basic_os:-unicos} + ;; + fx80-unknown) + vendor=alliant + ;; + romp-unknown) + vendor=ibm + ;; + mmix-unknown) + vendor=knuth + ;; + microblaze-unknown | microblazeel-unknown) + vendor=xilinx + ;; + rs6000-unknown) + vendor=ibm + ;; + vax-unknown) + vendor=dec + ;; + pdp11-unknown) + vendor=dec + ;; + we32k-unknown) + vendor=att + ;; + cydra-unknown) + vendor=cydrome + ;; + i370-ibm*) + vendor=ibm + ;; + orion-unknown) + vendor=highlevel + ;; + xps-unknown | xps100-unknown) + cpu=xps100 + vendor=honeywell + ;; + + # Here we normalize CPU types with a missing or matching vendor + dpx20-unknown | dpx20-bull) + cpu=rs6000 + vendor=bull + basic_os=${basic_os:-bosx} + ;; + + # Here we normalize CPU types irrespective of the vendor + amd64-*) + cpu=x86_64 + ;; + blackfin-*) + cpu=bfin + basic_os=linux + ;; + c54x-*) + cpu=tic54x + ;; + c55x-*) + cpu=tic55x + ;; + c6x-*) + cpu=tic6x + ;; + e500v[12]-*) + cpu=powerpc + basic_os=${basic_os}"spe" + ;; + mips3*-*) + cpu=mips64 + ;; + ms1-*) + cpu=mt + ;; + m68knommu-*) + cpu=m68k + basic_os=linux + ;; + m9s12z-* | m68hcs12z-* | hcs12z-* | s12z-*) + cpu=s12z + ;; + openrisc-*) + cpu=or32 + ;; + parisc-*) + cpu=hppa + basic_os=linux + ;; + pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) + cpu=i586 + ;; + pentiumpro-* | p6-* | 6x86-* | athlon-* | athalon_*-*) + cpu=i686 + ;; + pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) + cpu=i686 + ;; + pentium4-*) + cpu=i786 + ;; + pc98-*) + cpu=i386 + ;; + ppc-* | ppcbe-*) + cpu=powerpc + ;; + ppcle-* | powerpclittle-*) + cpu=powerpcle + ;; + ppc64-*) + cpu=powerpc64 + ;; + ppc64le-* | powerpc64little-*) + cpu=powerpc64le + ;; + sb1-*) + cpu=mipsisa64sb1 + ;; + sb1el-*) + cpu=mipsisa64sb1el + ;; + sh5e[lb]-*) + cpu=`echo "$cpu" | sed 's/^\(sh.\)e\(.\)$/\1\2e/'` + ;; + spur-*) + cpu=spur + ;; + strongarm-* | thumb-*) + cpu=arm + ;; + tx39-*) + cpu=mipstx39 + ;; + tx39el-*) + cpu=mipstx39el + ;; + x64-*) + cpu=x86_64 + ;; + xscale-* | xscalee[bl]-*) + cpu=`echo "$cpu" | sed 's/^xscale/arm/'` + ;; + arm64-*) + cpu=aarch64 + ;; + + # Recognize the canonical CPU Types that limit and/or modify the + # company names they are paired with. + cr16-*) + basic_os=${basic_os:-elf} + ;; + crisv32-* | etraxfs*-*) + cpu=crisv32 + vendor=axis + ;; + cris-* | etrax*-*) + cpu=cris + vendor=axis + ;; + crx-*) + basic_os=${basic_os:-elf} + ;; + neo-tandem) + cpu=neo + vendor=tandem + ;; + nse-tandem) + cpu=nse + vendor=tandem + ;; + nsr-tandem) + cpu=nsr + vendor=tandem + ;; + nsv-tandem) + cpu=nsv + vendor=tandem + ;; + nsx-tandem) + cpu=nsx + vendor=tandem + ;; + mipsallegrexel-sony) + cpu=mipsallegrexel + vendor=sony + ;; + tile*-*) + basic_os=${basic_os:-linux-gnu} + ;; + + *) + # Recognize the canonical CPU types that are allowed with any + # company name. + case $cpu in + 1750a | 580 \ + | a29k \ + | aarch64 | aarch64_be \ + | abacus \ + | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] \ + | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] \ + | alphapca5[67] | alpha64pca5[67] \ + | am33_2.0 \ + | amdgcn \ + | arc | arceb \ + | arm | arm[lb]e | arme[lb] | armv* \ + | avr | avr32 \ + | asmjs \ + | ba \ + | be32 | be64 \ + | bfin | bpf | bs2000 \ + | c[123]* | c30 | [cjt]90 | c4x \ + | c8051 | clipper | craynv | csky | cydra \ + | d10v | d30v | dlx | dsp16xx \ + | e2k | elxsi | epiphany \ + | f30[01] | f700 | fido | fr30 | frv | ft32 | fx80 \ + | h8300 | h8500 \ + | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ + | hexagon \ + | i370 | i*86 | i860 | i960 | ia16 | ia64 \ + | ip2k | iq2000 \ + | k1om \ + | le32 | le64 \ + | lm32 \ + | loongarch32 | loongarch64 | loongarchx32 \ + | m32c | m32r | m32rle \ + | m5200 | m68000 | m680[012346]0 | m68360 | m683?2 | m68k \ + | m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x \ + | m88110 | m88k | maxq | mb | mcore | mep | metag \ + | microblaze | microblazeel \ + | mips | mipsbe | mipseb | mipsel | mipsle \ + | mips16 \ + | mips64 | mips64eb | mips64el \ + | mips64octeon | mips64octeonel \ + | mips64orion | mips64orionel \ + | mips64r5900 | mips64r5900el \ + | mips64vr | mips64vrel \ + | mips64vr4100 | mips64vr4100el \ + | mips64vr4300 | mips64vr4300el \ + | mips64vr5000 | mips64vr5000el \ + | mips64vr5900 | mips64vr5900el \ + | mipsisa32 | mipsisa32el \ + | mipsisa32r2 | mipsisa32r2el \ + | mipsisa32r6 | mipsisa32r6el \ + | mipsisa64 | mipsisa64el \ + | mipsisa64r2 | mipsisa64r2el \ + | mipsisa64r6 | mipsisa64r6el \ + | mipsisa64sb1 | mipsisa64sb1el \ + | mipsisa64sr71k | mipsisa64sr71kel \ + | mipsr5900 | mipsr5900el \ + | mipstx39 | mipstx39el \ + | mmix \ + | mn10200 | mn10300 \ + | moxie \ + | mt \ + | msp430 \ + | nds32 | nds32le | nds32be \ + | nfp \ + | nios | nios2 | nios2eb | nios2el \ + | none | np1 | ns16k | ns32k | nvptx \ + | open8 \ + | or1k* \ + | or32 \ + | orion \ + | picochip \ + | pdp10 | pdp11 | pj | pjl | pn | power \ + | powerpc | powerpc64 | powerpc64le | powerpcle | powerpcspe \ + | pru \ + | pyramid \ + | riscv | riscv32 | riscv64 \ + | rl78 | romp | rs6000 | rx \ + | s390 | s390x \ + | score \ + | sh | shl \ + | sh[1234] | sh[24]a | sh[24]ae[lb] | sh[23]e | she[lb] | sh[lb]e \ + | sh[1234]e[lb] | sh[12345][lb]e | sh[23]ele | sh64 | sh64le \ + | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet \ + | sparclite \ + | sparcv8 | sparcv9 | sparcv9b | sparcv9v | sv1 | sx* \ + | spu \ + | tahoe \ + | tic30 | tic4x | tic54x | tic55x | tic6x | tic80 \ + | tron \ + | ubicom32 \ + | v70 | v850 | v850e | v850e1 | v850es | v850e2 | v850e2v3 \ + | vax \ + | visium \ + | w65 \ + | wasm32 | wasm64 \ + | we32k \ + | x86 | x86_64 | xc16x | xgate | xps100 \ + | xstormy16 | xtensa* \ + | ymp \ + | z8k | z80) + ;; + + *) + echo Invalid configuration \`"$1"\': machine \`"$cpu-$vendor"\' not recognized 1>&2 + exit 1 + ;; + esac + ;; +esac + +# Here we canonicalize certain aliases for manufacturers. +case $vendor in + digital*) + vendor=dec + ;; + commodore*) + vendor=cbm + ;; + *) + ;; +esac + +# Decode manufacturer-specific aliases for certain operating systems. + +if test x$basic_os != x +then + +# First recognize some ad-hoc caes, or perhaps split kernel-os, or else just +# set os. +case $basic_os in + gnu/linux*) + kernel=linux + os=`echo $basic_os | sed -e 's|gnu/linux|gnu|'` + ;; + nto-qnx*) + kernel=nto + os=`echo $basic_os | sed -e 's|nto-qnx|qnx|'` + ;; + *-*) + # shellcheck disable=SC2162 + IFS="-" read kernel os <<EOF +$basic_os +EOF + ;; + # Default OS when just kernel was specified + nto*) + kernel=nto + os=`echo $basic_os | sed -e 's|nto|qnx|'` + ;; + linux*) + kernel=linux + os=`echo $basic_os | sed -e 's|linux|gnu|'` + ;; + *) + kernel= + os=$basic_os + ;; +esac + +# Now, normalize the OS (knowing we just have one component, it's not a kernel, +# etc.) +case $os in + # First match some system type aliases that might get confused + # with valid system types. + # solaris* is a basic system type, with this one exception. + auroraux) + os=auroraux + ;; + bluegene*) + os=cnk + ;; + solaris1 | solaris1.*) + os=`echo $os | sed -e 's|solaris1|sunos4|'` + ;; + solaris) + os=solaris2 + ;; + unixware*) + os=sysv4.2uw + ;; + # es1800 is here to avoid being matched by es* (a different OS) + es1800*) + os=ose + ;; + # Some version numbers need modification + chorusos*) + os=chorusos + ;; + isc) + os=isc2.2 + ;; + sco6) + os=sco5v6 + ;; + sco5) + os=sco3.2v5 + ;; + sco4) + os=sco3.2v4 + ;; + sco3.2.[4-9]*) + os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` + ;; + sco*v* | scout) + # Don't match below + ;; + sco*) + os=sco3.2v2 + ;; + psos*) + os=psos + ;; + qnx*) + os=qnx + ;; + hiux*) + os=hiuxwe2 + ;; + lynx*178) + os=lynxos178 + ;; + lynx*5) + os=lynxos5 + ;; + lynxos*) + # don't get caught up in next wildcard + ;; + lynx*) + os=lynxos + ;; + mac[0-9]*) + os=`echo "$os" | sed -e 's|mac|macos|'` + ;; + opened*) + os=openedition + ;; + os400*) + os=os400 + ;; + sunos5*) + os=`echo "$os" | sed -e 's|sunos5|solaris2|'` + ;; + sunos6*) + os=`echo "$os" | sed -e 's|sunos6|solaris3|'` + ;; + wince*) + os=wince + ;; + utek*) + os=bsd + ;; + dynix*) + os=bsd + ;; + acis*) + os=aos + ;; + atheos*) + os=atheos + ;; + syllable*) + os=syllable + ;; + 386bsd) + os=bsd + ;; + ctix* | uts*) + os=sysv + ;; + nova*) + os=rtmk-nova + ;; + ns2) + os=nextstep2 + ;; + # Preserve the version number of sinix5. + sinix5.*) + os=`echo $os | sed -e 's|sinix|sysv|'` + ;; + sinix*) + os=sysv4 + ;; + tpf*) + os=tpf + ;; + triton*) + os=sysv3 + ;; + oss*) + os=sysv3 + ;; + svr4*) + os=sysv4 + ;; + svr3) + os=sysv3 + ;; + sysvr4) + os=sysv4 + ;; + ose*) + os=ose + ;; + *mint | mint[0-9]* | *MiNT | MiNT[0-9]*) + os=mint + ;; + dicos*) + os=dicos + ;; + pikeos*) + # Until real need of OS specific support for + # particular features comes up, bare metal + # configurations are quite functional. + case $cpu in + arm*) + os=eabi + ;; + *) + os=elf + ;; + esac + ;; + *) + # No normalization, but not necessarily accepted, that comes below. + ;; +esac + +else + +# Here we handle the default operating systems that come with various machines. +# The value should be what the vendor currently ships out the door with their +# machine or put another way, the most popular os provided with the machine. + +# Note that if you're going to try to match "-MANUFACTURER" here (say, +# "-sun"), then you have to tell the case statement up towards the top +# that MANUFACTURER isn't an operating system. Otherwise, code above +# will signal an error saying that MANUFACTURER isn't an operating +# system, and we'll never get to this point. + +kernel= +case $cpu-$vendor in + score-*) + os=elf + ;; + spu-*) + os=elf + ;; + *-acorn) + os=riscix1.2 + ;; + arm*-rebel) + kernel=linux + os=gnu + ;; + arm*-semi) + os=aout + ;; + c4x-* | tic4x-*) + os=coff + ;; + c8051-*) + os=elf + ;; + clipper-intergraph) + os=clix + ;; + hexagon-*) + os=elf + ;; + tic54x-*) + os=coff + ;; + tic55x-*) + os=coff + ;; + tic6x-*) + os=coff + ;; + # This must come before the *-dec entry. + pdp10-*) + os=tops20 + ;; + pdp11-*) + os=none + ;; + *-dec | vax-*) + os=ultrix4.2 + ;; + m68*-apollo) + os=domain + ;; + i386-sun) + os=sunos4.0.2 + ;; + m68000-sun) + os=sunos3 + ;; + m68*-cisco) + os=aout + ;; + mep-*) + os=elf + ;; + mips*-cisco) + os=elf + ;; + mips*-*) + os=elf + ;; + or32-*) + os=coff + ;; + *-tti) # must be before sparc entry or we get the wrong os. + os=sysv3 + ;; + sparc-* | *-sun) + os=sunos4.1.1 + ;; + pru-*) + os=elf + ;; + *-be) + os=beos + ;; + *-ibm) + os=aix + ;; + *-knuth) + os=mmixware + ;; + *-wec) + os=proelf + ;; + *-winbond) + os=proelf + ;; + *-oki) + os=proelf + ;; + *-hp) + os=hpux + ;; + *-hitachi) + os=hiux + ;; + i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) + os=sysv + ;; + *-cbm) + os=amigaos + ;; + *-dg) + os=dgux + ;; + *-dolphin) + os=sysv3 + ;; + m68k-ccur) + os=rtu + ;; + m88k-omron*) + os=luna + ;; + *-next) + os=nextstep + ;; + *-sequent) + os=ptx + ;; + *-crds) + os=unos + ;; + *-ns) + os=genix + ;; + i370-*) + os=mvs + ;; + *-gould) + os=sysv + ;; + *-highlevel) + os=bsd + ;; + *-encore) + os=bsd + ;; + *-sgi) + os=irix + ;; + *-siemens) + os=sysv4 + ;; + *-masscomp) + os=rtu + ;; + f30[01]-fujitsu | f700-fujitsu) + os=uxpv + ;; + *-rom68k) + os=coff + ;; + *-*bug) + os=coff + ;; + *-apple) + os=macos + ;; + *-atari*) + os=mint + ;; + *-wrs) + os=vxworks + ;; + *) + os=none + ;; +esac + +fi + +# Now, validate our (potentially fixed-up) OS. +case $os in + # Sometimes we do "kernel-abi", so those need to count as OSes. + musl* | newlib* | uclibc*) + ;; + # Likewise for "kernel-libc" + eabi | eabihf | gnueabi | gnueabihf) + ;; + # Now accept the basic system types. + # The portable systems comes first. + # Each alternative MUST end in a * to match a version number. + gnu* | android* | bsd* | mach* | minix* | genix* | ultrix* | irix* \ + | *vms* | esix* | aix* | cnk* | sunos | sunos[34]* \ + | hpux* | unos* | osf* | luna* | dgux* | auroraux* | solaris* \ + | sym* | plan9* | psp* | sim* | xray* | os68k* | v88r* \ + | hiux* | abug | nacl* | netware* | windows* \ + | os9* | macos* | osx* | ios* \ + | mpw* | magic* | mmixware* | mon960* | lnews* \ + | amigaos* | amigados* | msdos* | newsos* | unicos* | aof* \ + | aos* | aros* | cloudabi* | sortix* | twizzler* \ + | nindy* | vxsim* | vxworks* | ebmon* | hms* | mvs* \ + | clix* | riscos* | uniplus* | iris* | isc* | rtu* | xenix* \ + | mirbsd* | netbsd* | dicos* | openedition* | ose* \ + | bitrig* | openbsd* | solidbsd* | libertybsd* | os108* \ + | ekkobsd* | freebsd* | riscix* | lynxos* | os400* \ + | bosx* | nextstep* | cxux* | aout* | elf* | oabi* \ + | ptx* | coff* | ecoff* | winnt* | domain* | vsta* \ + | udi* | lites* | ieee* | go32* | aux* | hcos* \ + | chorusrdb* | cegcc* | glidix* \ + | cygwin* | msys* | pe* | moss* | proelf* | rtems* \ + | midipix* | mingw32* | mingw64* | mint* \ + | uxpv* | beos* | mpeix* | udk* | moxiebox* \ + | interix* | uwin* | mks* | rhapsody* | darwin* \ + | openstep* | oskit* | conix* | pw32* | nonstopux* \ + | storm-chaos* | tops10* | tenex* | tops20* | its* \ + | os2* | vos* | palmos* | uclinux* | nucleus* | morphos* \ + | scout* | superux* | sysv* | rtmk* | tpf* | windiss* \ + | powermax* | dnix* | nx6 | nx7 | sei* | dragonfly* \ + | skyos* | haiku* | rdos* | toppers* | drops* | es* \ + | onefs* | tirtos* | phoenix* | fuchsia* | redox* | bme* \ + | midnightbsd* | amdhsa* | unleashed* | emscripten* | wasi* \ + | nsk* | powerunix* | genode* | zvmoe* | qnx* ) + ;; + # This one is extra strict with allowed versions + sco3.2v2 | sco3.2v[4-9]* | sco5v6*) + # Don't forget version if it is 3.2v4 or newer. + ;; + none) + ;; + *) + echo Invalid configuration \`"$1"\': OS \`"$os"\' not recognized 1>&2 + exit 1 + ;; +esac + +# As a final step for OS-related things, validate the OS-kernel combination +# (given a valid OS), if there is a kernel. +case $kernel-$os in + linux-gnu* | linux-dietlibc* | linux-android* | linux-newlib* | linux-musl* | linux-uclibc* ) + ;; + uclinux-uclibc* ) + ;; + -dietlibc* | -newlib* | -musl* | -uclibc* ) + # These are just libc implementations, not actual OSes, and thus + # require a kernel. + echo "Invalid configuration \`$1': libc \`$os' needs explicit kernel." 1>&2 + exit 1 + ;; + kfreebsd*-gnu* | kopensolaris*-gnu*) + ;; + nto-qnx*) + ;; + *-eabi* | *-gnueabi*) + ;; + -*) + # Blank kernel with real OS is always fine. + ;; + *-*) + echo "Invalid configuration \`$1': Kernel \`$kernel' not known to work with OS \`$os'." 1>&2 + exit 1 + ;; +esac + +# Here we handle the case where we know the os, and the CPU type, but not the +# manufacturer. We pick the logical manufacturer. +case $vendor in + unknown) + case $cpu-$os in + *-riscix*) + vendor=acorn + ;; + *-sunos*) + vendor=sun + ;; + *-cnk* | *-aix*) + vendor=ibm + ;; + *-beos*) + vendor=be + ;; + *-hpux*) + vendor=hp + ;; + *-mpeix*) + vendor=hp + ;; + *-hiux*) + vendor=hitachi + ;; + *-unos*) + vendor=crds + ;; + *-dgux*) + vendor=dg + ;; + *-luna*) + vendor=omron + ;; + *-genix*) + vendor=ns + ;; + *-clix*) + vendor=intergraph + ;; + *-mvs* | *-opened*) + vendor=ibm + ;; + *-os400*) + vendor=ibm + ;; + s390-* | s390x-*) + vendor=ibm + ;; + *-ptx*) + vendor=sequent + ;; + *-tpf*) + vendor=ibm + ;; + *-vxsim* | *-vxworks* | *-windiss*) + vendor=wrs + ;; + *-aux*) + vendor=apple + ;; + *-hms*) + vendor=hitachi + ;; + *-mpw* | *-macos*) + vendor=apple + ;; + *-*mint | *-mint[0-9]* | *-*MiNT | *-MiNT[0-9]*) + vendor=atari + ;; + *-vos*) + vendor=stratus + ;; + esac + ;; +esac + +echo "$cpu-$vendor-${kernel:+$kernel-}$os" +exit + +# Local variables: +# eval: (add-hook 'before-save-hook 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/config/install-sh b/config/install-sh new file mode 100755 index 0000000..b34a8fc --- /dev/null +++ b/config/install-sh @@ -0,0 +1,514 @@ +#!/bin/sh +# install - install a program, script, or datafile + +scriptversion=2020-07-26.22; # UTC + +# This originates from X11R5 (mit/util/scripts/install.sh), which was +# later released in X11R6 (xc/config/util/install.sh) with the +# following copyright and license. +# +# Copyright (C) 1994 X Consortium +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- +# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +# Except as contained in this notice, the name of the X Consortium shall not +# be used in advertising or otherwise to promote the sale, use or other deal- +# ings in this Software without prior written authorization from the X Consor- +# tium. +# +# +# FSF changes to this file are in the public domain. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# 'make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. + +tab=' ' +nl=' +' +IFS=" $tab$nl" + +# Set DOITPROG to "echo" to test this script. + +doit=${DOITPROG-} +doit_exec=${doit:-exec} + +# Put in absolute file names if you don't have them in your path; +# or use environment vars. + +chgrpprog=${CHGRPPROG-chgrp} +chmodprog=${CHMODPROG-chmod} +chownprog=${CHOWNPROG-chown} +cmpprog=${CMPPROG-cmp} +cpprog=${CPPROG-cp} +mkdirprog=${MKDIRPROG-mkdir} +mvprog=${MVPROG-mv} +rmprog=${RMPROG-rm} +stripprog=${STRIPPROG-strip} + +posix_mkdir= + +# Desired mode of installed file. +mode=0755 + +# Create dirs (including intermediate dirs) using mode 755. +# This is like GNU 'install' as of coreutils 8.32 (2020). +mkdir_umask=22 + +chgrpcmd= +chmodcmd=$chmodprog +chowncmd= +mvcmd=$mvprog +rmcmd="$rmprog -f" +stripcmd= + +src= +dst= +dir_arg= +dst_arg= + +copy_on_change=false +is_target_a_directory=possibly + +usage="\ +Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE + or: $0 [OPTION]... SRCFILES... DIRECTORY + or: $0 [OPTION]... -t DIRECTORY SRCFILES... + or: $0 [OPTION]... -d DIRECTORIES... + +In the 1st form, copy SRCFILE to DSTFILE. +In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. +In the 4th, create DIRECTORIES. + +Options: + --help display this help and exit. + --version display version info and exit. + + -c (ignored) + -C install only if different (preserve the last data modification time) + -d create directories instead of installing files. + -g GROUP $chgrpprog installed files to GROUP. + -m MODE $chmodprog installed files to MODE. + -o USER $chownprog installed files to USER. + -s $stripprog installed files. + -t DIRECTORY install into DIRECTORY. + -T report an error if DSTFILE is a directory. + +Environment variables override the default commands: + CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG + RMPROG STRIPPROG +" + +while test $# -ne 0; do + case $1 in + -c) ;; + + -C) copy_on_change=true;; + + -d) dir_arg=true;; + + -g) chgrpcmd="$chgrpprog $2" + shift;; + + --help) echo "$usage"; exit $?;; + + -m) mode=$2 + case $mode in + *' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*) + echo "$0: invalid mode: $mode" >&2 + exit 1;; + esac + shift;; + + -o) chowncmd="$chownprog $2" + shift;; + + -s) stripcmd=$stripprog;; + + -t) + is_target_a_directory=always + dst_arg=$2 + # Protect names problematic for 'test' and other utilities. + case $dst_arg in + -* | [=\(\)!]) dst_arg=./$dst_arg;; + esac + shift;; + + -T) is_target_a_directory=never;; + + --version) echo "$0 $scriptversion"; exit $?;; + + --) shift + break;; + + -*) echo "$0: invalid option: $1" >&2 + exit 1;; + + *) break;; + esac + shift +done + +# We allow the use of options -d and -T together, by making -d +# take the precedence; this is for compatibility with GNU install. + +if test -n "$dir_arg"; then + if test -n "$dst_arg"; then + echo "$0: target directory not allowed when installing a directory." >&2 + exit 1 + fi +fi + +if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then + # When -d is used, all remaining arguments are directories to create. + # When -t is used, the destination is already specified. + # Otherwise, the last argument is the destination. Remove it from $@. + for arg + do + if test -n "$dst_arg"; then + # $@ is not empty: it contains at least $arg. + set fnord "$@" "$dst_arg" + shift # fnord + fi + shift # arg + dst_arg=$arg + # Protect names problematic for 'test' and other utilities. + case $dst_arg in + -* | [=\(\)!]) dst_arg=./$dst_arg;; + esac + done +fi + +if test $# -eq 0; then + if test -z "$dir_arg"; then + echo "$0: no input file specified." >&2 + exit 1 + fi + # It's OK to call 'install-sh -d' without argument. + # This can happen when creating conditional directories. + exit 0 +fi + +if test -z "$dir_arg"; then + if test $# -gt 1 || test "$is_target_a_directory" = always; then + if test ! -d "$dst_arg"; then + echo "$0: $dst_arg: Is not a directory." >&2 + exit 1 + fi + fi +fi + +if test -z "$dir_arg"; then + do_exit='(exit $ret); exit $ret' + trap "ret=129; $do_exit" 1 + trap "ret=130; $do_exit" 2 + trap "ret=141; $do_exit" 13 + trap "ret=143; $do_exit" 15 + + # Set umask so as not to create temps with too-generous modes. + # However, 'strip' requires both read and write access to temps. + case $mode in + # Optimize common cases. + *644) cp_umask=133;; + *755) cp_umask=22;; + + *[0-7]) + if test -z "$stripcmd"; then + u_plus_rw= + else + u_plus_rw='% 200' + fi + cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; + *) + if test -z "$stripcmd"; then + u_plus_rw= + else + u_plus_rw=,u+rw + fi + cp_umask=$mode$u_plus_rw;; + esac +fi + +for src +do + # Protect names problematic for 'test' and other utilities. + case $src in + -* | [=\(\)!]) src=./$src;; + esac + + if test -n "$dir_arg"; then + dst=$src + dstdir=$dst + test -d "$dstdir" + dstdir_status=$? + else + + # Waiting for this to be detected by the "$cpprog $src $dsttmp" command + # might cause directories to be created, which would be especially bad + # if $src (and thus $dsttmp) contains '*'. + if test ! -f "$src" && test ! -d "$src"; then + echo "$0: $src does not exist." >&2 + exit 1 + fi + + if test -z "$dst_arg"; then + echo "$0: no destination specified." >&2 + exit 1 + fi + dst=$dst_arg + + # If destination is a directory, append the input filename. + if test -d "$dst"; then + if test "$is_target_a_directory" = never; then + echo "$0: $dst_arg: Is a directory" >&2 + exit 1 + fi + dstdir=$dst + dstbase=`basename "$src"` + case $dst in + */) dst=$dst$dstbase;; + *) dst=$dst/$dstbase;; + esac + dstdir_status=0 + else + dstdir=`dirname "$dst"` + test -d "$dstdir" + dstdir_status=$? + fi + fi + + case $dstdir in + */) dstdirslash=$dstdir;; + *) dstdirslash=$dstdir/;; + esac + + obsolete_mkdir_used=false + + if test $dstdir_status != 0; then + case $posix_mkdir in + '') + # With -d, create the new directory with the user-specified mode. + # Otherwise, rely on $mkdir_umask. + if test -n "$dir_arg"; then + mkdir_mode=-m$mode + else + mkdir_mode= + fi + + posix_mkdir=false + # The $RANDOM variable is not portable (e.g., dash). Use it + # here however when possible just to lower collision chance. + tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ + + trap ' + ret=$? + rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null + exit $ret + ' 0 + + # Because "mkdir -p" follows existing symlinks and we likely work + # directly in world-writeable /tmp, make sure that the '$tmpdir' + # directory is successfully created first before we actually test + # 'mkdir -p'. + if (umask $mkdir_umask && + $mkdirprog $mkdir_mode "$tmpdir" && + exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1 + then + if test -z "$dir_arg" || { + # Check for POSIX incompatibilities with -m. + # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or + # other-writable bit of parent directory when it shouldn't. + # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. + test_tmpdir="$tmpdir/a" + ls_ld_tmpdir=`ls -ld "$test_tmpdir"` + case $ls_ld_tmpdir in + d????-?r-*) different_mode=700;; + d????-?--*) different_mode=755;; + *) false;; + esac && + $mkdirprog -m$different_mode -p -- "$test_tmpdir" && { + ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"` + test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" + } + } + then posix_mkdir=: + fi + rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" + else + # Remove any dirs left behind by ancient mkdir implementations. + rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null + fi + trap '' 0;; + esac + + if + $posix_mkdir && ( + umask $mkdir_umask && + $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" + ) + then : + else + + # mkdir does not conform to POSIX, + # or it failed possibly due to a race condition. Create the + # directory the slow way, step by step, checking for races as we go. + + case $dstdir in + /*) prefix='/';; + [-=\(\)!]*) prefix='./';; + *) prefix='';; + esac + + oIFS=$IFS + IFS=/ + set -f + set fnord $dstdir + shift + set +f + IFS=$oIFS + + prefixes= + + for d + do + test X"$d" = X && continue + + prefix=$prefix$d + if test -d "$prefix"; then + prefixes= + else + if $posix_mkdir; then + (umask $mkdir_umask && + $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break + # Don't fail if two instances are running concurrently. + test -d "$prefix" || exit 1 + else + case $prefix in + *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; + *) qprefix=$prefix;; + esac + prefixes="$prefixes '$qprefix'" + fi + fi + prefix=$prefix/ + done + + if test -n "$prefixes"; then + # Don't fail if two instances are running concurrently. + (umask $mkdir_umask && + eval "\$doit_exec \$mkdirprog $prefixes") || + test -d "$dstdir" || exit 1 + obsolete_mkdir_used=true + fi + fi + fi + + if test -n "$dir_arg"; then + { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && + { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && + { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || + test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 + else + + # Make a couple of temp file names in the proper directory. + dsttmp=${dstdirslash}_inst.$$_ + rmtmp=${dstdirslash}_rm.$$_ + + # Trap to clean up those temp files at exit. + trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 + + # Copy the file name to the temp name. + (umask $cp_umask && + { test -z "$stripcmd" || { + # Create $dsttmp read-write so that cp doesn't create it read-only, + # which would cause strip to fail. + if test -z "$doit"; then + : >"$dsttmp" # No need to fork-exec 'touch'. + else + $doit touch "$dsttmp" + fi + } + } && + $doit_exec $cpprog "$src" "$dsttmp") && + + # and set any options; do chmod last to preserve setuid bits. + # + # If any of these fail, we abort the whole thing. If we want to + # ignore errors from any of these, just make sure not to ignore + # errors from the above "$doit $cpprog $src $dsttmp" command. + # + { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && + { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && + { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && + { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && + + # If -C, don't bother to copy if it wouldn't change the file. + if $copy_on_change && + old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && + new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && + set -f && + set X $old && old=:$2:$4:$5:$6 && + set X $new && new=:$2:$4:$5:$6 && + set +f && + test "$old" = "$new" && + $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 + then + rm -f "$dsttmp" + else + # Rename the file to the real destination. + $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || + + # The rename failed, perhaps because mv can't rename something else + # to itself, or perhaps because mv is so ancient that it does not + # support -f. + { + # Now remove or move aside any old file at destination location. + # We try this two ways since rm can't unlink itself on some + # systems and the destination file might be busy for other + # reasons. In this case, the final cleanup might fail but the new + # file should still install successfully. + { + test ! -f "$dst" || + $doit $rmcmd -f "$dst" 2>/dev/null || + { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && + { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } + } || + { echo "$0: cannot unlink or rename $dst" >&2 + (exit 1); exit 1 + } + } && + + # Now rename the file to the real destination. + $doit $mvcmd "$dsttmp" "$dst" + } + fi || exit 1 + + trap '' 0 + fi +done + +# Local variables: +# eval: (add-hook 'before-save-hook 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC0" +# time-stamp-end: "; # UTC" +# End: diff --git a/configure b/configure new file mode 100755 index 0000000..9e94195 --- /dev/null +++ b/configure @@ -0,0 +1,6723 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.71 for kexec-tools 2.0.27. +# +# +# Copyright (C) 1992-1996, 1998-2017, 2020-2021 Free Software Foundation, +# Inc. +# +# +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +as_nop=: +if test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 +then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else $as_nop + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + + +# Reset variables that may have inherited troublesome values from +# the environment. + +# IFS needs to be set, to space, tab, and newline, in precisely that order. +# (If _AS_PATH_WALK were called with IFS unset, it would have the +# side effect of setting IFS to empty, thus disabling word splitting.) +# Quoting is to prevent editors from complaining about space-tab. +as_nl=' +' +export as_nl +IFS=" "" $as_nl" + +PS1='$ ' +PS2='> ' +PS4='+ ' + +# Ensure predictable behavior from utilities with locale-dependent output. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# We cannot yet rely on "unset" to work, but we need these variables +# to be unset--not just set to an empty or harmless value--now, to +# avoid bugs in old shells (e.g. pre-3.0 UWIN ksh). This construct +# also avoids known problems related to "unset" and subshell syntax +# in other old shells (e.g. bash 2.01 and pdksh 5.2.14). +for as_var in BASH_ENV ENV MAIL MAILPATH CDPATH +do eval test \${$as_var+y} \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done + +# Ensure that fds 0, 1, and 2 are open. +if (exec 3>&0) 2>/dev/null; then :; else exec 0</dev/null; fi +if (exec 3>&1) 2>/dev/null; then :; else exec 1>/dev/null; fi +if (exec 3>&2) ; then :; else exec 2>/dev/null; fi + +# The user is always right. +if ${PATH_SEPARATOR+false} :; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + test -r "$as_dir$0" && as_myself=$as_dir$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + printf "%s\n" "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + + +# Use a proper internal environment variable to ensure we don't fall + # into an infinite loop, continuously re-executing ourselves. + if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then + _as_can_reexec=no; export _as_can_reexec; + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2 +exit 255 + fi + # We don't want this to propagate to other subprocesses. + { _as_can_reexec=; unset _as_can_reexec;} +if test "x$CONFIG_SHELL" = x; then + as_bourne_compatible="as_nop=: +if test \${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 +then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which + # is contrary to our usage. Disable this feature. + alias -g '\${1+\"\$@\"}'='\"\$@\"' + setopt NO_GLOB_SUBST +else \$as_nop + case \`(set -o) 2>/dev/null\` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi +" + as_required="as_fn_return () { (exit \$1); } +as_fn_success () { as_fn_return 0; } +as_fn_failure () { as_fn_return 1; } +as_fn_ret_success () { return 0; } +as_fn_ret_failure () { return 1; } + +exitcode=0 +as_fn_success || { exitcode=1; echo as_fn_success failed.; } +as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } +as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } +as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } +if ( set x; as_fn_ret_success y && test x = \"\$1\" ) +then : + +else \$as_nop + exitcode=1; echo positional parameters were not saved. +fi +test x\$exitcode = x0 || exit 1 +blah=\$(echo \$(echo blah)) +test x\"\$blah\" = xblah || exit 1 +test -x / || exit 1" + as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO + as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO + eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && + test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1" + if (eval "$as_required") 2>/dev/null +then : + as_have_required=yes +else $as_nop + as_have_required=no +fi + if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null +then : + +else $as_nop + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +as_found=false +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + as_found=: + case $as_dir in #( + /*) + for as_base in sh bash ksh sh5; do + # Try only shells that exist, to save several forks. + as_shell=$as_dir$as_base + if { test -f "$as_shell" || test -f "$as_shell.exe"; } && + as_run=a "$as_shell" -c "$as_bourne_compatible""$as_required" 2>/dev/null +then : + CONFIG_SHELL=$as_shell as_have_required=yes + if as_run=a "$as_shell" -c "$as_bourne_compatible""$as_suggested" 2>/dev/null +then : + break 2 +fi +fi + done;; + esac + as_found=false +done +IFS=$as_save_IFS +if $as_found +then : + +else $as_nop + if { test -f "$SHELL" || test -f "$SHELL.exe"; } && + as_run=a "$SHELL" -c "$as_bourne_compatible""$as_required" 2>/dev/null +then : + CONFIG_SHELL=$SHELL as_have_required=yes +fi +fi + + + if test "x$CONFIG_SHELL" != x +then : + export CONFIG_SHELL + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2 +exit 255 +fi + + if test x$as_have_required = xno +then : + printf "%s\n" "$0: This script requires a shell more modern than all" + printf "%s\n" "$0: the shells that I found on your system." + if test ${ZSH_VERSION+y} ; then + printf "%s\n" "$0: In particular, zsh $ZSH_VERSION has bugs and should" + printf "%s\n" "$0: be upgraded to zsh 4.3.4 or later." + else + printf "%s\n" "$0: Please tell bug-autoconf@gnu.org about your system, +$0: including any error possibly output before this +$0: message. Then install a modern shell, or manually run +$0: the script under such a shell if you do have one." + fi + exit 1 +fi +fi +fi +SHELL=${CONFIG_SHELL-/bin/sh} +export SHELL +# Unset more variables known to interfere with behavior of common tools. +CLICOLOR_FORCE= GREP_OPTIONS= +unset CLICOLOR_FORCE GREP_OPTIONS + +## --------------------- ## +## M4sh Shell Functions. ## +## --------------------- ## +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset + + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit +# as_fn_nop +# --------- +# Do nothing but, unlike ":", preserve the value of $?. +as_fn_nop () +{ + return $? +} +as_nop=as_fn_nop + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`printf "%s\n" "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +printf "%s\n" X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null +then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else $as_nop + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null +then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else $as_nop + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + +# as_fn_nop +# --------- +# Do nothing but, unlike ":", preserve the value of $?. +as_fn_nop () +{ + return $? +} +as_nop=as_fn_nop + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + printf "%s\n" "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +printf "%s\n" X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + + + as_lineno_1=$LINENO as_lineno_1a=$LINENO + as_lineno_2=$LINENO as_lineno_2a=$LINENO + eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && + test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { + # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= + ' <$as_myself | + sed ' + s/[$]LINENO.*/&-/ + t lineno + b + :lineno + N + :loop + s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ + t loop + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || + { printf "%s\n" "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } + + # If we had to re-execute with $CONFIG_SHELL, we're ensured to have + # already done that, so ensure we don't try to do so again and fall + # in an infinite loop. This has already happened in practice. + _as_can_reexec=no; export _as_can_reexec + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensitive to this). + . "./$as_me.lineno" + # Exit status is that of the last command. + exit +} + + +# Determine whether it's possible to make 'echo' print without a newline. +# These variables are no longer used directly by Autoconf, but are AC_SUBSTed +# for compatibility with existing Makefiles. +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +# For backward compatibility with old third-party macros, we provide +# the shell variables $as_echo and $as_echo_n. New code should use +# AS_ECHO(["message"]) and AS_ECHO_N(["message"]), respectively. +as_echo='printf %s\n' +as_echo_n='printf %s' + + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -pR'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -pR' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -pR' + fi +else + as_ln_s='cp -pR' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +as_test_x='test -x' +as_executable_p=as_fn_executable_p + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +test -n "$DJDIR" || exec 7<&0 </dev/null +exec 6>&1 + +# Name of the host. +# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_clean_files= +ac_config_libobj_dir=. +LIBOBJS= +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= + +# Identity of this package. +PACKAGE_NAME='kexec-tools' +PACKAGE_TARNAME='kexec-tools' +PACKAGE_VERSION='2.0.27' +PACKAGE_STRING='kexec-tools 2.0.27' +PACKAGE_BUGREPORT='' +PACKAGE_URL='' + +# Factoring default headers for most tests. +ac_includes_default="\ +#include <stddef.h> +#ifdef HAVE_STDIO_H +# include <stdio.h> +#endif +#ifdef HAVE_STDLIB_H +# include <stdlib.h> +#endif +#ifdef HAVE_STRING_H +# include <string.h> +#endif +#ifdef HAVE_INTTYPES_H +# include <inttypes.h> +#endif +#ifdef HAVE_STDINT_H +# include <stdint.h> +#endif +#ifdef HAVE_STRINGS_H +# include <strings.h> +#endif +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif +#ifdef HAVE_SYS_STAT_H +# include <sys/stat.h> +#endif +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif" + +ac_header_c_list= +ac_subst_vars='LTLIBOBJS +LIBOBJS +INSTALL +OBJDIR +SUBARCH +ARCH +ASFLAGS +TARGET_CFLAGS +BUILD_CFLAGS +DIRNAME +XARGS +FIND +SED +RPMBUILD +TAR +LN +CP +RM +MKDIR +CHMOD +INSTALL_DATA +INSTALL_SCRIPT +INSTALL_PROGRAM +PURGATORY_EXTRA_CFLAGS +TARGET_LD +TARGET_CC +STRIP +AR +OBJCOPY +AS +LD +CPP +BUILD_CC +target_os +target_vendor +target_cpu +target +host_os +host_vendor +host_cpu +host +build_os +build_vendor +build_cpu +build +OBJEXT +EXEEXT +ac_ct_CC +CPPFLAGS +LDFLAGS +CFLAGS +CC +target_alias +host_alias +build_alias +LIBS +ECHO_T +ECHO_N +ECHO_C +DEFS +mandir +localedir +libdir +psdir +pdfdir +dvidir +htmldir +infodir +docdir +oldincludedir +includedir +runstatedir +localstatedir +sharedstatedir +sysconfdir +datadir +datarootdir +libexecdir +sbindir +bindir +program_transform_name +prefix +exec_prefix +PACKAGE_URL +PACKAGE_BUGREPORT +PACKAGE_STRING +PACKAGE_VERSION +PACKAGE_TARNAME +PACKAGE_NAME +PATH_SEPARATOR +SHELL' +ac_subst_files='' +ac_user_opts=' +enable_option_checking +with_objdir +with_gamecube +with_zlib +with_lzma +with_xen +with_booke +' + ac_precious_vars='build_alias +host_alias +target_alias +CC +CFLAGS +LDFLAGS +LIBS +CPPFLAGS +CPP' + + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +ac_unrecognized_opts= +ac_unrecognized_sep= +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +# (The list follows the same order as the GNU Coding Standards.) +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datarootdir='${prefix}/share' +datadir='${datarootdir}' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +runstatedir='${localstatedir}/run' +includedir='${prefix}/include' +oldincludedir='/usr/include' +docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' +infodir='${datarootdir}/info' +htmldir='${docdir}' +dvidir='${docdir}' +pdfdir='${docdir}' +psdir='${docdir}' +libdir='${exec_prefix}/lib' +localedir='${datarootdir}/locale' +mandir='${datarootdir}/man' + +ac_prev= +ac_dashdash= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval $ac_prev=\$ac_option + ac_prev= + continue + fi + + case $ac_option in + *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; + *=) ac_optarg= ;; + *) ac_optarg=yes ;; + esac + + case $ac_dashdash$ac_option in + --) + ac_dashdash=yes ;; + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=*) + datadir=$ac_optarg ;; + + -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ + | --dataroo | --dataro | --datar) + ac_prev=datarootdir ;; + -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ + | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) + datarootdir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: \`$ac_useropt'" + ac_useropt_orig=$ac_useropt + ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=no ;; + + -docdir | --docdir | --docdi | --doc | --do) + ac_prev=docdir ;; + -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) + docdir=$ac_optarg ;; + + -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) + ac_prev=dvidir ;; + -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) + dvidir=$ac_optarg ;; + + -enable-* | --enable-*) + ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: \`$ac_useropt'" + ac_useropt_orig=$ac_useropt + ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=\$ac_optarg ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) + ac_prev=htmldir ;; + -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ + | --ht=*) + htmldir=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localedir | --localedir | --localedi | --localed | --locale) + ac_prev=localedir ;; + -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) + localedir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst | --locals) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c | -n) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) + ac_prev=pdfdir ;; + -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) + pdfdir=$ac_optarg ;; + + -psdir | --psdir | --psdi | --psd | --ps) + ac_prev=psdir ;; + -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) + psdir=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -runstatedir | --runstatedir | --runstatedi | --runstated \ + | --runstate | --runstat | --runsta | --runst | --runs \ + | --run | --ru | --r) + ac_prev=runstatedir ;; + -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ + | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ + | --run=* | --ru=* | --r=*) + runstatedir=$ac_optarg ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: \`$ac_useropt'" + ac_useropt_orig=$ac_useropt + ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=\$ac_optarg ;; + + -without-* | --without-*) + ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: \`$ac_useropt'" + ac_useropt_orig=$ac_useropt + ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=no ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) as_fn_error $? "unrecognized option: \`$ac_option' +Try \`$0 --help' for more information" + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + case $ac_envvar in #( + '' | [0-9]* | *[!_$as_cr_alnum]* ) + as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; + esac + eval $ac_envvar=\$ac_optarg + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + printf "%s\n" "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + printf "%s\n" "$as_me: WARNING: invalid host type: $ac_option" >&2 + : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + as_fn_error $? "missing argument to $ac_option" +fi + +if test -n "$ac_unrecognized_opts"; then + case $enable_option_checking in + no) ;; + fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; + *) printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; + esac +fi + +# Check all directory arguments for consistency. +for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ + datadir sysconfdir sharedstatedir localstatedir includedir \ + oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ + libdir localedir mandir runstatedir +do + eval ac_val=\$$ac_var + # Remove trailing slashes. + case $ac_val in + */ ) + ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` + eval $ac_var=\$ac_val;; + esac + # Be sure to have absolute directory names. + case $ac_val in + [\\/$]* | ?:[\\/]* ) continue;; + NONE | '' ) case $ac_var in *prefix ) continue;; esac;; + esac + as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +# FIXME: To remove some day. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: To remove some day. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + + +ac_pwd=`pwd` && test -n "$ac_pwd" && +ac_ls_di=`ls -di .` && +ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || + as_fn_error $? "working directory cannot be determined" +test "X$ac_ls_di" = "X$ac_pwd_ls_di" || + as_fn_error $? "pwd does not report name of working directory" + + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then the parent directory. + ac_confdir=`$as_dirname -- "$as_myself" || +$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_myself" : 'X\(//\)[^/]' \| \ + X"$as_myself" : 'X\(//\)$' \| \ + X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || +printf "%s\n" X"$as_myself" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + srcdir=$ac_confdir + if test ! -r "$srcdir/$ac_unique_file"; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r "$srcdir/$ac_unique_file"; then + test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." + as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" +fi +ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" +ac_abs_confdir=`( + cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" + pwd)` +# When building in place, set srcdir=. +if test "$ac_abs_confdir" = "$ac_pwd"; then + srcdir=. +fi +# Remove unnecessary trailing slashes from srcdir. +# Double slashes in file names in object file debugging info +# mess up M-x gdb in Emacs. +case $srcdir in +*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; +esac +for ac_var in $ac_precious_vars; do + eval ac_env_${ac_var}_set=\${${ac_var}+set} + eval ac_env_${ac_var}_value=\$${ac_var} + eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} + eval ac_cv_env_${ac_var}_value=\$${ac_var} +done + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat <<_ACEOF +\`configure' configures kexec-tools 2.0.27 to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking ...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] + --datadir=DIR read-only architecture-independent data [DATAROOTDIR] + --infodir=DIR info documentation [DATAROOTDIR/info] + --localedir=DIR locale-dependent data [DATAROOTDIR/locale] + --mandir=DIR man documentation [DATAROOTDIR/man] + --docdir=DIR documentation root [DATAROOTDIR/doc/kexec-tools] + --htmldir=DIR html documentation [DOCDIR] + --dvidir=DIR dvi documentation [DOCDIR] + --pdfdir=DIR pdf documentation [DOCDIR] + --psdir=DIR ps documentation [DOCDIR] +_ACEOF + + cat <<\_ACEOF + +System types: + --build=BUILD configure for building on BUILD [guessed] + --host=HOST cross-compile to build programs to run on HOST [BUILD] + --target=TARGET configure for building compilers for TARGET [HOST] +_ACEOF +fi + +if test -n "$ac_init_help"; then + case $ac_init_help in + short | recursive ) echo "Configuration of kexec-tools 2.0.27:";; + esac + cat <<\_ACEOF + +Optional Packages: + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --with-objdir=<dir> select directory for object files + --with-gamecube enable gamecube support + --without-zlib disable zlib support + --without-lzma disable lzma support + --without-xen disable extended xen support + --with-booke build for booke + +Some influential environment variables: + CC C compiler command + CFLAGS C compiler flags + LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a + nonstandard directory <lib dir> + LIBS libraries to pass to the linker, e.g. -l<library> + CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if + you have headers in a nonstandard directory <include dir> + CPP C preprocessor + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +Report bugs to the package provider. +_ACEOF +ac_status=$? +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d "$ac_dir" || + { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || + continue + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`printf "%s\n" "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + cd "$ac_dir" || { ac_status=$?; continue; } + # Check for configure.gnu first; this name is used for a wrapper for + # Metaconfig's "Configure" on case-insensitive file systems. + if test -f "$ac_srcdir/configure.gnu"; then + echo && + $SHELL "$ac_srcdir/configure.gnu" --help=recursive + elif test -f "$ac_srcdir/configure"; then + echo && + $SHELL "$ac_srcdir/configure" --help=recursive + else + printf "%s\n" "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi || ac_status=$? + cd "$ac_pwd" || { ac_status=$?; break; } + done +fi + +test -n "$ac_init_help" && exit $ac_status +if $ac_init_version; then + cat <<\_ACEOF +kexec-tools configure 2.0.27 +generated by GNU Autoconf 2.71 + +Copyright (C) 2021 Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +_ACEOF + exit +fi + +## ------------------------ ## +## Autoconf initialization. ## +## ------------------------ ## + +# ac_fn_c_try_compile LINENO +# -------------------------- +# Try to compile conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext conftest.beam + if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +printf "%s\n" "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext +then : + ac_retval=0 +else $as_nop + printf "%s\n" "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_compile + +# ac_fn_c_try_cpp LINENO +# ---------------------- +# Try to preprocess conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_cpp () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +printf "%s\n" "$ac_try_echo"; } >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } > conftest.i && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + } +then : + ac_retval=0 +else $as_nop + printf "%s\n" "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_cpp + +# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES +# ------------------------------------------------------- +# Tests whether HEADER exists and can be compiled using the include files in +# INCLUDES, setting the cache variable VAR accordingly. +ac_fn_c_check_header_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +printf %s "checking for $2... " >&6; } +if eval test \${$3+y} +then : + printf %s "(cached) " >&6 +else $as_nop + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + eval "$3=yes" +else $as_nop + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +fi +eval ac_res=\$$3 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +printf "%s\n" "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_header_compile + +# ac_fn_c_try_link LINENO +# ----------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_link () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext conftest.beam conftest$ac_exeext + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +printf "%s\n" "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + test -x conftest$ac_exeext + } +then : + ac_retval=0 +else $as_nop + printf "%s\n" "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information + # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would + # interfere with the next link command; also delete a directory that is + # left behind by Apple's compiler. We do this before executing the actions. + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_link +ac_configure_args_raw= +for ac_arg +do + case $ac_arg in + *\'*) + ac_arg=`printf "%s\n" "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + as_fn_append ac_configure_args_raw " '$ac_arg'" +done + +case $ac_configure_args_raw in + *$as_nl*) + ac_safe_unquote= ;; + *) + ac_unsafe_z='|&;<>()$`\\"*?[ '' ' # This string ends in space, tab. + ac_unsafe_a="$ac_unsafe_z#~" + ac_safe_unquote="s/ '\\([^$ac_unsafe_a][^$ac_unsafe_z]*\\)'/ \\1/g" + ac_configure_args_raw=` printf "%s\n" "$ac_configure_args_raw" | sed "$ac_safe_unquote"`;; +esac + +cat >config.log <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by kexec-tools $as_me 2.0.27, which was +generated by GNU Autoconf 2.71. Invocation command line was + + $ $0$ac_configure_args_raw + +_ACEOF +exec 5>>config.log +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + printf "%s\n" "PATH: $as_dir" + done +IFS=$as_save_IFS + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Strip out --silent because we don't want to record it for future runs. +# Also quote any args containing shell meta-characters. +# Make two passes to allow for proper duplicate-argument suppression. +ac_configure_args= +ac_configure_args0= +ac_configure_args1= +ac_must_keep_next=false +for ac_pass in 1 2 +do + for ac_arg + do + case $ac_arg in + -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *\'*) + ac_arg=`printf "%s\n" "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; + 2) + as_fn_append ac_configure_args1 " '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else + case $ac_arg in + *=* | --config-cache | -C | -disable-* | --disable-* \ + | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ + | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ + | -with-* | --with-* | -without-* | --without-* | --x) + case "$ac_configure_args0 " in + "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; + esac + ;; + -* ) ac_must_keep_next=true ;; + esac + fi + as_fn_append ac_configure_args " '$ac_arg'" + ;; + esac + done +done +{ ac_configure_args0=; unset ac_configure_args0;} +{ ac_configure_args1=; unset ac_configure_args1;} + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Use '\'' to represent an apostrophe within the trap. +# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. +trap 'exit_status=$? + # Sanitize IFS. + IFS=" "" $as_nl" + # Save into config.log some information that might help in debugging. + { + echo + + printf "%s\n" "## ---------------- ## +## Cache variables. ## +## ---------------- ##" + echo + # The following way of writing the cache mishandles newlines in values, +( + for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +printf "%s\n" "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + (set) 2>&1 | + case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + sed -n \ + "s/'\''/'\''\\\\'\'''\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" + ;; #( + *) + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) + echo + + printf "%s\n" "## ----------------- ## +## Output variables. ## +## ----------------- ##" + echo + for ac_var in $ac_subst_vars + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`printf "%s\n" "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + printf "%s\n" "$ac_var='\''$ac_val'\''" + done | sort + echo + + if test -n "$ac_subst_files"; then + printf "%s\n" "## ------------------- ## +## File substitutions. ## +## ------------------- ##" + echo + for ac_var in $ac_subst_files + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`printf "%s\n" "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + printf "%s\n" "$ac_var='\''$ac_val'\''" + done | sort + echo + fi + + if test -s confdefs.h; then + printf "%s\n" "## ----------- ## +## confdefs.h. ## +## ----------- ##" + echo + cat confdefs.h + echo + fi + test "$ac_signal" != 0 && + printf "%s\n" "$as_me: caught signal $ac_signal" + printf "%s\n" "$as_me: exit $exit_status" + } >&5 + rm -f core *.core core.conftest.* && + rm -f -r conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status +' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -f -r conftest* confdefs.h + +printf "%s\n" "/* confdefs.h */" > confdefs.h + +# Predefined preprocessor variables. + +printf "%s\n" "#define PACKAGE_NAME \"$PACKAGE_NAME\"" >>confdefs.h + +printf "%s\n" "#define PACKAGE_TARNAME \"$PACKAGE_TARNAME\"" >>confdefs.h + +printf "%s\n" "#define PACKAGE_VERSION \"$PACKAGE_VERSION\"" >>confdefs.h + +printf "%s\n" "#define PACKAGE_STRING \"$PACKAGE_STRING\"" >>confdefs.h + +printf "%s\n" "#define PACKAGE_BUGREPORT \"$PACKAGE_BUGREPORT\"" >>confdefs.h + +printf "%s\n" "#define PACKAGE_URL \"$PACKAGE_URL\"" >>confdefs.h + + +# Let the site file select an alternate cache file if it wants to. +# Prefer an explicitly selected file to automatically selected ones. +if test -n "$CONFIG_SITE"; then + ac_site_files="$CONFIG_SITE" +elif test "x$prefix" != xNONE; then + ac_site_files="$prefix/share/config.site $prefix/etc/config.site" +else + ac_site_files="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" +fi + +for ac_site_file in $ac_site_files +do + case $ac_site_file in #( + */*) : + ;; #( + *) : + ac_site_file=./$ac_site_file ;; +esac + if test -f "$ac_site_file" && test -r "$ac_site_file"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 +printf "%s\n" "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" \ + || { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "failed to load site script $ac_site_file +See \`config.log' for more details" "$LINENO" 5; } + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special files + # actually), so we avoid doing that. DJGPP emulates it as a regular file. + if test /dev/null != "$cache_file" && test -f "$cache_file"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 +printf "%s\n" "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . "$cache_file";; + *) . "./$cache_file";; + esac + fi +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 +printf "%s\n" "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Test code for whether the C compiler supports C89 (global declarations) +ac_c_conftest_c89_globals=' +/* Does the compiler advertise C89 conformance? + Do not test the value of __STDC__, because some compilers set it to 0 + while being otherwise adequately conformant. */ +#if !defined __STDC__ +# error "Compiler does not advertise C89 conformance" +#endif + +#include <stddef.h> +#include <stdarg.h> +struct stat; +/* Most of the following tests are stolen from RCS 5.7 src/conf.sh. */ +struct buf { int x; }; +struct buf * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not \xHH hex character constants. + These do not provoke an error unfortunately, instead are silently treated + as an "x". The following induces an error, until -std is added to get + proper ANSI mode. Curiously \x00 != x always comes out true, for an + array size at least. It is necessary to write \x00 == 0 to get something + that is true only with -std. */ +int osf4_cc_array ['\''\x00'\'' == 0 ? 1 : -1]; + +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) '\''x'\'' +int xlc6_cc_array[FOO(a) == '\''x'\'' ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, int *(*)(struct buf *, struct stat *, int), + int, int);' + +# Test code for whether the C compiler supports C89 (body of main). +ac_c_conftest_c89_main=' +ok |= (argc == 0 || f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]); +' + +# Test code for whether the C compiler supports C99 (global declarations) +ac_c_conftest_c99_globals=' +// Does the compiler advertise C99 conformance? +#if !defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L +# error "Compiler does not advertise C99 conformance" +#endif + +#include <stdbool.h> +extern int puts (const char *); +extern int printf (const char *, ...); +extern int dprintf (int, const char *, ...); +extern void *malloc (size_t); + +// Check varargs macros. These examples are taken from C99 6.10.3.5. +// dprintf is used instead of fprintf to avoid needing to declare +// FILE and stderr. +#define debug(...) dprintf (2, __VA_ARGS__) +#define showlist(...) puts (#__VA_ARGS__) +#define report(test,...) ((test) ? puts (#test) : printf (__VA_ARGS__)) +static void +test_varargs_macros (void) +{ + int x = 1234; + int y = 5678; + debug ("Flag"); + debug ("X = %d\n", x); + showlist (The first, second, and third items.); + report (x>y, "x is %d but y is %d", x, y); +} + +// Check long long types. +#define BIG64 18446744073709551615ull +#define BIG32 4294967295ul +#define BIG_OK (BIG64 / BIG32 == 4294967297ull && BIG64 % BIG32 == 0) +#if !BIG_OK + #error "your preprocessor is broken" +#endif +#if BIG_OK +#else + #error "your preprocessor is broken" +#endif +static long long int bignum = -9223372036854775807LL; +static unsigned long long int ubignum = BIG64; + +struct incomplete_array +{ + int datasize; + double data[]; +}; + +struct named_init { + int number; + const wchar_t *name; + double average; +}; + +typedef const char *ccp; + +static inline int +test_restrict (ccp restrict text) +{ + // See if C++-style comments work. + // Iterate through items via the restricted pointer. + // Also check for declarations in for loops. + for (unsigned int i = 0; *(text+i) != '\''\0'\''; ++i) + continue; + return 0; +} + +// Check varargs and va_copy. +static bool +test_varargs (const char *format, ...) +{ + va_list args; + va_start (args, format); + va_list args_copy; + va_copy (args_copy, args); + + const char *str = ""; + int number = 0; + float fnumber = 0; + + while (*format) + { + switch (*format++) + { + case '\''s'\'': // string + str = va_arg (args_copy, const char *); + break; + case '\''d'\'': // int + number = va_arg (args_copy, int); + break; + case '\''f'\'': // float + fnumber = va_arg (args_copy, double); + break; + default: + break; + } + } + va_end (args_copy); + va_end (args); + + return *str && number && fnumber; +} +' + +# Test code for whether the C compiler supports C99 (body of main). +ac_c_conftest_c99_main=' + // Check bool. + _Bool success = false; + success |= (argc != 0); + + // Check restrict. + if (test_restrict ("String literal") == 0) + success = true; + char *restrict newvar = "Another string"; + + // Check varargs. + success &= test_varargs ("s, d'\'' f .", "string", 65, 34.234); + test_varargs_macros (); + + // Check flexible array members. + struct incomplete_array *ia = + malloc (sizeof (struct incomplete_array) + (sizeof (double) * 10)); + ia->datasize = 10; + for (int i = 0; i < ia->datasize; ++i) + ia->data[i] = i * 1.234; + + // Check named initializers. + struct named_init ni = { + .number = 34, + .name = L"Test wide string", + .average = 543.34343, + }; + + ni.number = 58; + + int dynamic_array[ni.number]; + dynamic_array[0] = argv[0][0]; + dynamic_array[ni.number - 1] = 543; + + // work around unused variable warnings + ok |= (!success || bignum == 0LL || ubignum == 0uLL || newvar[0] == '\''x'\'' + || dynamic_array[ni.number - 1] != 543); +' + +# Test code for whether the C compiler supports C11 (global declarations) +ac_c_conftest_c11_globals=' +// Does the compiler advertise C11 conformance? +#if !defined __STDC_VERSION__ || __STDC_VERSION__ < 201112L +# error "Compiler does not advertise C11 conformance" +#endif + +// Check _Alignas. +char _Alignas (double) aligned_as_double; +char _Alignas (0) no_special_alignment; +extern char aligned_as_int; +char _Alignas (0) _Alignas (int) aligned_as_int; + +// Check _Alignof. +enum +{ + int_alignment = _Alignof (int), + int_array_alignment = _Alignof (int[100]), + char_alignment = _Alignof (char) +}; +_Static_assert (0 < -_Alignof (int), "_Alignof is signed"); + +// Check _Noreturn. +int _Noreturn does_not_return (void) { for (;;) continue; } + +// Check _Static_assert. +struct test_static_assert +{ + int x; + _Static_assert (sizeof (int) <= sizeof (long int), + "_Static_assert does not work in struct"); + long int y; +}; + +// Check UTF-8 literals. +#define u8 syntax error! +char const utf8_literal[] = u8"happens to be ASCII" "another string"; + +// Check duplicate typedefs. +typedef long *long_ptr; +typedef long int *long_ptr; +typedef long_ptr long_ptr; + +// Anonymous structures and unions -- taken from C11 6.7.2.1 Example 1. +struct anonymous +{ + union { + struct { int i; int j; }; + struct { int k; long int l; } w; + }; + int m; +} v1; +' + +# Test code for whether the C compiler supports C11 (body of main). +ac_c_conftest_c11_main=' + _Static_assert ((offsetof (struct anonymous, i) + == offsetof (struct anonymous, w.k)), + "Anonymous union alignment botch"); + v1.i = 2; + v1.w.k = 5; + ok |= v1.i != 5; +' + +# Test code for whether the C compiler supports C11 (complete). +ac_c_conftest_c11_program="${ac_c_conftest_c89_globals} +${ac_c_conftest_c99_globals} +${ac_c_conftest_c11_globals} + +int +main (int argc, char **argv) +{ + int ok = 0; + ${ac_c_conftest_c89_main} + ${ac_c_conftest_c99_main} + ${ac_c_conftest_c11_main} + return ok; +} +" + +# Test code for whether the C compiler supports C99 (complete). +ac_c_conftest_c99_program="${ac_c_conftest_c89_globals} +${ac_c_conftest_c99_globals} + +int +main (int argc, char **argv) +{ + int ok = 0; + ${ac_c_conftest_c89_main} + ${ac_c_conftest_c99_main} + return ok; +} +" + +# Test code for whether the C compiler supports C89 (complete). +ac_c_conftest_c89_program="${ac_c_conftest_c89_globals} + +int +main (int argc, char **argv) +{ + int ok = 0; + ${ac_c_conftest_c89_main} + return ok; +} +" + +as_fn_append ac_header_c_list " stdio.h stdio_h HAVE_STDIO_H" +as_fn_append ac_header_c_list " stdlib.h stdlib_h HAVE_STDLIB_H" +as_fn_append ac_header_c_list " string.h string_h HAVE_STRING_H" +as_fn_append ac_header_c_list " inttypes.h inttypes_h HAVE_INTTYPES_H" +as_fn_append ac_header_c_list " stdint.h stdint_h HAVE_STDINT_H" +as_fn_append ac_header_c_list " strings.h strings_h HAVE_STRINGS_H" +as_fn_append ac_header_c_list " sys/stat.h sys_stat_h HAVE_SYS_STAT_H" +as_fn_append ac_header_c_list " sys/types.h sys_types_h HAVE_SYS_TYPES_H" +as_fn_append ac_header_c_list " unistd.h unistd_h HAVE_UNISTD_H" + +# Auxiliary files required by this configure script. +ac_aux_files="install-sh config.guess config.sub" + +# Locations in which to look for auxiliary files. +ac_aux_dir_candidates="${srcdir}/./config" + +# Search for a directory containing all of the required auxiliary files, +# $ac_aux_files, from the $PATH-style list $ac_aux_dir_candidates. +# If we don't find one directory that contains all the files we need, +# we report the set of missing files from the *first* directory in +# $ac_aux_dir_candidates and give up. +ac_missing_aux_files="" +ac_first_candidate=: +printf "%s\n" "$as_me:${as_lineno-$LINENO}: looking for aux files: $ac_aux_files" >&5 +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +as_found=false +for as_dir in $ac_aux_dir_candidates +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + as_found=: + + printf "%s\n" "$as_me:${as_lineno-$LINENO}: trying $as_dir" >&5 + ac_aux_dir_found=yes + ac_install_sh= + for ac_aux in $ac_aux_files + do + # As a special case, if "install-sh" is required, that requirement + # can be satisfied by any of "install-sh", "install.sh", or "shtool", + # and $ac_install_sh is set appropriately for whichever one is found. + if test x"$ac_aux" = x"install-sh" + then + if test -f "${as_dir}install-sh"; then + printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}install-sh found" >&5 + ac_install_sh="${as_dir}install-sh -c" + elif test -f "${as_dir}install.sh"; then + printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}install.sh found" >&5 + ac_install_sh="${as_dir}install.sh -c" + elif test -f "${as_dir}shtool"; then + printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}shtool found" >&5 + ac_install_sh="${as_dir}shtool install -c" + else + ac_aux_dir_found=no + if $ac_first_candidate; then + ac_missing_aux_files="${ac_missing_aux_files} install-sh" + else + break + fi + fi + else + if test -f "${as_dir}${ac_aux}"; then + printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}${ac_aux} found" >&5 + else + ac_aux_dir_found=no + if $ac_first_candidate; then + ac_missing_aux_files="${ac_missing_aux_files} ${ac_aux}" + else + break + fi + fi + fi + done + if test "$ac_aux_dir_found" = yes; then + ac_aux_dir="$as_dir" + break + fi + ac_first_candidate=false + + as_found=false +done +IFS=$as_save_IFS +if $as_found +then : + +else $as_nop + as_fn_error $? "cannot find required auxiliary files:$ac_missing_aux_files" "$LINENO" 5 +fi + + +# These three variables are undocumented and unsupported, +# and are intended to be withdrawn in a future Autoconf release. +# They can cause serious problems if a builder's source tree is in a directory +# whose full name contains unusual characters. +if test -f "${ac_aux_dir}config.guess"; then + ac_config_guess="$SHELL ${ac_aux_dir}config.guess" +fi +if test -f "${ac_aux_dir}config.sub"; then + ac_config_sub="$SHELL ${ac_aux_dir}config.sub" +fi +if test -f "$ac_aux_dir/configure"; then + ac_configure="$SHELL ${ac_aux_dir}configure" +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in $ac_precious_vars; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val=\$ac_cv_env_${ac_var}_value + eval ac_new_val=\$ac_env_${ac_var}_value + case $ac_old_set,$ac_new_set in + set,) + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +printf "%s\n" "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 +printf "%s\n" "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + # differences in whitespace do not lead to failure. + ac_old_val_w=`echo x $ac_old_val` + ac_new_val_w=`echo x $ac_new_val` + if test "$ac_old_val_w" != "$ac_new_val_w"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 +printf "%s\n" "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + ac_cache_corrupted=: + else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 +printf "%s\n" "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} + eval $ac_var=\$ac_old_val + fi + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 +printf "%s\n" "$as_me: former value: \`$ac_old_val'" >&2;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 +printf "%s\n" "$as_me: current value: \`$ac_new_val'" >&2;} + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *\'*) ac_arg=$ac_var=`printf "%s\n" "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) as_fn_append ac_configure_args " '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 +printf "%s\n" "$as_me: error: changes in the environment can compromise the build" >&2;} + as_fn_error $? "run \`${MAKE-make} distclean' and/or \`rm $cache_file' + and start over" "$LINENO" 5 +fi +## -------------------- ## +## Main body of script. ## +## -------------------- ## + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + +ac_config_headers="$ac_config_headers include/config.h" + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + + + + + + + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +printf "%s\n" "$CC" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_ac_ct_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="gcc" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +printf "%s\n" "$ac_ct_CC" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +printf "%s\n" "$CC" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + + fi +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + if test "$as_dir$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +printf "%s\n" "$CC" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl.exe + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +printf "%s\n" "$CC" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl.exe +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_ac_ct_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="$ac_prog" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +printf "%s\n" "$ac_ct_CC" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + + test -n "$ac_ct_CC" && break +done + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +fi + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}clang", so it can be a program name with args. +set dummy ${ac_tool_prefix}clang; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}clang" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +printf "%s\n" "$CC" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "clang", so it can be a program name with args. +set dummy clang; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_ac_ct_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="clang" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +printf "%s\n" "$ac_ct_CC" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" +fi + +fi + + +test -z "$CC" && { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "no acceptable C compiler found in \$PATH +See \`config.log' for more details" "$LINENO" 5; } + +# Provide some information about the compiler. +printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion -version; do + { { ac_try="$ac_compiler $ac_option >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +printf "%s\n" "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + fi + rm -f conftest.er1 conftest.err + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done + +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main (void) +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 +printf %s "checking whether the C compiler works... " >&6; } +ac_link_default=`printf "%s\n" "$ac_link" | sed 's/ -o *conftest[^ ]*//'` + +# The possible output files: +ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" + +ac_rmfiles= +for ac_file in $ac_files +do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + * ) ac_rmfiles="$ac_rmfiles $ac_file";; + esac +done +rm -f $ac_rmfiles + +if { { ac_try="$ac_link_default" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +printf "%s\n" "$ac_try_echo"; } >&5 + (eval "$ac_link_default") 2>&5 + ac_status=$? + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +then : + # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. +# So ignore a value of `no', otherwise this would lead to `EXEEXT = no' +# in a Makefile. We should not override ac_cv_exeext if it was cached, +# so that the user can short-circuit this test for compilers unknown to +# Autoconf. +for ac_file in $ac_files '' +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + if test ${ac_cv_exeext+y} && test "$ac_cv_exeext" != no; + then :; else + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + fi + # We set ac_cv_exeext here because the later test for it is not + # safe: cross compilers may not add the suffix if given an `-o' + # argument, so we may need to know it at that point already. + # Even if this section looks crufty: it has the advantage of + # actually working. + break;; + * ) + break;; + esac +done +test "$ac_cv_exeext" = no && ac_cv_exeext= + +else $as_nop + ac_file='' +fi +if test -z "$ac_file" +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +printf "%s\n" "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "C compiler cannot create executables +See \`config.log' for more details" "$LINENO" 5; } +else $as_nop + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +printf "%s\n" "yes" >&6; } +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 +printf %s "checking for C compiler default output file name... " >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 +printf "%s\n" "$ac_file" >&6; } +ac_exeext=$ac_cv_exeext + +rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 +printf %s "checking for suffix of executables... " >&6; } +if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +printf "%s\n" "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +then : + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + break;; + * ) break;; + esac +done +else $as_nop + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest conftest$ac_cv_exeext +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 +printf "%s\n" "$ac_cv_exeext" >&6; } + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <stdio.h> +int +main (void) +{ +FILE *f = fopen ("conftest.out", "w"); + return ferror (f) || fclose (f) != 0; + + ; + return 0; +} +_ACEOF +ac_clean_files="$ac_clean_files conftest.out" +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 +printf %s "checking whether we are cross compiling... " >&6; } +if test "$cross_compiling" != yes; then + { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +printf "%s\n" "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + if { ac_try='./conftest$ac_cv_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +printf "%s\n" "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details" "$LINENO" 5; } + fi + fi +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 +printf "%s\n" "$cross_compiling" >&6; } + +rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out +ac_clean_files=$ac_clean_files_save +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 +printf %s "checking for suffix of object files... " >&6; } +if test ${ac_cv_objext+y} +then : + printf %s "(cached) " >&6 +else $as_nop + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main (void) +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +printf "%s\n" "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>&5 + ac_status=$? + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +then : + for ac_file in conftest.o conftest.obj conftest.*; do + test -f "$ac_file" || continue; + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else $as_nop + printf "%s\n" "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of object files: cannot compile +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 +printf "%s\n" "$ac_cv_objext" >&6; } +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports GNU C" >&5 +printf %s "checking whether the compiler supports GNU C... " >&6; } +if test ${ac_cv_c_compiler_gnu+y} +then : + printf %s "(cached) " >&6 +else $as_nop + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main (void) +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + ac_compiler_gnu=yes +else $as_nop + ac_compiler_gnu=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 +printf "%s\n" "$ac_cv_c_compiler_gnu" >&6; } +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +if test $ac_compiler_gnu = yes; then + GCC=yes +else + GCC= +fi +ac_test_CFLAGS=${CFLAGS+y} +ac_save_CFLAGS=$CFLAGS +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 +printf %s "checking whether $CC accepts -g... " >&6; } +if test ${ac_cv_prog_cc_g+y} +then : + printf %s "(cached) " >&6 +else $as_nop + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main (void) +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + ac_cv_prog_cc_g=yes +else $as_nop + CFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main (void) +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + +else $as_nop + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main (void) +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + ac_cv_prog_cc_g=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 +printf "%s\n" "$ac_cv_prog_cc_g" >&6; } +if test $ac_test_CFLAGS; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +ac_prog_cc_stdc=no +if test x$ac_prog_cc_stdc = xno +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C11 features" >&5 +printf %s "checking for $CC option to enable C11 features... " >&6; } +if test ${ac_cv_prog_cc_c11+y} +then : + printf %s "(cached) " >&6 +else $as_nop + ac_cv_prog_cc_c11=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_c_conftest_c11_program +_ACEOF +for ac_arg in '' -std=gnu11 +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO" +then : + ac_cv_prog_cc_c11=$ac_arg +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam + test "x$ac_cv_prog_cc_c11" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC +fi + +if test "x$ac_cv_prog_cc_c11" = xno +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +printf "%s\n" "unsupported" >&6; } +else $as_nop + if test "x$ac_cv_prog_cc_c11" = x +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +printf "%s\n" "none needed" >&6; } +else $as_nop + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c11" >&5 +printf "%s\n" "$ac_cv_prog_cc_c11" >&6; } + CC="$CC $ac_cv_prog_cc_c11" +fi + ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c11 + ac_prog_cc_stdc=c11 +fi +fi +if test x$ac_prog_cc_stdc = xno +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C99 features" >&5 +printf %s "checking for $CC option to enable C99 features... " >&6; } +if test ${ac_cv_prog_cc_c99+y} +then : + printf %s "(cached) " >&6 +else $as_nop + ac_cv_prog_cc_c99=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_c_conftest_c99_program +_ACEOF +for ac_arg in '' -std=gnu99 -std=c99 -c99 -qlanglvl=extc1x -qlanglvl=extc99 -AC99 -D_STDC_C99= +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO" +then : + ac_cv_prog_cc_c99=$ac_arg +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam + test "x$ac_cv_prog_cc_c99" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC +fi + +if test "x$ac_cv_prog_cc_c99" = xno +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +printf "%s\n" "unsupported" >&6; } +else $as_nop + if test "x$ac_cv_prog_cc_c99" = x +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +printf "%s\n" "none needed" >&6; } +else $as_nop + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5 +printf "%s\n" "$ac_cv_prog_cc_c99" >&6; } + CC="$CC $ac_cv_prog_cc_c99" +fi + ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c99 + ac_prog_cc_stdc=c99 +fi +fi +if test x$ac_prog_cc_stdc = xno +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C89 features" >&5 +printf %s "checking for $CC option to enable C89 features... " >&6; } +if test ${ac_cv_prog_cc_c89+y} +then : + printf %s "(cached) " >&6 +else $as_nop + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_c_conftest_c89_program +_ACEOF +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO" +then : + ac_cv_prog_cc_c89=$ac_arg +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam + test "x$ac_cv_prog_cc_c89" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC +fi + +if test "x$ac_cv_prog_cc_c89" = xno +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +printf "%s\n" "unsupported" >&6; } +else $as_nop + if test "x$ac_cv_prog_cc_c89" = x +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +printf "%s\n" "none needed" >&6; } +else $as_nop + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 +printf "%s\n" "$ac_cv_prog_cc_c89" >&6; } + CC="$CC $ac_cv_prog_cc_c89" +fi + ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c89 + ac_prog_cc_stdc=c89 +fi +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + + + + + # Make sure we can run config.sub. +$SHELL "${ac_aux_dir}config.sub" sun4 >/dev/null 2>&1 || + as_fn_error $? "cannot run $SHELL ${ac_aux_dir}config.sub" "$LINENO" 5 + +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 +printf %s "checking build system type... " >&6; } +if test ${ac_cv_build+y} +then : + printf %s "(cached) " >&6 +else $as_nop + ac_build_alias=$build_alias +test "x$ac_build_alias" = x && + ac_build_alias=`$SHELL "${ac_aux_dir}config.guess"` +test "x$ac_build_alias" = x && + as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 +ac_cv_build=`$SHELL "${ac_aux_dir}config.sub" $ac_build_alias` || + as_fn_error $? "$SHELL ${ac_aux_dir}config.sub $ac_build_alias failed" "$LINENO" 5 + +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 +printf "%s\n" "$ac_cv_build" >&6; } +case $ac_cv_build in +*-*-*) ;; +*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;; +esac +build=$ac_cv_build +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_build +shift +build_cpu=$1 +build_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +build_os=$* +IFS=$ac_save_IFS +case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac + + +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 +printf %s "checking host system type... " >&6; } +if test ${ac_cv_host+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test "x$host_alias" = x; then + ac_cv_host=$ac_cv_build +else + ac_cv_host=`$SHELL "${ac_aux_dir}config.sub" $host_alias` || + as_fn_error $? "$SHELL ${ac_aux_dir}config.sub $host_alias failed" "$LINENO" 5 +fi + +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 +printf "%s\n" "$ac_cv_host" >&6; } +case $ac_cv_host in +*-*-*) ;; +*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;; +esac +host=$ac_cv_host +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_host +shift +host_cpu=$1 +host_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +host_os=$* +IFS=$ac_save_IFS +case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac + + +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking target system type" >&5 +printf %s "checking target system type... " >&6; } +if test ${ac_cv_target+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test "x$target_alias" = x; then + ac_cv_target=$ac_cv_host +else + ac_cv_target=`$SHELL "${ac_aux_dir}config.sub" $target_alias` || + as_fn_error $? "$SHELL ${ac_aux_dir}config.sub $target_alias failed" "$LINENO" 5 +fi + +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_target" >&5 +printf "%s\n" "$ac_cv_target" >&6; } +case $ac_cv_target in +*-*-*) ;; +*) as_fn_error $? "invalid value of canonical target" "$LINENO" 5;; +esac +target=$ac_cv_target +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_target +shift +target_cpu=$1 +target_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +target_os=$* +IFS=$ac_save_IFS +case $target_os in *\ *) target_os=`echo "$target_os" | sed 's/ /-/g'`;; esac + + +# The aliases save the names the user supplied, while $host etc. +# will get canonicalized. +test -n "$target_alias" && + test "$program_prefix$program_suffix$program_transform_name" = \ + NONENONEs,x,x, && + program_prefix=${target_alias}- + +case $target_cpu in + i?86 ) + ARCH="i386" + ;; + powerpc ) + ARCH="ppc" + SUBARCH="BE" + ;; + powerpc64 ) + ARCH="ppc64" + SUBARCH="BE" + ;; + powerpc64le ) + ARCH="ppc64" + SUBARCH="LE" + ;; + aarch64* ) + ARCH="arm64" + ;; + arm* ) + ARCH="arm" + ;; + s390x|s390 ) + ARCH="s390" + ;; + sh4|sh4a|sh3|sh ) + ARCH="sh" + ;; + mips* ) + ARCH="mips" + ;; + cris|crisv32 ) + ARCH="cris" + ;; + ia64|x86_64|alpha|m68k ) + ARCH="$target_cpu" + ;; + hppa*) + ARCH="hppa" + ;; + loongarch*) + ARCH="loongarch" + ;; + * ) + as_fn_error $? "unsupported architecture $target_cpu" "$LINENO" 5 + ;; +esac + +if test "$ARCH" = "x86_64" ; then + case $target in + *x32 ) + SUBARCH="x32" + ;; + * ) + SUBARCH="" + ;; + esac +fi + + +OBJDIR=`pwd`/objdir +if test "${host_alias}" ; then + OBJDIR="$OBJDIR-${host_alias}" +fi + +if test "x$BUILD_CFLAGS" = "x" ; then + BUILD_CFLAGS='-O2 -Wall' +fi + +if test "x$TARGET_CFLAGS" = "x" ; then + TARGET_CFLAGS='-O2 -Wall' +fi + + +# Check whether --with-objdir was given. +if test ${with_objdir+y} +then : + withval=$with_objdir; OBJDIR="$withval" +else $as_nop + OBJDIR="$OBJDIR" +fi + + + +# Check whether --with-gamecube was given. +if test ${with_gamecube+y} +then : + withval=$with_gamecube; +printf "%s\n" "#define WITH_GAMECUBE 1" >>confdefs.h + +fi + + + +# Check whether --with-zlib was given. +if test ${with_zlib+y} +then : + withval=$with_zlib; with_zlib="$withval" +else $as_nop + with_zlib=yes +fi + + + +# Check whether --with-lzma was given. +if test ${with_lzma+y} +then : + withval=$with_lzma; with_lzma="$withval" +else $as_nop + with_lzma=yes +fi + + + +# Check whether --with-xen was given. +if test ${with_xen+y} +then : + withval=$with_xen; with_xen="$withval" +else $as_nop + with_xen=yes +fi + + + +# Check whether --with-booke was given. +if test ${with_booke+y} +then : + withval=$with_booke; +printf "%s\n" "#define CONFIG_BOOKE 1" >>confdefs.h + +fi + + +if test "${build}" != "${host}" ; then + for ac_prog in ${build_alias}-gcc ${build}-gcc gcc +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_BUILD_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$BUILD_CC"; then + ac_cv_prog_BUILD_CC="$BUILD_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_BUILD_CC="$ac_prog" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +BUILD_CC=$ac_cv_prog_BUILD_CC +if test -n "$BUILD_CC"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $BUILD_CC" >&5 +printf "%s\n" "$BUILD_CC" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + + test -n "$BUILD_CC" && break +done + +else + BUILD_CC="$CC" +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 +printf %s "checking how to run the C preprocessor... " >&6; } +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if test ${ac_cv_prog_CPP+y} +then : + printf %s "(cached) " >&6 +else $as_nop + # Double quotes because $CC needs to be expanded + for CPP in "$CC -E" "$CC -E -traditional-cpp" cpp /lib/cpp + do + ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <limits.h> + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO" +then : + +else $as_nop + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <ac_nonexistent.h> +_ACEOF +if ac_fn_c_try_cpp "$LINENO" +then : + # Broken: success on invalid input. +continue +else $as_nop + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok +then : + break +fi + + done + ac_cv_prog_CPP=$CPP + +fi + CPP=$ac_cv_prog_CPP +else + ac_cv_prog_CPP=$CPP +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 +printf "%s\n" "$CPP" >&6; } +ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <limits.h> + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO" +then : + +else $as_nop + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <ac_nonexistent.h> +_ACEOF +if ac_fn_c_try_cpp "$LINENO" +then : + # Broken: success on invalid input. +continue +else $as_nop + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok +then : + +else $as_nop + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details" "$LINENO" 5; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}ld", so it can be a program name with args. +set dummy ${ac_tool_prefix}ld; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_LD+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$LD"; then + ac_cv_prog_LD="$LD" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_LD="${ac_tool_prefix}ld" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +LD=$ac_cv_prog_LD +if test -n "$LD"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $LD" >&5 +printf "%s\n" "$LD" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_LD"; then + ac_ct_LD=$LD + # Extract the first word of "ld", so it can be a program name with args. +set dummy ld; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_ac_ct_LD+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$ac_ct_LD"; then + ac_cv_prog_ac_ct_LD="$ac_ct_LD" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_LD="ld" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_LD=$ac_cv_prog_ac_ct_LD +if test -n "$ac_ct_LD"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_LD" >&5 +printf "%s\n" "$ac_ct_LD" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + if test "x$ac_ct_LD" = x; then + LD=""no"" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + LD=$ac_ct_LD + fi +else + LD="$ac_cv_prog_LD" +fi + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}as", so it can be a program name with args. +set dummy ${ac_tool_prefix}as; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_AS+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$AS"; then + ac_cv_prog_AS="$AS" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_AS="${ac_tool_prefix}as" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +AS=$ac_cv_prog_AS +if test -n "$AS"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $AS" >&5 +printf "%s\n" "$AS" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_AS"; then + ac_ct_AS=$AS + # Extract the first word of "as", so it can be a program name with args. +set dummy as; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_ac_ct_AS+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$ac_ct_AS"; then + ac_cv_prog_ac_ct_AS="$ac_ct_AS" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_AS="as" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_AS=$ac_cv_prog_ac_ct_AS +if test -n "$ac_ct_AS"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AS" >&5 +printf "%s\n" "$ac_ct_AS" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + if test "x$ac_ct_AS" = x; then + AS=""no"" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + AS=$ac_ct_AS + fi +else + AS="$ac_cv_prog_AS" +fi + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}objcopy", so it can be a program name with args. +set dummy ${ac_tool_prefix}objcopy; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_OBJCOPY+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$OBJCOPY"; then + ac_cv_prog_OBJCOPY="$OBJCOPY" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_OBJCOPY="${ac_tool_prefix}objcopy" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +OBJCOPY=$ac_cv_prog_OBJCOPY +if test -n "$OBJCOPY"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $OBJCOPY" >&5 +printf "%s\n" "$OBJCOPY" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_OBJCOPY"; then + ac_ct_OBJCOPY=$OBJCOPY + # Extract the first word of "objcopy", so it can be a program name with args. +set dummy objcopy; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_ac_ct_OBJCOPY+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$ac_ct_OBJCOPY"; then + ac_cv_prog_ac_ct_OBJCOPY="$ac_ct_OBJCOPY" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_OBJCOPY="objcopy" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_OBJCOPY=$ac_cv_prog_ac_ct_OBJCOPY +if test -n "$ac_ct_OBJCOPY"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OBJCOPY" >&5 +printf "%s\n" "$ac_ct_OBJCOPY" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + if test "x$ac_ct_OBJCOPY" = x; then + OBJCOPY=""no"" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + OBJCOPY=$ac_ct_OBJCOPY + fi +else + OBJCOPY="$ac_cv_prog_OBJCOPY" +fi + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}ar", so it can be a program name with args. +set dummy ${ac_tool_prefix}ar; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_AR+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$AR"; then + ac_cv_prog_AR="$AR" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_AR="${ac_tool_prefix}ar" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +AR=$ac_cv_prog_AR +if test -n "$AR"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $AR" >&5 +printf "%s\n" "$AR" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_AR"; then + ac_ct_AR=$AR + # Extract the first word of "ar", so it can be a program name with args. +set dummy ar; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_ac_ct_AR+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$ac_ct_AR"; then + ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_AR="ar" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_AR=$ac_cv_prog_ac_ct_AR +if test -n "$ac_ct_AR"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5 +printf "%s\n" "$ac_ct_AR" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + if test "x$ac_ct_AR" = x; then + AR="""" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + AR=$ac_ct_AR + fi +else + AR="$ac_cv_prog_AR" +fi + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. +set dummy ${ac_tool_prefix}strip; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_STRIP+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$STRIP"; then + ac_cv_prog_STRIP="$STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_STRIP="${ac_tool_prefix}strip" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +STRIP=$ac_cv_prog_STRIP +if test -n "$STRIP"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 +printf "%s\n" "$STRIP" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_STRIP"; then + ac_ct_STRIP=$STRIP + # Extract the first word of "strip", so it can be a program name with args. +set dummy strip; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_ac_ct_STRIP+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$ac_ct_STRIP"; then + ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_STRIP="strip" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP +if test -n "$ac_ct_STRIP"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 +printf "%s\n" "$ac_ct_STRIP" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + if test "x$ac_ct_STRIP" = x; then + STRIP=""no"" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + STRIP=$ac_ct_STRIP + fi +else + STRIP="$ac_cv_prog_STRIP" +fi + + +if test "${target}" != "${host}" ; then + for ac_prog in ${target_alias}-gcc ${target}-gcc gcc +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_TARGET_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$TARGET_CC"; then + ac_cv_prog_TARGET_CC="$TARGET_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_TARGET_CC="$ac_prog" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +TARGET_CC=$ac_cv_prog_TARGET_CC +if test -n "$TARGET_CC"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $TARGET_CC" >&5 +printf "%s\n" "$TARGET_CC" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + + test -n "$TARGET_CC" && break +done + + for ac_prog in ${target_alias}-ld ${target}-ld ld +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_TARGET_LD+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$TARGET_LD"; then + ac_cv_prog_TARGET_LD="$TARGET_LD" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_TARGET_LD="$ac_prog" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +TARGET_LD=$ac_cv_prog_TARGET_LD +if test -n "$TARGET_LD"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $TARGET_LD" >&5 +printf "%s\n" "$TARGET_LD" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + + test -n "$TARGET_LD" && break +done + +else + TARGET_CC="$CC" + TARGET_LD="$LD" +fi + +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $TARGET_CC accepts -fno-zero-initialized-in-bss" >&5 +printf %s "checking whether $TARGET_CC accepts -fno-zero-initialized-in-bss... " >&6; } +saved_CFLAGS="$CFLAGS" +saved_CC="$CC" +CC="$TARGET_CC" +CFLAGS="$CFLAGS -fno-zero-initialized-in-bss" + +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main (void) +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + PURGATORY_EXTRA_CFLAGS="-fno-zero-initialized-in-bss" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Yes" >&5 +printf "%s\n" "Yes" >&6; } +else $as_nop + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: No" >&5 +printf "%s\n" "No" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +CFLAGS="$saved_CFLAGS" +CC="$saved_CC" + +PURGATORY_EXTRA_CFLAGS=$PURGATORY_EXTRA_CFLAGS + + + + # Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AmigaOS /C/install, which installs bootblocks on floppy discs +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# OS/2's system install, which has a completely different semantic +# ./install, which can be erroneously created by make from ./install.sh. +# Reject install programs that cannot install multiple files. +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 +printf %s "checking for a BSD-compatible install... " >&6; } +if test -z "$INSTALL"; then +if test ${ac_cv_path_install+y} +then : + printf %s "(cached) " >&6 +else $as_nop + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + # Account for fact that we put trailing slashes in our PATH walk. +case $as_dir in #(( + ./ | /[cC]/* | \ + /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ + ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ + /usr/ucb/* ) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_prog$ac_exec_ext"; then + if test $ac_prog = install && + grep dspmsg "$as_dir$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + elif test $ac_prog = install && + grep pwplus "$as_dir$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # program-specific install script used by HP pwplus--don't use. + : + else + rm -rf conftest.one conftest.two conftest.dir + echo one > conftest.one + echo two > conftest.two + mkdir conftest.dir + if "$as_dir$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir/" && + test -s conftest.one && test -s conftest.two && + test -s conftest.dir/conftest.one && + test -s conftest.dir/conftest.two + then + ac_cv_path_install="$as_dir$ac_prog$ac_exec_ext -c" + break 3 + fi + fi + fi + done + done + ;; +esac + + done +IFS=$as_save_IFS + +rm -rf conftest.one conftest.two conftest.dir + +fi + if test ${ac_cv_path_install+y}; then + INSTALL=$ac_cv_path_install + else + # As a last resort, use the slow shell script. Don't cache a + # value for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the value is a relative name. + INSTALL=$ac_install_sh + fi +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 +printf "%s\n" "$INSTALL" >&6; } + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + +# Extract the first word of "chmod", so it can be a program name with args. +set dummy chmod; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_CHMOD+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$CHMOD"; then + ac_cv_prog_CHMOD="$CHMOD" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_CHMOD="chmod" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + test -z "$ac_cv_prog_CHMOD" && ac_cv_prog_CHMOD=""no"" +fi +fi +CHMOD=$ac_cv_prog_CHMOD +if test -n "$CHMOD"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CHMOD" >&5 +printf "%s\n" "$CHMOD" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + +# Extract the first word of "mkdir", so it can be a program name with args. +set dummy mkdir; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_MKDIR+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$MKDIR"; then + ac_cv_prog_MKDIR="$MKDIR" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_MKDIR="mkdir" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + test -z "$ac_cv_prog_MKDIR" && ac_cv_prog_MKDIR=""no"" +fi +fi +MKDIR=$ac_cv_prog_MKDIR +if test -n "$MKDIR"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $MKDIR" >&5 +printf "%s\n" "$MKDIR" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + +# Extract the first word of "rm", so it can be a program name with args. +set dummy rm; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_RM+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$RM"; then + ac_cv_prog_RM="$RM" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_RM="rm" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + test -z "$ac_cv_prog_RM" && ac_cv_prog_RM=""no"" +fi +fi +RM=$ac_cv_prog_RM +if test -n "$RM"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $RM" >&5 +printf "%s\n" "$RM" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + +# Extract the first word of "cp", so it can be a program name with args. +set dummy cp; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_CP+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$CP"; then + ac_cv_prog_CP="$CP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_CP="cp" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + test -z "$ac_cv_prog_CP" && ac_cv_prog_CP=""no"" +fi +fi +CP=$ac_cv_prog_CP +if test -n "$CP"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CP" >&5 +printf "%s\n" "$CP" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + +# Extract the first word of "ln", so it can be a program name with args. +set dummy ln; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_LN+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$LN"; then + ac_cv_prog_LN="$LN" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_LN="ln" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + test -z "$ac_cv_prog_LN" && ac_cv_prog_LN=""no"" +fi +fi +LN=$ac_cv_prog_LN +if test -n "$LN"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $LN" >&5 +printf "%s\n" "$LN" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + +# Extract the first word of "tar", so it can be a program name with args. +set dummy tar; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_TAR+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$TAR"; then + ac_cv_prog_TAR="$TAR" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_TAR="tar" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + test -z "$ac_cv_prog_TAR" && ac_cv_prog_TAR=""no"" +fi +fi +TAR=$ac_cv_prog_TAR +if test -n "$TAR"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $TAR" >&5 +printf "%s\n" "$TAR" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + +# Extract the first word of "rpmbuild", so it can be a program name with args. +set dummy rpmbuild; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_RPMBUILD+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$RPMBUILD"; then + ac_cv_prog_RPMBUILD="$RPMBUILD" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_RPMBUILD="rpmbuild" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + test -z "$ac_cv_prog_RPMBUILD" && ac_cv_prog_RPMBUILD=""no"" +fi +fi +RPMBUILD=$ac_cv_prog_RPMBUILD +if test -n "$RPMBUILD"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $RPMBUILD" >&5 +printf "%s\n" "$RPMBUILD" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + +# Extract the first word of "sed", so it can be a program name with args. +set dummy sed; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_SED+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$SED"; then + ac_cv_prog_SED="$SED" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_SED="sed" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + test -z "$ac_cv_prog_SED" && ac_cv_prog_SED=""no"" +fi +fi +SED=$ac_cv_prog_SED +if test -n "$SED"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $SED" >&5 +printf "%s\n" "$SED" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + +# Extract the first word of "find", so it can be a program name with args. +set dummy find; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_FIND+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$FIND"; then + ac_cv_prog_FIND="$FIND" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_FIND="find" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + test -z "$ac_cv_prog_FIND" && ac_cv_prog_FIND=""no"" +fi +fi +FIND=$ac_cv_prog_FIND +if test -n "$FIND"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $FIND" >&5 +printf "%s\n" "$FIND" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + +# Extract the first word of "xargs", so it can be a program name with args. +set dummy xargs; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_XARGS+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$XARGS"; then + ac_cv_prog_XARGS="$XARGS" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_XARGS="xargs" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + test -z "$ac_cv_prog_XARGS" && ac_cv_prog_XARGS=""no"" +fi +fi +XARGS=$ac_cv_prog_XARGS +if test -n "$XARGS"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $XARGS" >&5 +printf "%s\n" "$XARGS" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + +# Extract the first word of "dirname", so it can be a program name with args. +set dummy dirname; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_DIRNAME+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$DIRNAME"; then + ac_cv_prog_DIRNAME="$DIRNAME" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_DIRNAME="dirname" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + test -z "$ac_cv_prog_DIRNAME" && ac_cv_prog_DIRNAME=""no"" +fi +fi +DIRNAME=$ac_cv_prog_DIRNAME +if test -n "$DIRNAME"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $DIRNAME" >&5 +printf "%s\n" "$DIRNAME" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + + +if test "$with_zlib" = yes ; then + ac_header= ac_cache= +for ac_item in $ac_header_c_list +do + if test $ac_cache; then + ac_fn_c_check_header_compile "$LINENO" $ac_header ac_cv_header_$ac_cache "$ac_includes_default" + if eval test \"x\$ac_cv_header_$ac_cache\" = xyes; then + printf "%s\n" "#define $ac_item 1" >> confdefs.h + fi + ac_header= ac_cache= + elif test $ac_header; then + ac_cache=$ac_item + else + ac_header=$ac_item + fi +done + + + + + + + + +if test $ac_cv_header_stdlib_h = yes && test $ac_cv_header_string_h = yes +then : + +printf "%s\n" "#define STDC_HEADERS 1" >>confdefs.h + +fi +ac_fn_c_check_header_compile "$LINENO" "zlib.h" "ac_cv_header_zlib_h" "$ac_includes_default" +if test "x$ac_cv_header_zlib_h" = xyes +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for inflateInit_ in -lz" >&5 +printf %s "checking for inflateInit_ in -lz... " >&6; } +if test ${ac_cv_lib_z_inflateInit_+y} +then : + printf %s "(cached) " >&6 +else $as_nop + ac_check_lib_save_LIBS=$LIBS +LIBS="-lz $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +char inflateInit_ (); +int +main (void) +{ +return inflateInit_ (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO" +then : + ac_cv_lib_z_inflateInit_=yes +else $as_nop + ac_cv_lib_z_inflateInit_=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_z_inflateInit_" >&5 +printf "%s\n" "$ac_cv_lib_z_inflateInit_" >&6; } +if test "x$ac_cv_lib_z_inflateInit_" = xyes +then : + printf "%s\n" "#define HAVE_LIBZ 1" >>confdefs.h + + LIBS="-lz $LIBS" + +else $as_nop + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: zlib support disabled" >&5 +printf "%s\n" "$as_me: zlib support disabled" >&6;} +fi + +fi + +fi + +if test "$with_lzma" = yes ; then + ac_fn_c_check_header_compile "$LINENO" "lzma.h" "ac_cv_header_lzma_h" "$ac_includes_default" +if test "x$ac_cv_header_lzma_h" = xyes +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for lzma_code in -llzma" >&5 +printf %s "checking for lzma_code in -llzma... " >&6; } +if test ${ac_cv_lib_lzma_lzma_code+y} +then : + printf %s "(cached) " >&6 +else $as_nop + ac_check_lib_save_LIBS=$LIBS +LIBS="-llzma $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +char lzma_code (); +int +main (void) +{ +return lzma_code (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO" +then : + ac_cv_lib_lzma_lzma_code=yes +else $as_nop + ac_cv_lib_lzma_lzma_code=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_lzma_lzma_code" >&5 +printf "%s\n" "$ac_cv_lib_lzma_lzma_code" >&6; } +if test "x$ac_cv_lib_lzma_lzma_code" = xyes +then : + printf "%s\n" "#define HAVE_LIBLZMA 1" >>confdefs.h + + LIBS="-llzma $LIBS" + +else $as_nop + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: lzma support disabled" >&5 +printf "%s\n" "$as_me: lzma support disabled" >&6;} +fi + +fi + +fi + +if test "$with_xen" = yes ; then + ac_fn_c_check_header_compile "$LINENO" "xenctrl.h" "ac_cv_header_xenctrl_h" "$ac_includes_default" +if test "x$ac_cv_header_xenctrl_h" = xyes +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for xc_kexec_load in -lxenctrl" >&5 +printf %s "checking for xc_kexec_load in -lxenctrl... " >&6; } +if test ${ac_cv_lib_xenctrl_xc_kexec_load+y} +then : + printf %s "(cached) " >&6 +else $as_nop + ac_check_lib_save_LIBS=$LIBS +LIBS="-lxenctrl $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +char xc_kexec_load (); +int +main (void) +{ +return xc_kexec_load (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO" +then : + ac_cv_lib_xenctrl_xc_kexec_load=yes +else $as_nop + ac_cv_lib_xenctrl_xc_kexec_load=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_xenctrl_xc_kexec_load" >&5 +printf "%s\n" "$ac_cv_lib_xenctrl_xc_kexec_load" >&6; } +if test "x$ac_cv_lib_xenctrl_xc_kexec_load" = xyes +then : + printf "%s\n" "#define HAVE_LIBXENCTRL 1" >>confdefs.h + + LIBS="-lxenctrl $LIBS" + +else $as_nop + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Xen support disabled" >&5 +printf "%s\n" "$as_me: Xen support disabled" >&6;} +fi + +fi + +fi + +if test "$with_xen" = dl ; then + ac_fn_c_check_header_compile "$LINENO" "dlfcn.h" "ac_cv_header_dlfcn_h" "$ac_includes_default" +if test "x$ac_cv_header_dlfcn_h" = xyes +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 +printf %s "checking for dlopen in -ldl... " >&6; } +if test ${ac_cv_lib_dl_dlopen+y} +then : + printf %s "(cached) " >&6 +else $as_nop + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldl $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +char dlopen (); +int +main (void) +{ +return dlopen (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO" +then : + ac_cv_lib_dl_dlopen=yes +else $as_nop + ac_cv_lib_dl_dlopen=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 +printf "%s\n" "$ac_cv_lib_dl_dlopen" >&6; } +if test "x$ac_cv_lib_dl_dlopen" = xyes +then : + printf "%s\n" "#define HAVE_LIBDL 1" >>confdefs.h + + LIBS="-ldl $LIBS" + +else $as_nop + as_fn_error $? "Dynamic library linking not available" "$LINENO" 5 +fi + +else $as_nop + as_fn_error $? "Dynamic library linking header not available" "$LINENO" 5 +fi + + +printf "%s\n" "#define CONFIG_LIBXENCTRL_DL 1" >>confdefs.h + + ac_fn_c_check_header_compile "$LINENO" "xenctrl.h" "ac_cv_header_xenctrl_h" "$ac_includes_default" +if test "x$ac_cv_header_xenctrl_h" = xyes +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for xc_kexec_load in -lxenctrl" >&5 +printf %s "checking for xc_kexec_load in -lxenctrl... " >&6; } +if test ${ac_cv_lib_xenctrl_xc_kexec_load+y} +then : + printf %s "(cached) " >&6 +else $as_nop + ac_check_lib_save_LIBS=$LIBS +LIBS="-lxenctrl $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +char xc_kexec_load (); +int +main (void) +{ +return xc_kexec_load (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO" +then : + ac_cv_lib_xenctrl_xc_kexec_load=yes +else $as_nop + ac_cv_lib_xenctrl_xc_kexec_load=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_xenctrl_xc_kexec_load" >&5 +printf "%s\n" "$ac_cv_lib_xenctrl_xc_kexec_load" >&6; } +if test "x$ac_cv_lib_xenctrl_xc_kexec_load" = xyes +then : + printf "%s\n" "#define HAVE_LIBXENCTRL 1" >>confdefs.h + +else $as_nop + # required define, and prevent -lxenctrl + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Xen support disabled" >&5 +printf "%s\n" "$as_me: Xen support disabled" >&6;} +fi + +fi + +fi + +if test "$ac_cv_lib_xenctrl_xc_kexec_load" = yes ; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for xc_kexec_status in -lxenctrl" >&5 +printf %s "checking for xc_kexec_status in -lxenctrl... " >&6; } +if test ${ac_cv_lib_xenctrl_xc_kexec_status+y} +then : + printf %s "(cached) " >&6 +else $as_nop + ac_check_lib_save_LIBS=$LIBS +LIBS="-lxenctrl $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +char xc_kexec_status (); +int +main (void) +{ +return xc_kexec_status (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO" +then : + ac_cv_lib_xenctrl_xc_kexec_status=yes +else $as_nop + ac_cv_lib_xenctrl_xc_kexec_status=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_xenctrl_xc_kexec_status" >&5 +printf "%s\n" "$ac_cv_lib_xenctrl_xc_kexec_status" >&6; } +if test "x$ac_cv_lib_xenctrl_xc_kexec_status" = xyes +then : + +printf "%s\n" "#define HAVE_KEXEC_CMD_STATUS 1" >>confdefs.h + +else $as_nop + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: The kexec_status call is not available" >&5 +printf "%s\n" "$as_me: The kexec_status call is not available" >&6;} +fi + +fi + +if test "$CC" = "no"; then as_fn_error $? "cc not found" "$LINENO" 5; fi +if test "$CPP" = "no"; then as_fn_error $? "cpp not found" "$LINENO" 5; fi +if test "$LD" = "no"; then as_fn_error $? "ld not found" "$LINENO" 5; fi +if test "$AS" = "no"; then as_fn_error $? "as not found" "$LINENO" 5; fi +if test "$OBJCOPY" = "no"; then as_fn_error $? "objcopy not found" "$LINENO" 5; fi +if test "$AR" = "no"; then as_fn_error $? "ar not found" "$LINENO" 5; fi + +if test "$MKDIR" = "no"; then as_fn_error $? " mkdir not found" "$LINENO" 5; fi +if test "$RM" = "no"; then as_fn_error $? " rm not found" "$LINENO" 5; fi +if test "$CP" = "no"; then as_fn_error $? " cp not found" "$LINENO" 5; fi +if test "$LN" = "no"; then as_fn_error $? " ln not found" "$LINENO" 5; fi +if test "$TAR" = "no"; then as_fn_error $? " tar not found" "$LINENO" 5; fi +if test "$RPM" = "no"; then as_fn_error $? " rpm not found" "$LINENO" 5; fi +if test "$SED" = "no"; then as_fn_error $? " sed not found" "$LINENO" 5; fi +if test "$FIND" = "no"; then as_fn_error $? " find not found" "$LINENO" 5; fi +if test "$XARGS" = "no"; then as_fn_error $? " xargs not found" "$LINENO" 5; fi +if test "$DIRNAME" = "no"; then as_fn_error $? " dirname not found" "$LINENO" 5; fi +if test "$STRIP" = "no"; then as_fn_error $? " strip not found" "$LINENO" 5; fi + + + + + + + + + + + + + +ac_config_files="$ac_config_files Makefile" + +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, we kill variables containing newlines. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +( + for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +printf "%s\n" "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + + (set) 2>&1 | + case $as_nl`(ac_space=' '; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + # `set' does not quote correctly, so add quotes: double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \. + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; #( + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) | + sed ' + /^ac_cv_env_/b end + t clear + :clear + s/^\([^=]*\)=\(.*[{}].*\)$/test ${\1+y} || &/ + t end + s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + :end' >>confcache +if diff "$cache_file" confcache >/dev/null 2>&1; then :; else + if test -w "$cache_file"; then + if test "x$cache_file" != "x/dev/null"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 +printf "%s\n" "$as_me: updating cache $cache_file" >&6;} + if test ! -f "$cache_file" || test -h "$cache_file"; then + cat confcache >"$cache_file" + else + case $cache_file in #( + */* | ?:*) + mv -f confcache "$cache_file"$$ && + mv -f "$cache_file"$$ "$cache_file" ;; #( + *) + mv -f confcache "$cache_file" ;; + esac + fi + fi + else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 +printf "%s\n" "$as_me: not updating unwritable cache $cache_file" >&6;} + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +DEFS=-DHAVE_CONFIG_H + +ac_libobjs= +ac_ltlibobjs= +U= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' + ac_i=`printf "%s\n" "$ac_i" | sed "$ac_script"` + # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR + # will be set to the directory where LIBOBJS objects are built. + as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" + as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + + +: "${CONFIG_STATUS=./config.status}" +ac_write_fail=0 +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 +printf "%s\n" "$as_me: creating $CONFIG_STATUS" >&6;} +as_write_fail=0 +cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false + +SHELL=\${CONFIG_SHELL-$SHELL} +export SHELL +_ASEOF +cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +as_nop=: +if test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 +then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else $as_nop + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + + +# Reset variables that may have inherited troublesome values from +# the environment. + +# IFS needs to be set, to space, tab, and newline, in precisely that order. +# (If _AS_PATH_WALK were called with IFS unset, it would have the +# side effect of setting IFS to empty, thus disabling word splitting.) +# Quoting is to prevent editors from complaining about space-tab. +as_nl=' +' +export as_nl +IFS=" "" $as_nl" + +PS1='$ ' +PS2='> ' +PS4='+ ' + +# Ensure predictable behavior from utilities with locale-dependent output. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# We cannot yet rely on "unset" to work, but we need these variables +# to be unset--not just set to an empty or harmless value--now, to +# avoid bugs in old shells (e.g. pre-3.0 UWIN ksh). This construct +# also avoids known problems related to "unset" and subshell syntax +# in other old shells (e.g. bash 2.01 and pdksh 5.2.14). +for as_var in BASH_ENV ENV MAIL MAILPATH CDPATH +do eval test \${$as_var+y} \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done + +# Ensure that fds 0, 1, and 2 are open. +if (exec 3>&0) 2>/dev/null; then :; else exec 0</dev/null; fi +if (exec 3>&1) 2>/dev/null; then :; else exec 1>/dev/null; fi +if (exec 3>&2) ; then :; else exec 2>/dev/null; fi + +# The user is always right. +if ${PATH_SEPARATOR+false} :; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + test -r "$as_dir$0" && as_myself=$as_dir$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + printf "%s\n" "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + printf "%s\n" "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + + + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset + +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null +then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else $as_nop + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null +then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else $as_nop + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +printf "%s\n" X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + + +# Determine whether it's possible to make 'echo' print without a newline. +# These variables are no longer used directly by Autoconf, but are AC_SUBSTed +# for compatibility with existing Makefiles. +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +# For backward compatibility with old third-party macros, we provide +# the shell variables $as_echo and $as_echo_n. New code should use +# AS_ECHO(["message"]) and AS_ECHO_N(["message"]), respectively. +as_echo='printf %s\n' +as_echo_n='printf %s' + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -pR'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -pR' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -pR' + fi +else + as_ln_s='cp -pR' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`printf "%s\n" "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +printf "%s\n" X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +as_test_x='test -x' +as_executable_p=as_fn_executable_p + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +exec 6>&1 +## ----------------------------------- ## +## Main body of $CONFIG_STATUS script. ## +## ----------------------------------- ## +_ASEOF +test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# Save the log message, to keep $0 and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. +ac_log=" +This file was extended by kexec-tools $as_me 2.0.27, which was +generated by GNU Autoconf 2.71. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +on `(hostname || uname -n) 2>/dev/null | sed 1q` +" + +_ACEOF + +case $ac_config_files in *" +"*) set x $ac_config_files; shift; ac_config_files=$*;; +esac + +case $ac_config_headers in *" +"*) set x $ac_config_headers; shift; ac_config_headers=$*;; +esac + + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +# Files that config.status was made for. +config_files="$ac_config_files" +config_headers="$ac_config_headers" + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +ac_cs_usage="\ +\`$as_me' instantiates files and other configuration actions +from templates according to the current configuration. Unless the files +and actions are specified as TAGs, all are instantiated by default. + +Usage: $0 [OPTION]... [TAG]... + + -h, --help print this help, then exit + -V, --version print version number and configuration settings, then exit + --config print configuration, then exit + -q, --quiet, --silent + do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + --header=FILE[:TEMPLATE] + instantiate the configuration header FILE + +Configuration files: +$config_files + +Configuration headers: +$config_headers + +Report bugs to the package provider." + +_ACEOF +ac_cs_config=`printf "%s\n" "$ac_configure_args" | sed "$ac_safe_unquote"` +ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\''/g"` +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_cs_config='$ac_cs_config_escaped' +ac_cs_version="\\ +kexec-tools config.status 2.0.27 +configured by $0, generated by GNU Autoconf 2.71, + with options \\"\$ac_cs_config\\" + +Copyright (C) 2021 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." + +ac_pwd='$ac_pwd' +srcdir='$srcdir' +INSTALL='$INSTALL' +test -n "\$AWK" || AWK=awk +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# The default lists apply if the user does not specify any file. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=?*) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` + ac_shift=: + ;; + --*=) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg= + ac_shift=: + ;; + *) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + esac + + case $ac_option in + # Handling of the options. + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) + printf "%s\n" "$ac_cs_version"; exit ;; + --config | --confi | --conf | --con | --co | --c ) + printf "%s\n" "$ac_cs_config"; exit ;; + --debug | --debu | --deb | --de | --d | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`printf "%s\n" "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + '') as_fn_error $? "missing file argument" ;; + esac + as_fn_append CONFIG_FILES " '$ac_optarg'" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`printf "%s\n" "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + as_fn_append CONFIG_HEADERS " '$ac_optarg'" + ac_need_defaults=false;; + --he | --h) + # Conflict between --help and --header + as_fn_error $? "ambiguous option: \`$1' +Try \`$0 --help' for more information.";; + --help | --hel | -h ) + printf "%s\n" "$ac_cs_usage"; exit ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) as_fn_error $? "unrecognized option: \`$1' +Try \`$0 --help' for more information." ;; + + *) as_fn_append ac_config_targets " $1" + ac_need_defaults=false ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +if \$ac_cs_recheck; then + set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion + shift + \printf "%s\n" "running CONFIG_SHELL=$SHELL \$*" >&6 + CONFIG_SHELL='$SHELL' + export CONFIG_SHELL + exec "\$@" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX + printf "%s\n" "$ac_log" +} >&5 + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + +# Handling of arguments. +for ac_config_target in $ac_config_targets +do + case $ac_config_target in + "include/config.h") CONFIG_HEADERS="$CONFIG_HEADERS include/config.h" ;; + "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; + + *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; + esac +done + + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test ${CONFIG_FILES+y} || CONFIG_FILES=$config_files + test ${CONFIG_HEADERS+y} || CONFIG_HEADERS=$config_headers +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason against having it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Hook for its removal unless debugging. +# Note that there is a small window in which the directory will not be cleaned: +# after its creation but before its name has been assigned to `$tmp'. +$debug || +{ + tmp= ac_tmp= + trap 'exit_status=$? + : "${ac_tmp:=$tmp}" + { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status +' 0 + trap 'as_fn_exit 1' 1 2 13 15 +} +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && + test -d "$tmp" +} || +{ + tmp=./conf$$-$RANDOM + (umask 077 && mkdir "$tmp") +} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 +ac_tmp=$tmp + +# Set up the scripts for CONFIG_FILES section. +# No need to generate them if there are no CONFIG_FILES. +# This happens for instance with `./config.status config.h'. +if test -n "$CONFIG_FILES"; then + + +ac_cr=`echo X | tr X '\015'` +# On cygwin, bash can eat \r inside `` if the user requested igncr. +# But we know of no other shell where ac_cr would be empty at this +# point, so we can use a bashism as a fallback. +if test "x$ac_cr" = x; then + eval ac_cr=\$\'\\r\' +fi +ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' </dev/null 2>/dev/null` +if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then + ac_cs_awk_cr='\\r' +else + ac_cs_awk_cr=$ac_cr +fi + +echo 'BEGIN {' >"$ac_tmp/subs1.awk" && +_ACEOF + + +{ + echo "cat >conf$$subs.awk <<_ACEOF" && + echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && + echo "_ACEOF" +} >conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 +ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` +ac_delim='%!_!# ' +for ac_last_try in false false false false false :; do + . ./conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + + ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` + if test $ac_delim_n = $ac_delim_num; then + break + elif $ac_last_try; then + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done +rm -f conf$$subs.sh + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && +_ACEOF +sed -n ' +h +s/^/S["/; s/!.*/"]=/ +p +g +s/^[^!]*!// +:repl +t repl +s/'"$ac_delim"'$// +t delim +:nl +h +s/\(.\{148\}\)..*/\1/ +t more1 +s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ +p +n +b repl +:more1 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t nl +:delim +h +s/\(.\{148\}\)..*/\1/ +t more2 +s/["\\]/\\&/g; s/^/"/; s/$/"/ +p +b +:more2 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t delim +' <conf$$subs.awk | sed ' +/^[^""]/{ + N + s/\n// +} +' >>$CONFIG_STATUS || ac_write_fail=1 +rm -f conf$$subs.awk +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACAWK +cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && + for (key in S) S_is_set[key] = 1 + FS = "" + +} +{ + line = $ 0 + nfields = split(line, field, "@") + substed = 0 + len = length(field[1]) + for (i = 2; i < nfields; i++) { + key = field[i] + keylen = length(key) + if (S_is_set[key]) { + value = S[key] + line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) + len += length(value) + length(field[++i]) + substed = 1 + } else + len += 1 + keylen + } + + print line +} + +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then + sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" +else + cat +fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ + || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 +_ACEOF + +# VPATH may cause trouble with some makes, so we remove sole $(srcdir), +# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ +h +s/// +s/^/:/ +s/[ ]*$/:/ +s/:\$(srcdir):/:/g +s/:\${srcdir}:/:/g +s/:@srcdir@:/:/g +s/^:*// +s/:*$// +x +s/\(=[ ]*\).*/\1/ +G +s/\n// +s/^[^=]*=[ ]*$// +}' +fi + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +fi # test -n "$CONFIG_FILES" + +# Set up the scripts for CONFIG_HEADERS section. +# No need to generate them if there are no CONFIG_HEADERS. +# This happens for instance with `./config.status Makefile'. +if test -n "$CONFIG_HEADERS"; then +cat >"$ac_tmp/defines.awk" <<\_ACAWK || +BEGIN { +_ACEOF + +# Transform confdefs.h into an awk script `defines.awk', embedded as +# here-document in config.status, that substitutes the proper values into +# config.h.in to produce config.h. + +# Create a delimiter string that does not exist in confdefs.h, to ease +# handling of long lines. +ac_delim='%!_!# ' +for ac_last_try in false false :; do + ac_tt=`sed -n "/$ac_delim/p" confdefs.h` + if test -z "$ac_tt"; then + break + elif $ac_last_try; then + as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done + +# For the awk script, D is an array of macro values keyed by name, +# likewise P contains macro parameters if any. Preserve backslash +# newline sequences. + +ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* +sed -n ' +s/.\{148\}/&'"$ac_delim"'/g +t rset +:rset +s/^[ ]*#[ ]*define[ ][ ]*/ / +t def +d +:def +s/\\$// +t bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3"/p +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p +d +:bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3\\\\\\n"\\/p +t cont +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p +t cont +d +:cont +n +s/.\{148\}/&'"$ac_delim"'/g +t clear +:clear +s/\\$// +t bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/"/p +d +:bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p +b cont +' <confdefs.h | sed ' +s/'"$ac_delim"'/"\\\ +"/g' >>$CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + for (key in D) D_is_set[key] = 1 + FS = "" +} +/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { + line = \$ 0 + split(line, arg, " ") + if (arg[1] == "#") { + defundef = arg[2] + mac1 = arg[3] + } else { + defundef = substr(arg[1], 2) + mac1 = arg[2] + } + split(mac1, mac2, "(") #) + macro = mac2[1] + prefix = substr(line, 1, index(line, defundef) - 1) + if (D_is_set[macro]) { + # Preserve the white space surrounding the "#". + print prefix "define", macro P[macro] D[macro] + next + } else { + # Replace #undef with comments. This is necessary, for example, + # in the case of _POSIX_SOURCE, which is predefined and required + # on some systems where configure will not decide to define it. + if (defundef == "undef") { + print "/*", prefix defundef, macro, "*/" + next + } + } +} +{ print } +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 +fi # test -n "$CONFIG_HEADERS" + + +eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS " +shift +for ac_tag +do + case $ac_tag in + :[FHLC]) ac_mode=$ac_tag; continue;; + esac + case $ac_mode$ac_tag in + :[FHL]*:*);; + :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; + :[FH]-) ac_tag=-:-;; + :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; + esac + ac_save_IFS=$IFS + IFS=: + set x $ac_tag + IFS=$ac_save_IFS + shift + ac_file=$1 + shift + + case $ac_mode in + :L) ac_source=$1;; + :[FH]) + ac_file_inputs= + for ac_f + do + case $ac_f in + -) ac_f="$ac_tmp/stdin";; + *) # Look for the file first in the build tree, then in the source tree + # (if the path is not absolute). The absolute path cannot be DOS-style, + # because $ac_f cannot contain `:'. + test -f "$ac_f" || + case $ac_f in + [\\/$]*) false;; + *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; + esac || + as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; + esac + case $ac_f in *\'*) ac_f=`printf "%s\n" "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac + as_fn_append ac_file_inputs " '$ac_f'" + done + + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + configure_input='Generated from '` + printf "%s\n" "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' + `' by configure.' + if test x"$ac_file" != x-; then + configure_input="$ac_file. $configure_input" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 +printf "%s\n" "$as_me: creating $ac_file" >&6;} + fi + # Neutralize special characters interpreted by sed in replacement strings. + case $configure_input in #( + *\&* | *\|* | *\\* ) + ac_sed_conf_input=`printf "%s\n" "$configure_input" | + sed 's/[\\\\&|]/\\\\&/g'`;; #( + *) ac_sed_conf_input=$configure_input;; + esac + + case $ac_tag in + *:-:* | *:-) cat >"$ac_tmp/stdin" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; + esac + ;; + esac + + ac_dir=`$as_dirname -- "$ac_file" || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || +printf "%s\n" X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + as_dir="$ac_dir"; as_fn_mkdir_p + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`printf "%s\n" "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + + case $ac_mode in + :F) + # + # CONFIG_FILE + # + + case $INSTALL in + [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; + *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; + esac +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# If the template does not know about datarootdir, expand it. +# FIXME: This hack should be removed a few years after 2.60. +ac_datarootdir_hack=; ac_datarootdir_seen= +ac_sed_dataroot=' +/datarootdir/ { + p + q +} +/@datadir@/p +/@docdir@/p +/@infodir@/p +/@localedir@/p +/@mandir@/p' +case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in +*datarootdir*) ac_datarootdir_seen=yes;; +*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 +printf "%s\n" "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + ac_datarootdir_hack=' + s&@datadir@&$datadir&g + s&@docdir@&$docdir&g + s&@infodir@&$infodir&g + s&@localedir@&$localedir&g + s&@mandir@&$mandir&g + s&\\\${datarootdir}&$datarootdir&g' ;; +esac +_ACEOF + +# Neutralize VPATH when `$srcdir' = `.'. +# Shell code in configure.ac might set extrasub. +# FIXME: do we really want to maintain this feature? +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_sed_extra="$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s|@configure_input@|$ac_sed_conf_input|;t t +s&@top_builddir@&$ac_top_builddir_sub&;t t +s&@top_build_prefix@&$ac_top_build_prefix&;t t +s&@srcdir@&$ac_srcdir&;t t +s&@abs_srcdir@&$ac_abs_srcdir&;t t +s&@top_srcdir@&$ac_top_srcdir&;t t +s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t +s&@builddir@&$ac_builddir&;t t +s&@abs_builddir@&$ac_abs_builddir&;t t +s&@abs_top_builddir@&$ac_abs_top_builddir&;t t +s&@INSTALL@&$ac_INSTALL&;t t +$ac_datarootdir_hack +" +eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ + >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + +test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && + { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && + { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ + "$ac_tmp/out"`; test -z "$ac_out"; } && + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&5 +printf "%s\n" "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&2;} + + rm -f "$ac_tmp/stdin" + case $ac_file in + -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; + *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; + esac \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + ;; + :H) + # + # CONFIG_HEADER + # + if test x"$ac_file" != x-; then + { + printf "%s\n" "/* $configure_input */" >&1 \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" + } >"$ac_tmp/config.h" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 +printf "%s\n" "$as_me: $ac_file is unchanged" >&6;} + else + rm -f "$ac_file" + mv "$ac_tmp/config.h" "$ac_file" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + fi + else + printf "%s\n" "/* $configure_input */" >&1 \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ + || as_fn_error $? "could not create -" "$LINENO" 5 + fi + ;; + + + esac + +done # for ac_tag + + +as_fn_exit 0 +_ACEOF +ac_clean_files=$ac_clean_files_save + +test $ac_write_fail = 0 || + as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || as_fn_exit 1 +fi +if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 +printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} +fi + + diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..192976c --- /dev/null +++ b/configure.ac @@ -0,0 +1,245 @@ +dnl +dnl configure.ac for kexec-tools +dnl +dnl + +dnl ---Required +AC_INIT(kexec-tools, 2.0.27) +AC_CONFIG_AUX_DIR(./config) +AC_CONFIG_HEADERS([include/config.h]) +AC_LANG(C) +AC_PROG_CC + + +dnl -- Compilation platform configuration +dnl -- the host specifices the host machine for the kexec binary, the +dnl -- the target specifies the architecture of the kernel to be kexeced. +AC_CANONICAL_HOST +AC_CANONICAL_TARGET + +dnl Compute ARCH from target cpu info +case $target_cpu in + i?86 ) + ARCH="i386" + ;; + powerpc ) + ARCH="ppc" + SUBARCH="BE" + ;; + powerpc64 ) + ARCH="ppc64" + SUBARCH="BE" + ;; + powerpc64le ) + ARCH="ppc64" + SUBARCH="LE" + ;; + aarch64* ) + ARCH="arm64" + ;; + arm* ) + ARCH="arm" + ;; + s390x|s390 ) + ARCH="s390" + ;; + sh4|sh4a|sh3|sh ) + ARCH="sh" + ;; + mips* ) + ARCH="mips" + ;; + cris|crisv32 ) + ARCH="cris" + ;; + ia64|x86_64|alpha|m68k ) + ARCH="$target_cpu" + ;; + hppa*) + ARCH="hppa" + ;; + loongarch*) + ARCH="loongarch" + ;; + * ) + AC_MSG_ERROR([unsupported architecture $target_cpu]) + ;; +esac + +if test "$ARCH" = "x86_64" ; then + case $target in + *x32 ) + SUBARCH="x32" + ;; + * ) + SUBARCH="" + ;; + esac +fi + +dnl ---Options + +OBJDIR=`pwd`/objdir +if test "${host_alias}" ; then + OBJDIR="$OBJDIR-${host_alias}" +fi + +if test "x$BUILD_CFLAGS" = "x" ; then + BUILD_CFLAGS='-O2 -Wall' +fi + +if test "x$TARGET_CFLAGS" = "x" ; then + TARGET_CFLAGS='-O2 -Wall' +fi + +AC_ARG_WITH([objdir], AC_HELP_STRING([--with-objdir=<dir>],[select directory for object files]), + [ OBJDIR="$withval" ], [ OBJDIR="$OBJDIR" ]) + +AC_ARG_WITH([gamecube], + AC_HELP_STRING([--with-gamecube],[enable gamecube support]), + AC_DEFINE(WITH_GAMECUBE, 1, + [Define to include gamecube support])) + +AC_ARG_WITH([zlib], AC_HELP_STRING([--without-zlib],[disable zlib support]), + [ with_zlib="$withval"], [ with_zlib=yes ] ) + +AC_ARG_WITH([lzma], AC_HELP_STRING([--without-lzma],[disable lzma support]), + [ with_lzma="$withval"], [ with_lzma=yes ] ) + +AC_ARG_WITH([xen], AC_HELP_STRING([--without-xen], + [disable extended xen support]), [ with_xen="$withval"], [ with_xen=yes ] ) + +AC_ARG_WITH([booke], + AC_HELP_STRING([--with-booke],[build for booke]), + AC_DEFINE(CONFIG_BOOKE,1, + [Define to build for BookE])) + +dnl ---Programs +dnl To specify a different compiler, just 'export CC=/path/to/compiler' +if test "${build}" != "${host}" ; then + AC_CHECK_PROGS(BUILD_CC, [${build_alias}-gcc ${build}-gcc gcc]) +else + BUILD_CC="$CC" +fi + +dnl Find the compiler tool chain +AC_PROG_CPP +AC_CHECK_TOOL([LD], ld, "no", $PATH) +AC_CHECK_TOOL([AS], as, "no", $PATH) +AC_CHECK_TOOL([OBJCOPY], objcopy, "no", $PATH) +AC_CHECK_TOOL([AR], ar, "", $PATH) +AC_CHECK_TOOL([STRIP], strip, "no", $PATH) + +dnl Find compiler for target +if test "${target}" != "${host}" ; then + AC_CHECK_PROGS(TARGET_CC, [${target_alias}-gcc ${target}-gcc gcc]) + AC_CHECK_PROGS(TARGET_LD, [${target_alias}-ld ${target}-ld ld]) +else + TARGET_CC="$CC" + TARGET_LD="$LD" +fi + +AC_MSG_CHECKING([whether $TARGET_CC accepts -fno-zero-initialized-in-bss]) +saved_CFLAGS="$CFLAGS" +saved_CC="$CC" +CC="$TARGET_CC" +CFLAGS="$CFLAGS -fno-zero-initialized-in-bss" +AC_COMPILE_IFELSE([AC_LANG_PROGRAM(,,)], + PURGATORY_EXTRA_CFLAGS="-fno-zero-initialized-in-bss" + AC_MSG_RESULT([Yes]), AC_MSG_RESULT([No])) +CFLAGS="$saved_CFLAGS" +CC="$saved_CC" + +AC_SUBST(PURGATORY_EXTRA_CFLAGS, [$PURGATORY_EXTRA_CFLAGS]) + +dnl Find the helper functions +AC_PROG_INSTALL +AC_CHECK_PROG([CHMOD], chmod, chmod, "no", [$PATH]) +AC_CHECK_PROG([MKDIR], mkdir, mkdir, "no", [$PATH]) +AC_CHECK_PROG([RM], rm, rm, "no", [$PATH]) +AC_CHECK_PROG([CP], cp, cp, "no", [$PATH]) +AC_CHECK_PROG([LN], ln, ln, "no", [$PATH]) +AC_CHECK_PROG([TAR], tar, tar, "no", [$PATH]) +AC_CHECK_PROG([RPMBUILD], rpmbuild, rpmbuild, "no", [$PATH]) +AC_CHECK_PROG([SED], sed, sed, "no", [$PATH]) +AC_CHECK_PROG([FIND], find, find, "no", [$PATH]) +AC_CHECK_PROG([XARGS], xargs, xargs, "no", [$PATH]) +AC_CHECK_PROG([DIRNAME], dirname, dirname, "no", [$PATH]) + +dnl See if I have a usable copy of zlib available +if test "$with_zlib" = yes ; then + AC_CHECK_HEADER(zlib.h, + [AC_CHECK_LIB(z, inflateInit_, , + AC_MSG_NOTICE([zlib support disabled]))]) +fi + +dnl See if I have a usable copy of lzma available +if test "$with_lzma" = yes ; then + AC_CHECK_HEADER(lzma.h, + [AC_CHECK_LIB(lzma, lzma_code, , + AC_MSG_NOTICE([lzma support disabled]))]) +fi + +dnl find Xen control stack libraries +if test "$with_xen" = yes ; then + AC_CHECK_HEADER(xenctrl.h, + [AC_CHECK_LIB(xenctrl, xc_kexec_load, , + AC_MSG_NOTICE([Xen support disabled]))]) +fi + +dnl Link libxenctrl.so at run-time rather than build-time +if test "$with_xen" = dl ; then + AC_CHECK_HEADER(dlfcn.h, + [AC_CHECK_LIB(dl, dlopen, , + AC_MSG_ERROR([Dynamic library linking not available]))], + AC_MSG_ERROR([Dynamic library linking header not available])) + AC_DEFINE(CONFIG_LIBXENCTRL_DL, 1, [Define to 1 to link libxenctrl.so at run-time rather than build-time]) + AC_CHECK_HEADER(xenctrl.h, + [AC_CHECK_LIB(xenctrl, xc_kexec_load, + AC_DEFINE(HAVE_LIBXENCTRL, 1, ), # required define, and prevent -lxenctrl + AC_MSG_NOTICE([Xen support disabled]))]) +fi + +dnl Check for the Xen kexec_status hypercall - reachable from --with-xen=yes|dl +if test "$ac_cv_lib_xenctrl_xc_kexec_load" = yes ; then + AC_CHECK_LIB(xenctrl, xc_kexec_status, + AC_DEFINE(HAVE_KEXEC_CMD_STATUS, 1, + [The kexec_status call is available]), + AC_MSG_NOTICE([The kexec_status call is not available])) +fi + +dnl ---Sanity checks +if test "$CC" = "no"; then AC_MSG_ERROR([cc not found]); fi +if test "$CPP" = "no"; then AC_MSG_ERROR([cpp not found]); fi +if test "$LD" = "no"; then AC_MSG_ERROR([ld not found]); fi +if test "$AS" = "no"; then AC_MSG_ERROR([as not found]); fi +if test "$OBJCOPY" = "no"; then AC_MSG_ERROR([objcopy not found]); fi +if test "$AR" = "no"; then AC_MSG_ERROR([ar not found]); fi + +if test "$MKDIR" = "no"; then AC_MSG_ERROR([ mkdir not found]); fi +if test "$RM" = "no"; then AC_MSG_ERROR([ rm not found]); fi +if test "$CP" = "no"; then AC_MSG_ERROR([ cp not found]); fi +if test "$LN" = "no"; then AC_MSG_ERROR([ ln not found]); fi +if test "$TAR" = "no"; then AC_MSG_ERROR([ tar not found]); fi +if test "$RPM" = "no"; then AC_MSG_ERROR([ rpm not found]); fi +if test "$SED" = "no"; then AC_MSG_ERROR([ sed not found]); fi +if test "$FIND" = "no"; then AC_MSG_ERROR([ find not found]); fi +if test "$XARGS" = "no"; then AC_MSG_ERROR([ xargs not found]); fi +if test "$DIRNAME" = "no"; then AC_MSG_ERROR([ dirname not found]); fi +if test "$STRIP" = "no"; then AC_MSG_ERROR([ strip not found]); fi + +dnl ---Output variables... +AC_SUBST([BUILD_CC]) +AC_SUBST([BUILD_CFLAGS]) +AC_SUBST([TARGET_CC]) +AC_SUBST([TARGET_LD]) +AC_SUBST([TARGET_CFLAGS]) +AC_SUBST([ASFLAGS]) + +AC_SUBST([ARCH]) +AC_SUBST([SUBARCH]) +AC_SUBST([OBJDIR]) +AC_SUBST([INSTALL]) + +dnl ---Output +AC_OUTPUT([Makefile]) diff --git a/doc/Makefile b/doc/Makefile new file mode 100644 index 0000000..3442e09 --- /dev/null +++ b/doc/Makefile @@ -0,0 +1,5 @@ +dist += doc/Makefile \ + doc/linux-i386-boot.txt \ + doc/linux-i386-zero-page.txt \ + doc/multiboot.html \ + doc/nbi-spec.txt diff --git a/doc/linux-i386-boot.txt b/doc/linux-i386-boot.txt new file mode 100644 index 0000000..afa6d12 --- /dev/null +++ b/doc/linux-i386-boot.txt @@ -0,0 +1,438 @@ + THE LINUX/I386 BOOT PROTOCOL + ---------------------------- + + H. Peter Anvin <hpa@zytor.com> + Last update 2002-01-01 + +On the i386 platform, the Linux kernel uses a rather complicated boot +convention. This has evolved partially due to historical aspects, as +well as the desire in the early days to have the kernel itself be a +bootable image, the complicated PC memory model and due to changed +expectations in the PC industry caused by the effective demise of +real-mode DOS as a mainstream operating system. + +Currently, four versions of the Linux/i386 boot protocol exist. + +Old kernels: zImage/Image support only. Some very early kernels + may not even support a command line. + +Protocol 2.00: (Kernel 1.3.73) Added bzImage and initrd support, as + well as a formalized way to communicate between the + boot loader and the kernel. setup.S made relocatable, + although the traditional setup area still assumed + writable. + +Protocol 2.01: (Kernel 1.3.76) Added a heap overrun warning. + +Protocol 2.02: (Kernel 2.4.0-test3-pre3) New command line protocol. + Lower the conventional memory ceiling. No overwrite + of the traditional setup area, thus making booting + safe for systems which use the EBDA from SMM or 32-bit + BIOS entry points. zImage deprecated but still + supported. + +Protocol 2.03: (Kernel 2.4.18-pre1) Explicitly makes the highest possible + initrd address available to the bootloader. + + +**** MEMORY LAYOUT + +The traditional memory map for the kernel loader, used for Image or +zImage kernels, typically looks like: + + | | +0A0000 +------------------------+ + | Reserved for BIOS | Do not use. Reserved for BIOS EBDA. +09A000 +------------------------+ + | Stack/heap/cmdline | For use by the kernel real-mode code. +098000 +------------------------+ + | Kernel setup | The kernel real-mode code. +090200 +------------------------+ + | Kernel boot sector | The kernel legacy boot sector. +090000 +------------------------+ + | Protected-mode kernel | The bulk of the kernel image. +010000 +------------------------+ + | Boot loader | <- Boot sector entry point 0000:7C00 +001000 +------------------------+ + | Reserved for MBR/BIOS | +000800 +------------------------+ + | Typically used by MBR | +000600 +------------------------+ + | BIOS use only | +000000 +------------------------+ + + +When using bzImage, the protected-mode kernel was relocated to +0x100000 ("high memory"), and the kernel real-mode block (boot sector, +setup, and stack/heap) was made relocatable to any address between +0x10000 and end of low memory. Unfortunately, in protocols 2.00 and +2.01 the command line is still required to live in the 0x9XXXX memory +range, and that memory range is still overwritten by the early kernel. +The 2.02 protocol resolves that problem. + +It is desirable to keep the "memory ceiling" -- the highest point in +low memory touched by the boot loader -- as low as possible, since +some newer BIOSes have begun to allocate some rather large amounts of +memory, called the Extended BIOS Data Area, near the top of low +memory. The boot loader should use the "INT 12h" BIOS call to verify +how much low memory is available. + +Unfortunately, if INT 12h reports that the amount of memory is too +low, there is usually nothing the boot loader can do but to report an +error to the user. The boot loader should therefore be designed to +take up as little space in low memory as it reasonably can. For +zImage or old bzImage kernels, which need data written into the +0x90000 segment, the boot loader should make sure not to use memory +above the 0x9A000 point; too many BIOSes will break above that point. + + +**** THE REAL-MODE KERNEL HEADER + +In the following text, and anywhere in the kernel boot sequence, "a +sector" refers to 512 bytes. It is independent of the actual sector +size of the underlying medium. + +The first step in loading a Linux kernel should be to load the +real-mode code (boot sector and setup code) and then examine the +following header at offset 0x01f1. The real-mode code can total up to +32K, although the boot loader may choose to load only the first two +sectors (1K) and then examine the bootup sector size. + +The header looks like: + +Offset Proto Name Meaning +/Size + +01F1/1 ALL setup_sects The size of the setup in sectors +01F2/2 ALL root_flags If set, the root is mounted readonly +01F4/2 ALL syssize DO NOT USE - for bootsect.S use only +01F6/2 ALL swap_dev DO NOT USE - obsolete +01F8/2 ALL ram_size DO NOT USE - for bootsect.S use only +01FA/2 ALL vid_mode Video mode control +01FC/2 ALL root_dev Default root device number +01FE/2 ALL boot_flag 0xAA55 magic number +0200/2 2.00+ jump Jump instruction +0202/4 2.00+ header Magic signature "HdrS" +0206/2 2.00+ version Boot protocol version supported +0208/4 2.00+ realmode_swtch Boot loader hook (see below) +020C/2 2.00+ start_sys The load-low segment (0x1000) (obsolete) +020E/2 2.00+ kernel_version Pointer to kernel version string +0210/1 2.00+ type_of_loader Boot loader identifier +0211/1 2.00+ loadflags Boot protocol option flags +0212/2 2.00+ setup_move_size Move to high memory size (used with hooks) +0214/4 2.00+ code32_start Boot loader hook (see below) +0218/4 2.00+ ramdisk_image initrd load address (set by boot loader) +021C/4 2.00+ ramdisk_size initrd size (set by boot loader) +0220/4 2.00+ bootsect_kludge DO NOT USE - for bootsect.S use only +0224/2 2.01+ heap_end_ptr Free memory after setup end +0226/2 N/A pad1 Unused +0228/4 2.02+ cmd_line_ptr 32-bit pointer to the kernel command line +022C/4 2.03+ initrd_addr_max Highest legal initrd address + +For backwards compatibility, if the setup_sects field contains 0, the +real value is 4. + +If the "HdrS" (0x53726448) magic number is not found at offset 0x202, +the boot protocol version is "old". Loading an old kernel, the +following parameters should be assumed: + + Image type = zImage + initrd not supported + Real-mode kernel must be located at 0x90000. + +Otherwise, the "version" field contains the protocol version, +e.g. protocol version 2.01 will contain 0x0201 in this field. When +setting fields in the header, you must make sure only to set fields +supported by the protocol version in use. + +The "kernel_version" field, if set to a nonzero value, contains a +pointer to a null-terminated human-readable kernel version number +string, less 0x200. This can be used to display the kernel version to +the user. This value should be less than (0x200*setup_sects). For +example, if this value is set to 0x1c00, the kernel version number +string can be found at offset 0x1e00 in the kernel file. This is a +valid value if and only if the "setup_sects" field contains the value +14 or higher. + +Most boot loaders will simply load the kernel at its target address +directly. Such boot loaders do not need to worry about filling in +most of the fields in the header. The following fields should be +filled out, however: + + vid_mode: + Please see the section on SPECIAL COMMAND LINE OPTIONS. + + type_of_loader: + If your boot loader has an assigned id (see table below), enter + 0xTV here, where T is an identifier for the boot loader and V is + a version number. Otherwise, enter 0xFF here. + + Assigned boot loader ids: + 0 LILO + 1 Loadlin + 2 bootsect-loader + 3 SYSLINUX + 4 EtherBoot + + Please contact <hpa@zytor.com> if you need a bootloader ID + value assigned. + + loadflags, heap_end_ptr: + If the protocol version is 2.01 or higher, enter the + offset limit of the setup heap into heap_end_ptr and set the + 0x80 bit (CAN_USE_HEAP) of loadflags. heap_end_ptr appears to + be relative to the start of setup (offset 0x0200). + + setup_move_size: + When using protocol 2.00 or 2.01, if the real mode + kernel is not loaded at 0x90000, it gets moved there later in + the loading sequence. Fill in this field if you want + additional data (such as the kernel command line) moved in + addition to the real-mode kernel itself. + + ramdisk_image, ramdisk_size: + If your boot loader has loaded an initial ramdisk (initrd), + set ramdisk_image to the 32-bit pointer to the ramdisk data + and the ramdisk_size to the size of the ramdisk data. + + The initrd should typically be located as high in memory as + possible, as it may otherwise get overwritten by the early + kernel initialization sequence. However, it must never be + located above the address specified in the initrd_addr_max + field. The initrd should be at least 4K page aligned. + + cmd_line_ptr: + If the protocol version is 2.02 or higher, this is a 32-bit + pointer to the kernel command line. The kernel command line + can be located anywhere between the end of setup and 0xA0000. + Fill in this field even if your boot loader does not support a + command line, in which case you can point this to an empty + string (or better yet, to the string "auto".) If this field + is left at zero, the kernel will assume that your boot loader + does not support the 2.02+ protocol. + + ramdisk_max: + The maximum address that may be occupied by the initrd + contents. For boot protocols 2.02 or earlier, this field is + not present, and the maximum address is 0x37FFFFFF. (This + address is defined as the address of the highest safe byte, so + if your ramdisk is exactly 131072 bytes long and this field is + 0x37FFFFFF, you can start your ramdisk at 0x37FE0000.) + + +**** THE KERNEL COMMAND LINE + +The kernel command line has become an important way for the boot +loader to communicate with the kernel. Some of its options are also +relevant to the boot loader itself, see "special command line options" +below. + +The kernel command line is a null-terminated string up to 255 +characters long, plus the final null. + +If the boot protocol version is 2.02 or later, the address of the +kernel command line is given by the header field cmd_line_ptr (see +above.) + +If the protocol version is *not* 2.02 or higher, the kernel +command line is entered using the following protocol: + + At offset 0x0020 (word), "cmd_line_magic", enter the magic + number 0xA33F. + + At offset 0x0022 (word), "cmd_line_offset", enter the offset + of the kernel command line (relative to the start of the + real-mode kernel). + + The kernel command line *must* be within the memory region + covered by setup_move_size, so you may need to adjust this + field. + + +**** SAMPLE BOOT CONFIGURATION + +As a sample configuration, assume the following layout of the real +mode segment: + + 0x0000-0x7FFF Real mode kernel + 0x8000-0x8FFF Stack and heap + 0x9000-0x90FF Kernel command line + +Such a boot loader should enter the following fields in the header: + + unsigned long base_ptr; /* base address for real-mode segment */ + + if ( setup_sects == 0 ) { + setup_sects = 4; + } + + if ( protocol >= 0x0200 ) { + type_of_loader = <type code>; + if ( loading_initrd ) { + ramdisk_image = <initrd_address>; + ramdisk_size = <initrd_size>; + } + if ( protocol >= 0x0201 ) { + heap_end_ptr = 0x9000 - 0x200; + loadflags |= 0x80; /* CAN_USE_HEAP */ + } + if ( protocol >= 0x0202 ) { + cmd_line_ptr = base_ptr + 0x9000; + } else { + cmd_line_magic = 0xA33F; + cmd_line_offset = 0x9000; + setup_move_size = 0x9100; + } + } else { + /* Very old kernel */ + + cmd_line_magic = 0xA33F; + cmd_line_offset = 0x9000; + + /* A very old kernel MUST have its real-mode code + loaded at 0x90000 */ + + if ( base_ptr != 0x90000 ) { + /* Copy the real-mode kernel */ + memcpy(0x90000, base_ptr, (setup_sects+1)*512); + /* Copy the command line */ + memcpy(0x99000, base_ptr+0x9000, 256); + + base_ptr = 0x90000; /* Relocated */ + } + + /* It is recommended to clear memory up to the 32K mark */ + memset(0x90000 + (setup_sects+1)*512, 0, + (64-(setup_sects+1))*512); + } + + +**** LOADING THE REST OF THE KERNEL + +The non-real-mode kernel starts at offset (setup_sects+1)*512 in the +kernel file (again, if setup_sects == 0 the real value is 4.) It +should be loaded at address 0x10000 for Image/zImage kernels and +0x100000 for bzImage kernels. + +The kernel is a bzImage kernel if the protocol >= 2.00 and the 0x01 +bit (LOAD_HIGH) in the loadflags field is set: + + is_bzImage = (protocol >= 0x0200) && (loadflags & 0x01); + load_address = is_bzImage ? 0x100000 : 0x10000; + +Note that Image/zImage kernels can be up to 512K in size, and thus use +the entire 0x10000-0x90000 range of memory. This means it is pretty +much a requirement for these kernels to load the real-mode part at +0x90000. bzImage kernels allow much more flexibility. + + +**** SPECIAL COMMAND LINE OPTIONS + +If the command line provided by the boot loader is entered by the +user, the user may expect the following command line options to work. +They should normally not be deleted from the kernel command line even +though not all of them are actually meaningful to the kernel. Boot +loader authors who need additional command line options for the boot +loader itself should get them registered in +Documentation/kernel-parameters.txt to make sure they will not +conflict with actual kernel options now or in the future. + + vga=<mode> + <mode> here is either an integer (in C notation, either + decimal, octal, or hexadecimal) or one of the strings + "normal" (meaning 0xFFFF), "ext" (meaning 0xFFFE) or "ask" + (meaning 0xFFFD). This value should be entered into the + vid_mode field, as it is used by the kernel before the command + line is parsed. + + mem=<size> + <size> is an integer in C notation optionally followed by K, M + or G (meaning << 10, << 20 or << 30). This specifies the end + of memory to the kernel. This affects the possible placement + of an initrd, since an initrd should be placed near end of + memory. Note that this is an option to *both* the kernel and + the bootloader! + + initrd=<file> + An initrd should be loaded. The meaning of <file> is + obviously bootloader-dependent, and some boot loaders + (e.g. LILO) do not have such a command. + +In addition, some boot loaders add the following options to the +user-specified command line: + + BOOT_IMAGE=<file> + The boot image which was loaded. Again, the meaning of <file> + is obviously bootloader-dependent. + + auto + The kernel was booted without explicit user intervention. + +If these options are added by the boot loader, it is highly +recommended that they are located *first*, before the user-specified +or configuration-specified command line. Otherwise, "init=/bin/sh" +gets confused by the "auto" option. + + +**** RUNNING THE KERNEL + +The kernel is started by jumping to the kernel entry point, which is +located at *segment* offset 0x20 from the start of the real mode +kernel. This means that if you loaded your real-mode kernel code at +0x90000, the kernel entry point is 9020:0000. + +At entry, ds = es = ss should point to the start of the real-mode +kernel code (0x9000 if the code is loaded at 0x90000), sp should be +set up properly, normally pointing to the top of the heap, and +interrupts should be disabled. Furthermore, to guard against bugs in +the kernel, it is recommended that the boot loader sets fs = gs = ds = +es = ss. + +In our example from above, we would do: + + /* Note: in the case of the "old" kernel protocol, base_ptr must + be == 0x90000 at this point; see the previous sample code */ + + seg = base_ptr >> 4; + + cli(); /* Enter with interrupts disabled! */ + + /* Set up the real-mode kernel stack */ + _SS = seg; + _SP = 0x9000; /* Load SP immediately after loading SS! */ + + _DS = _ES = _FS = _GS = seg; + jmp_far(seg+0x20, 0); /* Run the kernel */ + +If your boot sector accesses a floppy drive, it is recommended to +switch off the floppy motor before running the kernel, since the +kernel boot leaves interrupts off and thus the motor will not be +switched off, especially if the loaded kernel has the floppy driver as +a demand-loaded module! + + +**** ADVANCED BOOT TIME HOOKS + +If the boot loader runs in a particularly hostile environment (such as +LOADLIN, which runs under DOS) it may be impossible to follow the +standard memory location requirements. Such a boot loader may use the +following hooks that, if set, are invoked by the kernel at the +appropriate time. The use of these hooks should probably be +considered an absolutely last resort! + +IMPORTANT: All the hooks are required to preserve %esp, %ebp, %esi and +%edi across invocation. + + realmode_swtch: + A 16-bit real mode far subroutine invoked immediately before + entering protected mode. The default routine disables NMI, so + your routine should probably do so, too. + + code32_start: + A 32-bit flat-mode routine *jumped* to immediately after the + transition to protected mode, but before the kernel is + uncompressed. No segments, except CS, are set up; you should + set them up to KERNEL_DS (0x18) yourself. + + After completing your hook, you should jump to the address + that was in this field before your boot loader overwrote it. diff --git a/doc/linux-i386-zero-page.txt b/doc/linux-i386-zero-page.txt new file mode 100644 index 0000000..e2582a6 --- /dev/null +++ b/doc/linux-i386-zero-page.txt @@ -0,0 +1,78 @@ +Summary of boot_params layout (kernel point of view) + ( collected by Hans Lermen and Martin Mares ) + +The contents of boot_params are used to pass parameters from the +16-bit realmode code of the kernel to the 32-bit part. References/settings +to it mainly are in: + + arch/i386/boot/setup.S + arch/i386/boot/video.S + arch/i386/kernel/head.S + arch/i386/kernel/setup.c + + +Offset Type Description +------ ---- ----------- + 0 32 bytes struct screen_info, SCREEN_INFO + ATTENTION, overlaps the following !!! + 2 unsigned short EXT_MEM_K, extended memory size in Kb (from int 0x15) + 0x20 unsigned short CL_MAGIC, commandline magic number (=0xA33F) + 0x22 unsigned short CL_OFFSET, commandline offset + Address of commandline is calculated: + 0x90000 + contents of CL_OFFSET + (only taken, when CL_MAGIC = 0xA33F) + 0x40 20 bytes struct apm_bios_info, APM_BIOS_INFO + 0x60 16 bytes Intel SpeedStep (IST) BIOS support information + 0x80 16 bytes hd0-disk-parameter from intvector 0x41 + 0x90 16 bytes hd1-disk-parameter from intvector 0x46 + + 0xa0 16 bytes System description table truncated to 16 bytes. + ( struct sys_desc_table_struct ) + 0xb0 - 0x1c3 Free. Add more parameters here if you really need them. + +0x1c4 unsigned long EFI system table pointer +0x1c8 unsigned long EFI memory descriptor size +0x1cc unsigned long EFI memory descriptor version +0x1d0 unsigned long EFI memory descriptor map pointer +0x1d4 unsigned long EFI memory descriptor map size +0x1e0 unsigned long ALT_MEM_K, alternative mem check, in Kb +0x1e8 char number of entries in E820MAP (below) +0x1e9 unsigned char number of entries in EDDBUF (below) +0x1ea unsigned char number of entries in EDD_MBR_SIG_BUFFER (below) +0x1f1 char size of setup.S, number of sectors +0x1f2 unsigned short MOUNT_ROOT_RDONLY (if !=0) +0x1f4 unsigned short size of compressed kernel-part in the + (b)zImage-file (in 16 byte units, rounded up) +0x1f6 unsigned short swap_dev (unused AFAIK) +0x1f8 unsigned short RAMDISK_FLAGS +0x1fa unsigned short VGA-Mode (old one) +0x1fc unsigned short ORIG_ROOT_DEV (high=Major, low=minor) +0x1ff char AUX_DEVICE_INFO + +0x200 short jump to start of setup code aka "reserved" field. +0x202 4 bytes Signature for SETUP-header, ="HdrS" +0x206 unsigned short Version number of header format + Current version is 0x0201... +0x208 8 bytes (used by setup.S for communication with boot loaders, + look there) +0x210 char LOADER_TYPE, = 0, old one + else it is set by the loader: + 0xTV: T=0 for LILO + 1 for Loadlin + 2 for bootsect-loader + 3 for SYSLINUX + 4 for ETHERBOOT + V = version +0x211 char loadflags: + bit0 = 1: kernel is loaded high (bzImage) + bit7 = 1: Heap and pointer (see below) set by boot + loader. +0x212 unsigned short (setup.S) +0x214 unsigned long KERNEL_START, where the loader started the kernel +0x218 unsigned long INITRD_START, address of loaded ramdisk image +0x21c unsigned long INITRD_SIZE, size in bytes of ramdisk image +0x220 4 bytes (setup.S) +0x224 unsigned short setup.S heap end pointer +0x290 - 0x2cf EDD_MBR_SIG_BUFFER (edd.S) +0x2d0 - 0x600 E820MAP +0xd00 - 0xeec EDDBUF (edd.S) for edd data diff --git a/doc/multiboot.html b/doc/multiboot.html new file mode 100644 index 0000000..bd41444 --- /dev/null +++ b/doc/multiboot.html @@ -0,0 +1,664 @@ +<HTML> + +<HEAD> +<TITLE>Multiboot Standard</TITLE> +</HEAD> + +<BODY> + +<CENTER><H1>Multiboot Standard</H1></CENTER> +<CENTER><H3>Version 0.6</H3></CENTER> + +<HR> + +<H2>Contents</H2> + +<UL> +<LI> <A HREF="#motivation">Motivation</A> +<LI> <A HREF="#terminology">Terminology</A> +<LI> <A HREF="#scope">Scope and Requirements</A> +<LI> <A HREF="#details">Details</A> +<LI> <A HREF="#author">Authors</A> +<LI> <B>NOTE: The following items are not part of the standards document, +but are included for prospective OS and bootloader writers.</B> +<LI> <A HREF="#notes">Notes on PCs</A> +<LI> <A HREF="#example_os">Example OS Code</A> +<LI> <A HREF="#example_boot">Example Bootloader Code</A> +</UL> + +<HR> + +<H2><A NAME="motivation">Motivation</A></H2> + +Every OS ever created tends to have its own boot loader. Installing a new +OS on a machine generally involves installing a whole new set of boot +mechanisms, each with completely different install-time and boot-time user +interfaces. Getting multiple operating systems to coexist reliably on one +machine through typical "chaining" mechanisms can be a nightmare. There is +little or no choice of boot loaders for a particular operating system - if +the one that comes with the OS doesn't do exactly what you want, or doesn't +work on your machine, you're screwed.<P> + +While we may not be able to fix this problem in existing commercial +operating systems, it shouldn't be too difficult for a few people in the +free OS communities to put their heads together and solve this problem for +the popular free operating systems. That's what this standard aims for. +Basically, it specifies an interface between a boot loader and a operating +system, such that any complying boot loader should be able to load any +complying operating system. This standard does NOT specify how boot +loaders should work - only how they must interface with the OS being +loaded.<P> + +<HR> + +<H2><A NAME="terminology">Terminology</A></H2> + +Throughout this document, the term "boot loader" means whatever program or +set of programs loads the image of the final operating system to be run on +the machine. The boot loader may itself consist of several stages, but +that is an implementation detail not relevant to this standard. Only the +"final" stage of the boot loader - the stage that eventually transfers +control to the OS - needs to follow the rules specified in this document +in order to be "MultiBoot compliant"; earlier boot loader stages can be +designed in whatever way is most convenient.<P> + +The term "OS image" is used to refer to the initial binary image that the +boot loader loads into memory and transfers control to to start the OS. +The OS image is typically an executable containing the OS kernel.<P> + +The term "boot module" refers to other auxiliary files that the boot loader +loads into memory along with the OS image, but does not interpret in any +way other than passing their locations to the OS when it is invoked.<P> + +<HR> + +<H2><A NAME="scope">Scope and Requirements</A></H2> + +<H3>Architectures</H3> + +This standard is primarily targetted at PC's, since they are the most +common and have the largest variety of OS's and boot loaders. However, to +the extent that certain other architectures may need a boot standard and do +not have one already, a variation of this standard, stripped of the +x86-specific details, could be adopted for them as well.<P> + +<H3>Operating systems</H3> + +This standard is targetted toward free 32-bit operating systems that can be +fairly easily modified to support the standard without going through lots of +bureaucratic rigmarole. The particular free OS's that this standard is +being primarily designed for are Linux, FreeBSD, NetBSD, Mach, and VSTa. +It is hoped that other emerging free OS's will adopt it from the start, and +thus immediately be able to take advantage of existing boot loaders. It +would be nice if commercial operating system vendors eventually adopted +this standard as well, but that's probably a pipe dream.<P> + +<H3>Boot sources</H3> + +It should be possible to write compliant boot loaders that +load the OS image from a variety of sources, including floppy disk, hard +disk, and across a network.<P> + +Disk-based boot loaders may use a variety of techniques to find the +relevant OS image and boot module data on disk, such as by interpretation +of specific file systems (e.g. the BSD/Mach boot loader), using +precalculated "block lists" (e.g. LILO), loading from a special "boot +partition" (e.g. OS/2), or even loading from within another operating +system (e.g. the VSTa boot code, which loads from DOS). Similarly, +network-based boot loaders could use a variety of network hardware and +protocols.<P> + +It is hoped that boot loaders will be created that support multiple loading +mechanisms, increasing their portability, robustness, and +user-friendliness.<P> + +<H3>Boot-time configuration</H3> + +It is often necessary for one reason or another for the user to be able to +provide some configuration information to the OS dynamically at boot time. +While this standard should not dictate how this configuration information +is obtained by the boot loader, it should provide a standard means for the +boot loader to pass such information to the OS.<P> + +<H3>Convenience to the OS</H3> + +OS images should be easy to generate. Ideally, an OS image should simply +be an ordinary 32-bit executable file in whatever file format the OS +normally uses. It should be possible to 'nm' or disassemble OS images just +like normal executables. Specialized tools should not be needed to create +OS images in a "special" file format. If this means shifting some work +from the OS to the boot loader, that is probably appropriate, because all +the memory consumed by the boot loader will typically be made available +again after the boot process is created, whereas every bit of code in the +OS image typically has to remain in memory forever. The OS should not have +to worry about getting into 32-bit mode initially, because mode switching +code generally needs to be in the boot loader anyway in order to load OS +data above the 1MB boundary, and forcing the OS to do this makes creation +of OS images much more difficult.<P> + +Unfortunately, there is a horrendous variety of executable file formats +even among free Unix-like PC-based OS's - generally a different format for +each OS. Most of the relevant free OS's use some variant of a.out format, +but some are moving to ELF. It is highly desirable for boot loaders not to +have to be able to interpret all the different types of executable file +formats in existence in order to load the OS image - otherwise the boot +loader effectively becomes OS-specific again.<P> + +This standard adopts a compromise solution to this problem. +MultiBoot compliant boot images always either (a) are in ELF format, or (b) +contain a "magic MultiBoot header", described below, which allows the boot +loader to load the image without having to understand numerous a.out +variants or other executable formats. This magic header does not need +to be at the very beginning of the executable file, so kernel images can +still conform to the local a.out format variant in addition to being +MultiBoot compliant.<P> + +<H3>Boot modules</H3> + +Many modern operating system kernels, such as those of VSTa and Mach, do +not by themselves contain enough mechanism to get the system fully +operational: they require the presence of additional software modules at +boot time in order to access devices, mount file systems, etc. While these +additional modules could be embedded in the main OS image along with the +kernel itself, and the resulting image be split apart manually by the OS +when it receives control, it is often more flexible, more space-efficient, +and more convenient to the OS and user if the boot loader can load these +additional modules independently in the first place.<P> + +Thus, this standard should provide a standard method for a boot loader to +indicate to the OS what auxiliary boot modules were loaded, and where they +can be found. Boot loaders don't have to support multiple boot modules, +but they are strongly encouraged to, because some OS's will be unable to +boot without them.<P> + +<HR> + +<H2><A NAME="details">Details</H2> + +There are three main aspects of the boot-loader/OS image interface this +standard must specify:<P> + +<UL> +<LI>The format of the OS image as seen by the boot loader. +<LI>The state of the machine when the boot loader starts the OS. +<LI>The format of the information passed by the boot loader to the OS. +</UL> + +<H3>OS Image Format</H3> + +An OS image is generally just an ordinary 32-bit executable file in the +standard format for that particular OS, except that it may be linked at a +non-default load address to avoid loading on top of the PC's I/O region +or other reserved areas, and of course it can't use shared libraries or +other fancy features. Initially, only images in a.out format are +supported; ELF support will probably later be specified in the standard.<P> + +Unfortunately, the exact meaning of the text, data, bss, and entry fields +of a.out headers tends to vary widely between different executable flavors, +and it is sometimes very difficult to distinguish one flavor from another +(e.g. Linux ZMAGIC executables and Mach ZMAGIC executables). Furthermore, +there is no simple, reliable way of determining at what address in memory +the text segment is supposed to start. Therefore, this standard requires +that an additional header, known as a 'multiboot_header', appear somewhere +near the beginning of the executable file. In general it should come "as +early as possible", and is typically embedded in the beginning of the text +segment after the "real" executable header. It _must_ be contained +completely within the first 8192 bytes of the executable file, and must be +longword (32-bit) aligned. These rules allow the boot loader to find and +synchronize with the text segment in the a.out file without knowing +beforehand the details of the a.out variant. The layout of the header is +as follows:<P> + +<pre> + +-------------------+ +0 | magic: 0x1BADB002 | (required) +4 | flags | (required) +8 | checksum | (required) + +-------------------+ +8 | header_addr | (present if flags[16] is set) +12 | load_addr | (present if flags[16] is set) +16 | load_end_addr | (present if flags[16] is set) +20 | bss_end_addr | (present if flags[16] is set) +24 | entry_addr | (present if flags[16] is set) + +-------------------+ +</pre> + +All fields are in little-endian byte order, of course. The first field is +the magic number identifying the header, which must be the hex value +0x1BADB002.<P> + +The flags field specifies features that the OS image requests or requires +of the boot loader. Bits 0-15 indicate requirements; if the boot loader +sees any of these bits set but doesn't understand the flag or can't fulfill +the requirements it indicates for some reason, it must notify the user and +fail to load the OS image. Bits 16-31 indicate optional features; if any +bits in this range are set but the boot loader doesn't understand them, it +can simply ignore them and proceed as usual. Naturally, all +as-yet-undefined bits in the flags word must be set to zero in OS +images. This way, the flags fields serves for version control as well as +simple feature selection.<P> + +If bit 0 in the flags word is set, then all boot modules loaded along with +the OS must be aligned on page (4KB) boundaries. Some OS's expect to be +able to map the pages containing boot modules directly into a paged address +space during startup, and thus need the boot modules to be page-aligned.<P> + +If bit 1 in the flags word is set, then information on available memory +via at least the 'mem_*' fields of the multiboot_info structure defined +below must be included. If the bootloader is capable of passing a memory +map (the 'mmap_*' fields) and one exists, then it must be included as +well.<P> + +If bit 16 in the flags word is set, then the fields at offsets 8-24 in the +multiboot_header are valid, and the boot loader should use them instead of +the fields in the actual executable header to calculate where to load the +OS image. This information does not need to be provided if the kernel +image is in ELF format, but it should be provided if the images is in a.out +format or in some other format. Compliant boot loaders must be able to +load images that either are in ELF format or contain the load address +information embedded in the multiboot_header; they may also directly +support other executable formats, such as particular a.out variants, but +are not required to.<P> + +All of the address fields enabled by flag bit 16 are physical addresses. +The meaning of each is as follows:<P> + +<UL> +<LI><B>header_addr</B> -- Contains the address corresponding to the +beginning of the multiboot_header - the physical memory location at which +the magic value is supposed to be loaded. This field serves to "synchronize" +the mapping between OS image offsets and physical memory addresses. +<LI><B>load_addr</B> -- Contains the physical address of the beginning +of the text segment. The offset in the OS image file at which to start +loading is defined by the offset at which the header was found, minus +(header_addr - load_addr). load_addr must be less than or equal to +header_addr. +<LI><B>load_end_addr</B> -- Contains the physical address of the end of the +data segment. (load_end_addr - load_addr) specifies how much data to load. +This implies that the text and data segments must be consecutive in the +OS image; this is true for existing a.out executable formats. +<LI><B>bss_end_addr</B> -- Contains the physical address of the end of +the bss segment. The boot loader initializes this area to zero, and +reserves the memory it occupies to avoid placing boot modules and other +data relevant to the OS in that area. +<LI><B>entry</B> -- The physical address to which the boot loader should +jump in order to start running the OS. +</UL> + +The checksum is a 32-bit unsigned value which, when added to +the other required fields, must have a 32-bit unsigned sum of zero.<P> + +<H3>Machine State</H3> + +When the boot loader invokes the 32-bit operating system, +the machine must have the following state:<P> + +<UL> +<LI>CS must be a 32-bit read/execute code segment with an offset of 0 +and a limit of 0xffffffff. +<LI>DS, ES, FS, GS, and SS must be a 32-bit read/write data segment with +an offset of 0 and a limit of 0xffffffff. +<LI>The address 20 line must be usable for standard linear 32-bit +addressing of memory (in standard PC hardware, it is wired to +0 at bootup, forcing addresses in the 1-2 MB range to be mapped to the +0-1 MB range, 3-4 is mapped to 2-3, etc.). +<LI>Paging must be turned off. +<LI>The processor interrupt flag must be turned off. +<LI>EAX must contain the magic value 0x2BADB002; the presence of this value +indicates to the OS that it was loaded by a MultiBoot-compliant boot +loader (e.g. as opposed to another type of boot loader that the OS can +also be loaded from). +<LI>EBX must contain the 32-bit physical address of the multiboot_info +structure provided by the boot loader (see below). +</UL> + +All other processor registers and flag bits are undefined. This includes, +in particular:<P> + +<UL> +<LI>ESP: the 32-bit OS must create its own stack as soon as it needs one. +<LI>GDTR: Even though the segment registers are set up as described above, +the GDTR may be invalid, so the OS must not load any segment registers +(even just reloading the same values!) until it sets up its own GDT. +<LI>IDTR: The OS must leave interrupts disabled until it sets up its own IDT. +</UL> + +However, other machine state should be left by the boot loader in "normal +working order", i.e. as initialized by the BIOS (or DOS, if that's what +the boot loader runs from). In other words, the OS should be able to make +BIOS calls and such after being loaded, as long as it does not overwrite +the BIOS data structures before doing so. Also, the boot loader must leave +the PIC programmed with the normal BIOS/DOS values, even if it changed them +during the switch to 32-bit mode.<P> + +<H3>Boot Information Format</H3> + +Upon entry to the OS, the EBX register contains the physical address of +a 'multiboot_info' data structure, through which the boot loader +communicates vital information to the OS. The OS can use or ignore any +parts of the structure as it chooses; all information passed by the boot +loader is advisory only.<P> + +The multiboot_info structure and its related substructures may be placed +anywhere in memory by the boot loader (with the exception of the memory +reserved for the kernel and boot modules, of course). It is the OS's +responsibility to avoid overwriting this memory until it is done using it.<P> + +The format of the multiboot_info structure (as defined so far) follows:<P> + +<pre> + +-------------------+ +0 | flags | (required) + +-------------------+ +4 | mem_lower | (present if flags[0] is set) +8 | mem_upper | (present if flags[0] is set) + +-------------------+ +12 | boot_device | (present if flags[1] is set) + +-------------------+ +16 | cmdline | (present if flags[2] is set) + +-------------------+ +20 | mods_count | (present if flags[3] is set) +24 | mods_addr | (present if flags[3] is set) + +-------------------+ +28 - 40 | syms | (present if flags[4] or flags[5] is set) + +-------------------+ +44 | mmap_length | (present if flags[6] is set) +48 | mmap_addr | (present if flags[6] is set) + +-------------------+ +</pre> + +The first longword indicates the presence and validity of other fields in +the multiboot_info structure. All as-yet-undefined bits must be set to +zero by the boot loader. Any set bits that the OS does not understand +should be ignored. Thus, the flags field also functions as a version +indicator, allowing the multiboot_info structure to be expanded in the +future without breaking anything.<P> + +If bit 0 in the multiboot_info.flags word is set, then the 'mem_*' fields +are valid. 'mem_lower' and 'mem_upper' indicate the amount of lower and upper +memory, respectively, in kilobytes. Lower memory starts at address 0, and +upper memory starts at address 1 megabyte. The maximum possible +value for lower memory is 640 kilobytes. The value returned for upper +memory is maximally the address of the first upper memory hole minus +1 megabyte. It is not guaranteed to be this value.<P> + +If bit 1 in the multiboot_info.flags word is set, then the 'boot_device' +field is valid, and indicates which BIOS disk device the boot loader loaded +the OS from. If the OS was not loaded from a BIOS disk, then this field +must not be present (bit 3 must be clear). The OS may use this field as a +hint for determining its own "root" device, but is not required to. The +boot_device field is layed out in four one-byte subfields as follows:<P> + +<pre> + +-------+-------+-------+-------+ + | drive | part1 | part2 | part3 | + +-------+-------+-------+-------+ +</pre> + +The first byte contains the BIOS drive number as understood by the BIOS +INT 0x13 low-level disk interface: e.g. 0x00 for the first floppy disk or +0x80 for the first hard disk.<P> + +The three remaining bytes specify the boot partition. 'part1' specifies +the "top-level" partition number, 'part2' specifies a "sub-partition" in +the top-level partition, etc. Partition numbers always start from zero. +Unused partition bytes must be set to 0xFF. For example, if the disk is +partitioned using a simple one-level DOS partitioning scheme, then 'part1' +contains the DOS partition number, and 'part2' and 'part3' are both zero. +As another example, if a disk is partitioned first into DOS partitions, and +then one of those DOS partitions is subdivided into several BSD partitions +using BSD's "disklabel" strategy, then 'part1' contains the DOS partition +number, 'part2' contains the BSD sub-partition within that DOS partition, +and 'part3' is 0xFF.<P> + +DOS extended partitions are indicated as partition numbers starting from 4 +and increasing, rather than as nested sub-partitions, even though the +underlying disk layout of extended partitions is hierarchical in nature. +For example, if the boot loader boots from the second extended partition +on a disk partitioned in conventional DOS style, then 'part1' will be 5, +and 'part2' and 'part3' will both be 0xFF.<P> + +If bit 2 of the flags longword is set, the 'cmdline' field is valid, and +contains the physical address of the the command line to be passed to the +kernel. The command line is a normal C-style null-terminated string.<P> + +If bit 3 of the flags is set, then the 'mods' fields indicate to the kernel +what boot modules were loaded along with the kernel image, and where they +can be found. 'mods_count' contains the number of modules loaded; +'mods_addr' contains the physical address of the first module structure. +'mods_count' may be zero, indicating no boot modules were loaded, even if +bit 1 of 'flags' is set. Each module structure is formatted as follows:<P> + +<pre> + +-------------------+ +0 | mod_start | +4 | mod_end | + +-------------------+ +8 | string | + +-------------------+ +12 | reserved (0) | + +-------------------+ +</pre> + +The first two fields contain the start and end addresses of the boot module +itself. The 'string' field provides an arbitrary string to be associated +with that particular boot module; it is a null-terminated ASCII string, +just like the kernel command line. The 'string' field may be 0 if there is +no string associated with the module. Typically the string might be a +command line (e.g. if the OS treats boot modules as executable programs), +or a pathname (e.g. if the OS treats boot modules as files in a file +system), but its exact use is specific to the OS. The 'reserved' field +must be set to 0 by the boot loader and ignored by the OS.<P> + +NOTE: Bits 4 & 5 are mutually exclusive.<P> + +If bit 4 in the multiboot_info.flags word is set, then the following +fields in the multiboot_info structure starting at byte 28 are valid:<P> + +<pre> + +-------------------+ +28 | tabsize | +32 | strsize | +36 | addr | +40 | reserved (0) | + +-------------------+ +</pre> + +These indicate where the symbol table from an a.out kernel image can be +found. 'addr' is the physical address of the size (4-byte unsigned +long) of an array of a.out-format 'nlist' structures, followed immediately +by the array itself, then the size (4-byte unsigned long) of a set of +null-terminated ASCII strings (plus sizeof(unsigned long) in this case), +and finally the set of strings itself. 'tabsize' is equal to it's size +parameter (found at the beginning of the symbol section), and 'strsize' +is equal to it's size parameter (found at the beginning of the string section) +of the following string table to which the symbol table refers. Note that +'tabsize' may be 0, indicating no symbols, even if bit 4 in the flags +word is set.<P> + +If bit 5 in the multiboot_info.flags word is set, then the following +fields in the multiboot_info structure starting at byte 28 are valid:<P> + +<pre> + +-------------------+ +28 | num | +32 | size | +36 | addr | +40 | shndx | + +-------------------+ +</pre> + +These indicate where the section header table from an ELF kernel is, the +size of each entry, number of entries, and the string table used as the +index of names. They correspond to the 'shdr_*' entries ('shdr_num', etc.) +in the Executable and Linkable Format (ELF) specification in the program +header. All sections are loaded, and the physical address fields +of the elf section header then refer to where the sections are in memory +(refer to the i386 ELF documentation for details as to how to read the +section header(s)). Note that 'shdr_num' may be 0, indicating no symbols, +even if bit 5 in the flags word is set.<P> + +If bit 6 in the multiboot_info.flags word is set, then the 'mmap_*' fields +are valid, and indicate the address and length of a buffer containing a +memory map of the machine provided by the BIOS. 'mmap_addr' is the address, +and 'mmap_length' is the total size of the buffer. The buffer consists of +one or more of the following size/structure pairs ('size' is really used +for skipping to the next pair):<P> + +<pre> + +-------------------+ +-4 | size | + +-------------------+ +0 | BaseAddrLow | +4 | BaseAddrHigh | +8 | LengthLow | +12 | LengthHigh | +16 | Type | + +-------------------+ +</pre> + +where 'size' is the size of the associated structure in bytes, which can +be greater than the minimum of 20 bytes. 'BaseAddrLow' is the lower 32 +bits of the starting address, and 'BaseAddrHigh' is the upper 32 bits, +for a total of a 64-bit starting address. 'LengthLow' is the lower 32 bits +of the size of the memory region in bytes, and 'LengthHigh' is the upper 32 +bits, for a total of a 64-bit length. 'Type' is the variety of address +range represented, where a value of 1 indicates available RAM, and all +other values currently indicated a reserved area.<P> + +The map provided is guaranteed to list all standard RAM that should +be available for normal use.<P> + +<HR> + +<H2><A NAME="author">Authors</A></H2> + +<pre> +Bryan Ford +Computer Systems Laboratory +University of Utah +Salt Lake City, UT 84112 +(801) 581-4280 +baford@cs.utah.edu + +Erich Stefan Boleyn +924 S.W. 16th Ave, #202 +Portland, OR, USA 97205 +(503) 226-0741 +erich@uruk.org +</pre> + +We would also like to thank the many other people have provided comments, +ideas, information, and other forms of support for our work.<P> + +<H3>Revision History</H3> + +<pre> +Version 0.6 3/29/96 (a few wording changes, header checksum, and + clarification of machine state passed to the OS) +Version 0.5 2/23/96 (name change) +Version 0.4 2/1/96 (major changes plus HTMLification) +Version 0.3 12/23/95 +Version 0.2 10/22/95 +Version 0.1 6/26/95 +</pre> + +<HR> + +<H2><A NAME="notes">Notes on PCs</A></H2> + +In reference to bit 0 of the multiboot_info.flags parameter, +if the bootloader +in question uses older BIOS interfaces, or the newest ones are not +available (see description about bit 6), then a maximum of either +15 or 63 megabytes of memory may be reported. It is HIGHLY recommended +that bootloaders perform a thorough memory probe.<P> + +In reference to bit 1 of the multiboot_info.flags parameter, it is +recognized that determination of which BIOS drive maps to which +OS-level device-driver is non-trivial, at best. Many kludges have +been made to various OSes instead of solving this problem, most of +them breaking under many conditions. To encourage the use of +general-purpose solutions to this problem, here are 2 +<A HREF=bios_mapping.txt>BIOS Device Mapping Techniques</A>.<P> + +In reference to bit 6 of the multiboot_info.flags parameter, it is +important to note that the data structure used there +(starting with 'BaseAddrLow') is the data returned by the +<A HREF=mem64mb.html>INT 15h, AX=E820h +- Query System Address Map</A> call. More information +on reserved memory regions is defined on that web page. +The interface here is meant to allow a bootloader to +work unmodified with any reasonable extensions of the BIOS interface, +passing along any extra data to be interpreted by the OS as desired.<P> + +<HR> + +<H2><A NAME="example_os">Example OS Code</A> (from Bryan Ford)</H2> + +EDITOR'S NOTE: These examples are relevant to the Proposal version 0.5, +which is basically identical except for the multiboot OS header, which was +missing the checksum. A patch to bring Mach4 UK22 up to version 0.6 is +available in the GRUB FTP area mentioned in the +<A HREF="#example_boot">Example Bootloader Code</A> section below.<P> + +The Mach 4 distribution, available by anonymous FTP from +flux.cs.utah.edu:/flux, contains a C header file that defines the +MultiBoot data structures described above; anyone is welcome to rip it +out and use it for other boot loaders and OS's:<P> + +<pre> + mach4-i386/include/mach/machine/multiboot.h +</pre> + +This distribution also contains code implementing a "Linux boot adaptor", +which collects a MultiBoot-compliant OS image and an optional set of boot +modules, compresses them, and packages them into a single traditional Linux +boot image that can be loaded from LILO or other Linux boot loaders. There +is also a corresponding "BSD boot adaptor" which can be used to wrap a +MultiBoot kernel and set of modules and produce an image that can be loaded +from the FreeBSD and NetBSD boot loaders. All of this code can be used as-is +or as a basis for other boot loaders. These are the directories of primary +relevance:<P> + +<pre> + mach4-i386/boot + mach4-i386/boot/bsd + mach4-i386/boot/linux +</pre> + +The Mach kernel itself in this distribution contains code that demonstrates +how to create a compliant OS. The following files are of primary +relevance:<P> + +<pre> + mach4-i386/kernel/i386at/boothdr.S + mach4-i386/kernel/i386at/model_dep.c +</pre> + +Finally, I have created patches against the Linux 1.2.2 and FreeBSD 2.0 +kernels, in order to make them compliant with this proposed standard. +These patches are available in kahlua.cs.utah.edu:/private/boot.<P> + +<HR> + +<H2><A NAME"example_boot">Example Bootloader Code</A> (from Erich Boleyn)</H2> + +The <A HREF=http://www.uruk.org/grub/>GRUB</A> bootloader project +will be fully +Multiboot-compliant, supporting all required and optional +features present in this standard.<P> + +A final release has not been made, but both the GRUB beta release +(which is quite stable) and a patch for Multiboot version 0.6 for +Mach4 UK22 are available in the GRUB +<A HREF=ftp://ftp.uruk.org/public/grub/>public release</A> +area.<P> + +<HR> + +<A HREF=mailto:erich@uruk.org><I>erich@uruk.org</I></A><P> + +</BODY> +</HTML> + diff --git a/doc/nbi-spec.txt b/doc/nbi-spec.txt new file mode 100644 index 0000000..320f025 --- /dev/null +++ b/doc/nbi-spec.txt @@ -0,0 +1,660 @@ + Draft Net Boot Image Proposal 0.3 + Jamie Honan and Gero Kuhlmann, gero@minix.han.de + June 15, 1997 + + This is the specification of the "tagged image" format + ______________________________________________________________________ + + Table of Contents + + + 1. Note + + 2. Preamble - the why + + 3. The target + + 4. Net Boot Process Description. + + 5. Image Format with Initial Magic Number. + + 6. Boot prom entry points. + + 7. Example of a boot image. + + 8. Terms + + 9. References + + + + ______________________________________________________________________ + + 11.. NNoottee + + + In order to provide more functionality to the boot rom code I changed + Jamie's draft a little bit. All my changes are preceded and followed + by ((ggkk)). + + + + Gero Kuhlmann + + + 22.. PPrreeaammbbllee -- tthhee wwhhyy + + + Whilst researching what other boot proms do (at least those + implementing TCP/IP protocols) it is clear that each 'does their own + thing' in terms of what they expect in a boot image. + + + + If we could all agree on working toward an open standard, O/S + suppliers and boot rom suppliers can build their products to this + norm, and be confident that they will work with each other. + + + + This is a description of how I will implement the boot rom for Linux. + I believe it to be flexible enough for any OS that will be loaded + when a PC boots from a network in the TCP/IP environment. + + + + + It would be good if this could be turned into some form of standard. + + + + This is very much a first draft. I am inviting comment. + + + + The ideas presented here should be independant of any implementation. + In the end, where there is a conflict between the final of this draft, + and an implementation, this description should prevail. + + + + The terms I use are defined at the end. + + + + ((ggkk))IMPORTANT NOTE: The scope of this document starts at the point + where the net boot process gains control from the BIOS, to where the + booted image reaches a state from which there is no return to the net + boot program possible.((ggkk)) + + + 33.. TThhee ttaarrggeett + + + The target is to have a PC retrieve a boot image from a network in the + TCP/IP environment. + + + + ((ggkk))The boot may take place from a network adaptor rom, from a boot + floppy.((ggkk)) + + + 44.. NNeett BBoooott PPrroocceessss DDeessccrriippttiioonn.. + + + ((ggkk))The net boot process is started as a result of the PC boot + process. The net boot program can reside on a rom, e.g. on an adaptor + card, or in ram as a result of reading off disk.((ggkk)) + + + + The boot process may execute in any mode (e.g. 8086, 80386) it + desires. When it jumps to the start location in the boot image, it + must be in 8086 mode and be capable of going into any mode supported + by the underlying processor. + + + + The image cannot be loaded into address spaces below 10000h, or + between A0000h through FFFFFh, or between 98000h through 9FFFFh. + ((ggkk))Only when the image is not going to return to the boot process, + all the memory is available to it once it has been started, so it can + relocate parts of itself to these areas.((ggkk)) + + + + The boot process must be capable of loading the image into all other + memory locations. Specifically, where the machine supports this, this + means memory over 100000h. + + + + The net boot process must execute the bootp protocol, followed by the + tftp protocol, as defined in the relevant rfc's. + + + + The file name used in the tftp protocol must be that given by the + bootp record. + + + + If less than 512 bytes are loaded, the net boot process attempts to + display on the screen any ascii data at the start of the image. The + net boot process then exits in the normal manner. For a boot prom, + this will allow normal disk booting. ((ggkk))Reference to DOS deleted.((ggkk)) + + + + When the first 512 bytes have been loaded, the boot process checks for + an initial magic number, which is defined later. If this number is + present, the net process continues loading under the control of the + image format. The image, which is described later, tells the net boot + process where to put this record and all subsequent data. + + + + If no initial magic number is present the net boot process checks for + a second magic number at offset 510. If the magic number 510 = 55h, + 511 = AAh, then the net process continues. If this second magic number + is not present, then the net boot process terminates the tftp + protocol, displays an error message and exits in the normal manner. + + + + If no initial magic number is present and the second one is, the net + boot process relocates the 512 bytes to location 7c00h. The net boot + process continues to load any further image data to 10000h up. This + data can overwrite the first 512 boot bytes. If the image reaches + 98000h, then any further data is continued to be loaded above 100000h. + When all the data has been loaded, the net boot process jumps to + location 0:7c00. + + + + ((ggkk))When the net boot program calls the image, it places 2 far + pointers onto the stack, in standard intel order (e.g. segment:offset + representation). The first far pointer which immediately follows the + return address on the stack, points to the loaded boot image header. + The second far pointer which is placed above the first one, shows to + the memory area where the net boot process saved the bootp reply. + + + + If the boot image is flagged as being returnable to the boot process, + the boot program has to provide the boot image with interrupt vector + 78h. It's an interface to services provided by the net boot program + (see below for further description). + + + + If the boot image is not flagged as being returnable to the boot + process, before the boot image is called, the boot program has to set + the system into a state in which it was before the net boot process + has started.((ggkk)) + + + + 55.. IImmaaggee FFoorrmmaatt wwiitthh IInniittiiaall MMaaggiicc NNuummbbeerr.. + + + The first 512 bytes of the image file contain the image header, and + image loading information records. This contains all the information + needed by the net boot process as to where data is to be loaded. + + The magic number (in time-honoured tradition (well why not?)) is: + + + ______________________________________________________________________ + 0 = 36h + 1 = 13h + 2 = 03h + 3 = 1Bh + ______________________________________________________________________ + + + + Apart from the two magic numbers, all words and double words are in PC + native endian. + + + + Including the initial magic number the header record is: + + + ______________________________________________________________________ + +---------------------+ + | | + | Initial Magic No. | 4 bytes + +---------------------+ + | | + | Flags and length | double word + +---------------------+ + | | + | Location Address | double word in ds:bx format + +---------------------+ + | | + | Execute Address | double word in cs:ip format + +---------------------+ + ______________________________________________________________________ + + + + The Location address is where to place the 512 bytes. The net boot + process does this before loading the rest of the image. The location + address cannot be one of the reserved locations mentioned above, but + must be an address lower than 100000h. + + + + The rest of the image must not overwrite these initial 512 bytes, + placed at the required location. The writing of data by the net boot + process into these 512 bytes is deprecated. These 512 bytes must be + available for the image to interogate once it is loaded and running. + + + + The execute address is the location in cs:ip of the initial + instruction once the full image has been loaded. This must be lower + than 100000h, since the initial instructions will be executed in 8086 + mode. When the jump (actaully a far call) is made to the boot image, + the stack contains a far return address, with a far pointer parameter + above that, pointing to the location of this header. + + The flags and length field is broken up in the following way: + + + + Bits 0 to 3 (lowest 4 bits) define the length of the non vendor header + in double words. Currently the value is 4. + + + + Bits 4 to 7 define the length required by the vendor extra information + in double words. A value of zero indicates no extra vendor + information. + + + + ((ggkk))Bit 8 is set if the boot image can return to the net boot process + after execution. If this bit is not set the boot image does never + return to the net boot process, and the net boot program has to set + the system into a clean state before calling the boot image. + + + + Bits 9 to 31 are reserved for future use and must be set to zero.((ggkk)) + + + + After this header, and any vendor header, come the image loading + information records. These specify where data is to be loaded, how + long it is, and communicates to the loaded image what sort of data it + is. + + + + The format of each image loading information record is : + + + + ______________________________________________________________________ + +---------------------+ + | Flags, tags and | double word + | lengths | + +---------------------+ + | | + | Load Address | double word + +---------------------+ + | | + | Image Length | double word + +---------------------+ + | | + | Memory Length | double word + +---------------------+ + ______________________________________________________________________ + + + + Each image loading information record follows the previous, or the + header. + + + + The memory length, image length and load address fields are unsigned + 32 numbers. They do not have the segment:offset format used by the + 8086. + + + + The flags, tags and lengths field is broken up as follows: + + + + Bits 0 to 3 (lowest 4 bits) are the length of the non vendor part of + this header in double words. Currently this value is 4. + + + + Bits 4 to 7 indicate the length of any vendor information, in double + words. + + + + Bits 8 to 15 are for vendor's tags. The vendor tag is a private number + that the loaded image can use to determine what sort of image is at + this particular location. + + + + Bits 16 to 23 are for future expansion and should be set to zero. + + + + Bits 24 to 31 are for flags, which are defined later. + + + + Vendors may place further information after this information record, + and before the next. Each information record may have a different + vendor length. + + + + There are two restrictions on vendor information. + + + + One is that the header and all information records that the net boot + process is to use fall within the first 512 bytes. + + + + The second restriction is that the net boot process must ignore all + vendor additions. The net boot process may not overwrite vendor + supplied information, or other undefined data in the initial 512 + bytes. + + + + The flags are used to modify the load address field, and to indicate + that this is the last information record that the net boot process + should use. + + + + Bit 24 works in conjunction with bit 25 to specify the meaning of the + load address. + + + + + + + + + ______________________________________________________________________ + B24 B25 + + 0 0 load address is an absolute 32 number + + 1 0 add the load address to the location one past the last byte + of the memory area required by the last image loaded. + If the first image, then add to 512 plus the location + where the 512 bytes were placed + + 0 1 subtract the load address from the one past the + last writeable location in memory. Thus 1 would + be the last location one could write in memory. + + 1 1 load address is subtracted from the start of + the last image loaded. If the first image, then + subtract from the start of where the 512 bytes were + placed + ______________________________________________________________________ + + + + (For convenience bit 24 is byte 0 of the flag field) + + + + Bit 26 is the end marker for the net boot process. It is set when this + is the last information record the net boot process should look at. + More records may be present, but the net boot process will not look at + them. (Vendors can continue information records out past the 512 + boundary for private use in this manner). + + + + The image length tells the net boot process how many bytes are to be + loaded. Zero is a valid value. This can be used to mark memory areas + such as shared memory for interprocessor communication, flash eproms, + data in eproms. + + + + The image length can also be different from the memory length. This + allows decompression programs to fluff up the kernel image. It also + allows a file system to be larger then the loaded file system image. + + + + Bits 27 through 31 are not defined as yet and must be set to zero + until they are. + + + 66.. BBoooott pprroomm eennttrryy ppooiinnttss.. + + + ((ggkk))As mentioned above the net boot process has to provide interrupt + 78h as an entry point in case, the returnable flag (bit 9 of the flags + field in the image header) of the boot image has been set. When + calling this interface interrupt, the caller has to load the AH + register with a value indicating the type of operation requested: + + + + + + + + ______________________________________________________________________ + 00h - Installation check + Input: none + Output: AX - returns the value 474Bh + BX - flags indicating what further services are + provided by the net boot program: + Bit 0 - packet driver interface (see below) + Bits 1 to 15 are unused and have to be zero + + 01h - Cleanup and terminate the boot process services. This will + also remove the services provided by interrupt 87h. + Input: none + Output: none + ______________________________________________________________________ + + + + + + Further functions are not yet defined. These functions are only + available to boot images which have the first magic number at the + beginning of the image header, and have the returnable flag set in the + flags field. + + + + In order to provide compatibility with net boot programs written to + match an earlier version of this document, the loaded image should + check for the existence of interrupt 78h by looking at it's vector. If + that's 0:0, or if it does not return a proper magic ID after calling + the installation check function, the boot image has to assume that the + net boot program does not support this services interrupt. + + + + If the bit 0 of register BX of function 00h is set, the boot program + has to provide a packet driver <http://www.crynwr.com> interface at + interrupt 79h as described in the packet driver interface standard, + version 1.09, published by FTP Software, Inc., which is not repeated + here. It serves as an interface to the system's network card. It is + important to note that the net boot process has to provide a clean + packet driver interface without any handles being defined when the + boot image gets started. It is expected that the boot image sets up + it's own TCP/IP or other network's stack on top of this packet driver + interface. When the boot image returns to the net boot process, it + has to return a clean packet driver interface as well, without any + handles being defined.((ggkk)) + + + 77.. EExxaammppllee ooff aa bboooott iimmaaggee.. + + + Here is an example of how the boot image would look for Linux: + + + + + + + + + + + + + + ______________________________________________________________________ + 0x1B031336, /* magic number */ + 0x4, /* length of header is 16 bytes, no vendor info */ + 0x90000000, /* location in ds:bx format */ + 0x90000200, /* execute address in cs:ip format */ + + /* 2048 setup.S bytes */ + 0x4, /* flags, not end, absolute address, 16 bytes this + record, no vendor info */ + 0x90200, /* load address - note format */ + 0x800, /* 4 8 512 byte blocks for linux */ + 0x800, + + /* kernel image */ + 0x4, /* flags, not end, absolute address, 16 bytes this + record, no vendor info */ + 0x10000, /* load address - note format */ + 0x80000, /* 512K (this could be shorter */ + 0x80000, + + /* ramdisk for root file system */ + 0x04000004, /* flags = last, absolute address, 16 bytes this + record, no vendor info *// + 0x100000, /* load address - in extended memory */ + 0x80000, /* 512K for instance */ + 0x80000, + + /* Then follows linux specific information */ + ______________________________________________________________________ + + + + + 88.. TTeerrmmss + + + When I say 'the net boot process', I mean the act of loading the image + into memory, setting up any tables, up until the jump to the required + location in the image. + + + + The net booting program executes the net boot process. The net boot + program may be a rom, but not neccassarily. It is a set of + instructions and data residing on the booting machine. + + + + The image, or boot image, consists of the data loaded by the net boot + process. + + + + When I say 'the PC boot process', I mean the general PC rom bios boot + process, the setting up of hardware, the scanning for adaptor roms, + the execution of adaptor roms, the loading in of the initial boot + track. The PC boot process will include the net boot process, if one + is present. + + + + When I say client, I mean the PC booting up. + + + + + When I say 'image host', I mean the host where the boot image is + comming from. This may not have the same architecture as the client. + + + + The bootp protocol is defined in RFC951 and RFC1084. The tftp protocol + is defined in RFC783. These are available on many sites. See Comer + 1991 for details on how to obtain them. + + + + A bootp server is the machine that answers the bootp request. It is + not neccessarily the image host. + + + + "Can" and "may" means doesn't have to, but is allowed to and might. + "Must" means just that. "Cannot" means must not. + + + 99.. RReeffeerreenncceess + + + Comer, D.E. 1991, Internetworking with TCP/IP Vol I: Principles, + Protocols, and Architecture Second Edition, Prentice Hall, Englewood + Cliffs, N.J., 1991 + + + + Stevens, W.R 1990, Unix Network Programming, Prentice Hall, Englewood + Cliffs, N.J., 1990 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/include/Makefile b/include/Makefile new file mode 100644 index 0000000..cd88a26 --- /dev/null +++ b/include/Makefile @@ -0,0 +1,15 @@ +dist += include/Makefile \ + include/config.h \ + include/config.h.in \ + include/kexec-pe-zboot.h \ + include/kexec-uImage.h \ + include/x86/x86-linux.h \ + include/x86/mb_info.h \ + include/x86/mb_header.h \ + include/x86/multiboot2.h \ + include/elf.h \ + include/image.h \ + include/unused.h \ + include/boot/linuxbios_tables.h \ + include/boot/beoboot.h \ + include/boot/elf_boot.h diff --git a/include/boot/beoboot.h b/include/boot/beoboot.h new file mode 100644 index 0000000..d22fef1 --- /dev/null +++ b/include/boot/beoboot.h @@ -0,0 +1,39 @@ + +/*--- Boot image definitions ---------------------------------------*/ +struct beoboot_header { + char magic[4]; + uint8_t arch; + uint8_t flags; + uint16_t cmdline_size;/* length of command line (including null) */ + /* The alpha chunk is a backward compatibility hack. The original + * assumption was that integer sizes didn't matter because we + * would never mix architectures. x86_64 + i386 broke that + * assumption. It's fixed for that combination and the future. + * However, alpha needs a little hack now... */ +#ifdef __alpha__ + unsigned long kernel_size; + unsigned long initrd_size; +#else + uint32_t kernel_size; + uint32_t initrd_size; +#endif +}; +#define BEOBOOT_MAGIC "BeoB" +#define BEOBOOT_ARCH_I386 1 +#define BEOBOOT_ARCH_ALPHA 2 +#define BEOBOOT_ARCH_PPC 3 +#define BEOBOOT_ARCH_PPC64 4 +#if defined(__i386__) || defined(__x86_64__) +#define BEOBOOT_ARCH BEOBOOT_ARCH_I386 +#elif defined(__alpha__) +#define BEOBOOT_ARCH BEOBOOT_ARCH_ALPHA +#elif defined(powerpc) +#define BEOBOOT_ARCH BEOBOOT_ARCH_PPC +#elif defined(__powerpc64__) +#define BEOBOOT_ARCH BEOBOOT_ARCH_PPC64 +#else +#error Unsupported architecture. +#endif +#define BEOBOOT_INITRD_PRESENT 1 +/*------------------------------------------------------------------*/ + diff --git a/include/boot/elf_boot.h b/include/boot/elf_boot.h new file mode 100644 index 0000000..08f561e --- /dev/null +++ b/include/boot/elf_boot.h @@ -0,0 +1,104 @@ +#ifndef ELF_BOOT_H +#define ELF_BOOT_H + +/* This defines the structure of a table of parameters useful for ELF + * bootable images. These parameters are all passed and generated + * by the bootloader to the booted image. For simplicity and + * consistency the Elf Note format is reused. + * + * All of the information must be Position Independent Data. + * That is it must be safe to relocate the whole ELF boot parameter + * block without changing the meaning or correctnes of the data. + * Additionally it must be safe to permute the order of the ELF notes + * to any possible permutation without changing the meaning or correctness + * of the data. + * + */ + +#define ELF_BOOT_MAGIC 0x0E1FB007 + +#ifndef ASSEMBLY +#include <stdint.h> +typedef uint16_t Elf_Half; +typedef uint32_t Elf_Word; +typedef uint64_t Elf_Xword; + +/* + * Elf boot notes... + */ + +typedef struct Elf_Bhdr +{ + Elf_Word b_signature; /* "0x0E1FB007" */ + Elf_Word b_size; + Elf_Half b_checksum; + Elf_Half b_records; +} Elf_Bhdr; + +/* + * ELF Notes. + */ + +typedef struct Elf_Nhdr +{ + Elf_Word n_namesz; /* Length of the note's name. */ + Elf_Word n_descsz; /* Length of the note's descriptor. */ + Elf_Word n_type; /* Type of the note. */ +} Elf_Nhdr; + +#endif /* ASSEMBLY */ + + +/* Standardized Elf image notes for booting... The name for all of these is ELFBoot */ +#define ELF_NOTE_BOOT "ELFBoot" + +#define EIN_PROGRAM_NAME 0x00000001 +/* The program in this ELF file */ +#define EIN_PROGRAM_VERSION 0x00000002 +/* The version of the program in this ELF file */ +#define EIN_PROGRAM_CHECKSUM 0x00000003 +/* ip style checksum of the memory image. */ + + +/* Linux image notes for booting... The name for all of these is Linux */ + +#if 0 +#define LIN_COMMAND_LINE_PTR 0x00000006 +/* Pointer to the command line to pass to the loaded kernel. */ +#define LIN_INITRD_START_PTR 0x00000007 +/* Pointer to the start of the ramdisk in bytes */ +#define LIN_INITRD_SIZE_PTR 0x00000008 +/* Pointer to the size of the ramdisk in bytes */ +#define LIN_VID_MODE_PTR 0x00000009 +/* Pointer to the vid_mode parameter */ +#endif + +/* Etherboot specific notes */ +#define EB_PARAM_NOTE "Etherboot" +#define EB_IA64_SYSTAB 0x00000001 +#define EB_IA64_MEMMAP 0x00000002 +#define EB_IA64_FPSWA 0x00000003 +#define EB_IA64_CONINFO 0x00000004 +#define EB_BOOTP_DATA 0x00000005 +#define EB_HEADER 0x00000006 +#define EB_IA64_IMAGE_HANDLE 0x00000007 +#define EB_I386_MEMMAP 0x00000008 + +/* For standard notes n_namesz must be zero */ +/* All of the following standard note types provide a single null + * terminated string in the descriptor. + */ +#define EBN_FIRMWARE_TYPE 0x00000001 +/* On platforms that support multiple classes of firmware this field + * specifies the class of firmware you are loaded under. + */ +#define EBN_BOOTLOADER_NAME 0x00000002 +/* This specifies just the name of the bootloader for easy comparison */ +#define EBN_BOOTLOADER_VERSION 0x00000003 +/* This specifies the version of the bootlader */ +#define EBN_COMMAND_LINE 0x00000004 +/* This specifies a command line that can be set by user interaction, + * and is provided as a free form string to the loaded image. + */ + +#endif /* ELF_BOOT_H */ diff --git a/include/boot/linuxbios_tables.h b/include/boot/linuxbios_tables.h new file mode 100644 index 0000000..c324cf5 --- /dev/null +++ b/include/boot/linuxbios_tables.h @@ -0,0 +1,82 @@ +#ifndef LINUXBIOS_TABLES_H +#define LINUXBIOS_TABLES_H + +#include <stdint.h> + +/* The linuxbios table information is for conveying information + * from the firmware to the loaded OS image. Primarily this + * is expected to be information that cannot be discovered by + * other means, such as quering the hardware directly. + * + * All of the information should be Position Independent Data. + * That is it should be safe to relocated any of the information + * without it's meaning/correctnes changing. For table that + * can reasonably be used on multiple architectures the data + * size should be fixed. This should ease the transition between + * 32 bit and 64 bit architectures etc. + * + * The completeness test for the information in this table is: + * - Can all of the hardware be detected? + * - Are the per motherboard constants available? + * - Is there enough to allow a kernel to run that was written before + * a particular motherboard is constructed? (Assuming the kernel + * has drivers for all of the hardware but it does not have + * assumptions on how the hardware is connected together). + * + * With this test it should be straight forward to determine if a + * table entry is required or not. This should remove much of the + * long term compatibility burden as table entries which are + * irrelevant or have been replaced by better alternatives may be + * dropped. Of course it is polite and expidite to include extra + * table entries and be backwards compatible, but it is not required. + */ + + +struct lb_header +{ + uint8_t signature[4]; /* LBIO */ + uint32_t header_bytes; + uint32_t header_checksum; + uint32_t table_bytes; + uint32_t table_checksum; + uint32_t table_entries; +}; + +/* Every entry in the boot enviroment list will correspond to a boot + * info record. Encoding both type and size. The type is obviously + * so you can tell what it is. The size allows you to skip that + * boot enviroment record if you don't know what it easy. This allows + * forward compatibility with records not yet defined. + */ +struct lb_record { + uint32_t tag; /* tag ID */ + uint32_t size; /* size of record (in bytes) */ +}; + +#define LB_TAG_UNUSED 0x0000 + +#define LB_TAG_MEMORY 0x0001 + +struct lb_memory_range { + uint64_t start; + uint64_t size; + uint32_t type; +#define LB_MEM_RAM 1 +#define LB_MEM_RESERVED 2 + +}; + +struct lb_memory { + uint32_t tag; + uint32_t size; + struct lb_memory_range map[0]; +}; + +#define LB_TAG_HWRPB 0x0002 +struct lb_hwrpb { + uint32_t tag; + uint32_t size; + uint64_t hwrpb; +}; + +#endif /* LINUXBIOS_TABLES_H */ diff --git a/include/config.h b/include/config.h new file mode 100644 index 0000000..d5400ef --- /dev/null +++ b/include/config.h @@ -0,0 +1,76 @@ +/* include/config.h. Generated from config.h.in by configure. */ +/* include/config.h.in. Generated from configure.ac by autoheader. */ + +/* Define to build for BookE */ +/* #undef CONFIG_BOOKE */ + +/* Define to 1 to link libxenctrl.so at run-time rather than build-time */ +/* #undef CONFIG_LIBXENCTRL_DL */ + +/* Define to 1 if you have the <inttypes.h> header file. */ +#define HAVE_INTTYPES_H 1 + +/* The kexec_status call is available */ +/* #undef HAVE_KEXEC_CMD_STATUS */ + +/* Define to 1 if you have the `dl' library (-ldl). */ +/* #undef HAVE_LIBDL */ + +/* Define to 1 if you have the `lzma' library (-llzma). */ +/* #undef HAVE_LIBLZMA */ + +/* Define to 1 if you have the `xenctrl' library (-lxenctrl). */ +/* #undef HAVE_LIBXENCTRL */ + +/* Define to 1 if you have the `z' library (-lz). */ +#define HAVE_LIBZ 1 + +/* Define to 1 if you have the <stdint.h> header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the <stdio.h> header file. */ +#define HAVE_STDIO_H 1 + +/* Define to 1 if you have the <stdlib.h> header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the <strings.h> header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the <string.h> header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the <sys/stat.h> header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the <sys/types.h> header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the <unistd.h> header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "kexec-tools" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "kexec-tools 2.0.27" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "kexec-tools" + +/* Define to the home page for this package. */ +#define PACKAGE_URL "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "2.0.27" + +/* Define to 1 if all of the C90 standard headers exist (not just the ones + required in a freestanding environment). This macro is provided for + backward compatibility; new code need not use it. */ +#define STDC_HEADERS 1 + +/* Define to include gamecube support */ +/* #undef WITH_GAMECUBE */ diff --git a/include/config.h.in b/include/config.h.in new file mode 100644 index 0000000..1667a9c --- /dev/null +++ b/include/config.h.in @@ -0,0 +1,75 @@ +/* include/config.h.in. Generated from configure.ac by autoheader. */ + +/* Define to build for BookE */ +#undef CONFIG_BOOKE + +/* Define to 1 to link libxenctrl.so at run-time rather than build-time */ +#undef CONFIG_LIBXENCTRL_DL + +/* Define to 1 if you have the <inttypes.h> header file. */ +#undef HAVE_INTTYPES_H + +/* The kexec_status call is available */ +#undef HAVE_KEXEC_CMD_STATUS + +/* Define to 1 if you have the `dl' library (-ldl). */ +#undef HAVE_LIBDL + +/* Define to 1 if you have the `lzma' library (-llzma). */ +#undef HAVE_LIBLZMA + +/* Define to 1 if you have the `xenctrl' library (-lxenctrl). */ +#undef HAVE_LIBXENCTRL + +/* Define to 1 if you have the `z' library (-lz). */ +#undef HAVE_LIBZ + +/* Define to 1 if you have the <stdint.h> header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the <stdio.h> header file. */ +#undef HAVE_STDIO_H + +/* Define to 1 if you have the <stdlib.h> header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the <strings.h> header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the <string.h> header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the <sys/stat.h> header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the <sys/types.h> header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the <unistd.h> header file. */ +#undef HAVE_UNISTD_H + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the home page for this package. */ +#undef PACKAGE_URL + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define to 1 if all of the C90 standard headers exist (not just the ones + required in a freestanding environment). This macro is provided for + backward compatibility; new code need not use it. */ +#undef STDC_HEADERS + +/* Define to include gamecube support */ +#undef WITH_GAMECUBE diff --git a/include/elf.h b/include/elf.h new file mode 100644 index 0000000..1c8d2cc --- /dev/null +++ b/include/elf.h @@ -0,0 +1,2377 @@ + +/* This file defines standard ELF types, structures, and macros. + Copyright (C) 1995, 1996, 1997, 1998, 1999 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ian Lance Taylor <ian@cygnus.com>. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + 12 April 2002 Modified by Eric Biederman <ebiederm@xmission.com> so + it can be used outside of glibc. + +*/ + +#ifndef ELF_H +#define ELF_H + +/* Standard ELF types. */ + +#include <stdint.h> +#include <stddef.h> + +/* Type for a 16-bit quantity. */ +typedef uint16_t Elf32_Half; +typedef uint16_t Elf64_Half; + +/* Types for signed and unsigned 32-bit quantities. */ +typedef uint32_t Elf32_Word; +typedef int32_t Elf32_Sword; +typedef uint32_t Elf64_Word; +typedef int32_t Elf64_Sword; + +/* Types for signed and unsigned 64-bit quantities. */ +typedef uint64_t Elf32_Xword; +typedef int64_t Elf32_Sxword; +typedef uint64_t Elf64_Xword; +typedef int64_t Elf64_Sxword; + +/* Type of addresses. */ +typedef uint32_t Elf32_Addr; +typedef uint64_t Elf64_Addr; + +/* Type of file offsets. */ +typedef uint32_t Elf32_Off; +typedef uint64_t Elf64_Off; + +/* Type for section indices, which are 16-bit quantities. */ +typedef uint16_t Elf32_Section; +typedef uint16_t Elf64_Section; + +/* Type of symbol indices. */ +typedef uint32_t Elf32_Symndx; +typedef uint64_t Elf64_Symndx; + +/* Type for version symbol information. */ +typedef Elf32_Half Elf32_Versym; +typedef Elf64_Half Elf64_Versym; + +/* The ELF file header. This appears at the start of every ELF file. */ + +#define EI_NIDENT (16) + +typedef struct +{ + unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ + Elf32_Half e_type; /* Object file type */ + Elf32_Half e_machine; /* Architecture */ + Elf32_Word e_version; /* Object file version */ + Elf32_Addr e_entry; /* Entry point virtual address */ + Elf32_Off e_phoff; /* Program header table file offset */ + Elf32_Off e_shoff; /* Section header table file offset */ + Elf32_Word e_flags; /* Processor-specific flags */ + Elf32_Half e_ehsize; /* ELF header size in bytes */ + Elf32_Half e_phentsize; /* Program header table entry size */ + Elf32_Half e_phnum; /* Program header table entry count */ + Elf32_Half e_shentsize; /* Section header table entry size */ + Elf32_Half e_shnum; /* Section header table entry count */ + Elf32_Half e_shstrndx; /* Section header string table index */ +} Elf32_Ehdr; + +typedef struct +{ + unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ + Elf64_Half e_type; /* Object file type */ + Elf64_Half e_machine; /* Architecture */ + Elf64_Word e_version; /* Object file version */ + Elf64_Addr e_entry; /* Entry point virtual address */ + Elf64_Off e_phoff; /* Program header table file offset */ + Elf64_Off e_shoff; /* Section header table file offset */ + Elf64_Word e_flags; /* Processor-specific flags */ + Elf64_Half e_ehsize; /* ELF header size in bytes */ + Elf64_Half e_phentsize; /* Program header table entry size */ + Elf64_Half e_phnum; /* Program header table entry count */ + Elf64_Half e_shentsize; /* Section header table entry size */ + Elf64_Half e_shnum; /* Section header table entry count */ + Elf64_Half e_shstrndx; /* Section header string table index */ +} Elf64_Ehdr; + +/* Fields in the e_ident array. The EI_* macros are indices into the + array. The macros under each EI_* macro are the values the byte + may have. */ + +#define EI_MAG0 0 /* File identification byte 0 index */ +#define ELFMAG0 0x7f /* Magic number byte 0 */ + +#define EI_MAG1 1 /* File identification byte 1 index */ +#define ELFMAG1 'E' /* Magic number byte 1 */ + +#define EI_MAG2 2 /* File identification byte 2 index */ +#define ELFMAG2 'L' /* Magic number byte 2 */ + +#define EI_MAG3 3 /* File identification byte 3 index */ +#define ELFMAG3 'F' /* Magic number byte 3 */ + +/* Conglomeration of the identification bytes, for easy testing as a word. */ +#define ELFMAG "\177ELF" +#define SELFMAG 4 + +#define EI_CLASS 4 /* File class byte index */ +#define ELFCLASSNONE 0 /* Invalid class */ +#define ELFCLASS32 1 /* 32-bit objects */ +#define ELFCLASS64 2 /* 64-bit objects */ +#define ELFCLASSNUM 3 + +#define EI_DATA 5 /* Data encoding byte index */ +#define ELFDATANONE 0 /* Invalid data encoding */ +#define ELFDATA2LSB 1 /* 2's complement, little endian */ +#define ELFDATA2MSB 2 /* 2's complement, big endian */ +#define ELFDATANUM 3 + +#define EI_VERSION 6 /* File version byte index */ + /* Value must be EV_CURRENT */ + +#define EI_OSABI 7 /* OS ABI identification */ +#define ELFOSABI_NONE 0 /* UNIX System V ABI */ +#define ELFOSABI_SYSV 0 /* Alias. */ +#define ELFOSABI_HPUX 1 /* HP-UX */ +#define ELFOSABI_NETBSD 2 /* NetBSD. */ +#define ELFOSABI_LINUX 3 /* Linux. */ +#define ELFOSABI_SOLARIS 6 /* Sun Solaris. */ +#define ELFOSABI_AIX 7 /* IBM AIX. */ +#define ELFOSABI_IRIX 8 /* SGI Irix. */ +#define ELFOSABI_FREEBSD 9 /* FreeBSD. */ +#define ELFOSABI_TRU64 10 /* Compaq TRU64 UNIX. */ +#define ELFOSABI_MODESTO 11 /* Novell Modesto. */ +#define ELFOSABI_OPENBSD 12 /* OpenBSD. */ +#define ELFOSABI_ARM 97 /* ARM */ +#define ELFOSABI_STANDALONE 255 /* Standalone (embedded) application */ + +#define EI_ABIVERSION 8 /* ABI version */ + +#define EI_PAD 9 /* Byte index of padding bytes */ + +/* Legal values for e_type (object file type). */ + +#define ET_NONE 0 /* No file type */ +#define ET_REL 1 /* Relocatable file */ +#define ET_EXEC 2 /* Executable file */ +#define ET_DYN 3 /* Shared object file */ +#define ET_CORE 4 /* Core file */ +#define ET_NUM 5 /* Number of defined types */ +#define ET_LOOS 0xfe00 /* OS-specific range start */ +#define ET_HIOS 0xfeff /* OS-specific range end */ +#define ET_LOPROC 0xff00 /* Processor-specific range start */ +#define ET_HIPROC 0xffff /* Processor-specific range end */ + +/* Legal values for e_machine (architecture). */ + +#define EM_NONE 0 /* No machine */ +#define EM_M32 1 /* AT&T WE 32100 */ +#define EM_SPARC 2 /* SUN SPARC */ +#define EM_386 3 /* Intel 80386 */ +#define EM_68K 4 /* Motorola m68k family */ +#define EM_88K 5 /* Motorola m88k family */ +#define EM_486 6 /* Intel 80486 */ +#define EM_860 7 /* Intel 80860 */ +#define EM_MIPS 8 /* MIPS R3000 big-endian */ +#define EM_S370 9 /* Amdahl */ +#define EM_MIPS_RS4_BE 10 /* MIPS R4000 big-endian */ +#define EM_RS6000 11 /* RS6000 */ + +#define EM_PARISC 15 /* HPPA */ +#define EM_nCUBE 16 /* nCUBE */ +#define EM_VPP500 17 /* Fujitsu VPP500 */ +#define EM_SPARC32PLUS 18 /* Sun's "v8plus" */ +#define EM_960 19 /* Intel 80960 */ +#define EM_PPC 20 /* PowerPC */ +#define EM_PPC64 21 /* PowerPC 64-bit */ +#define EM_S390 22 /* IBM S390 */ + +#define EM_V800 36 /* NEC V800 series */ +#define EM_FR20 37 /* Fujitsu FR20 */ +#define EM_RH32 38 /* TRW RH-32 */ +#define EM_RCE 39 /* Motorola RCE */ +#define EM_ARM 40 /* ARM */ +#define EM_FAKE_ALPHA 41 /* Digital Alpha */ +#define EM_SH 42 /* Hitachi SH */ +#define EM_SPARCV9 43 /* SPARC v9 64-bit */ +#define EM_TRICORE 44 /* Siemens Tricore */ +#define EM_ARC 45 /* Argonaut RISC Core */ +#define EM_H8_300 46 /* Hitachi H8/300 */ +#define EM_H8_300H 47 /* Hitachi H8/300H */ +#define EM_H8S 48 /* Hitachi H8S */ +#define EM_H8_500 49 /* Hitachi H8/500 */ +#define EM_IA_64 50 /* Intel Merced */ +#define EM_MIPS_X 51 /* Stanford MIPS-X */ +#define EM_COLDFIRE 52 /* Motorola Coldfire */ +#define EM_68HC12 53 /* Motorola M68HC12 */ +#define EM_MMA 54 /* Fujitsu MMA Multimedia Accelerator*/ +#define EM_PCP 55 /* Siemens PCP */ +#define EM_NCPU 56 /* Sony nCPU embeeded RISC */ +#define EM_NDR1 57 /* Denso NDR1 microprocessor */ +#define EM_STARCORE 58 /* Motorola Start*Core processor */ +#define EM_ME16 59 /* Toyota ME16 processor */ +#define EM_ST100 60 /* STMicroelectronic ST100 processor */ +#define EM_TINYJ 61 /* Advanced Logic Corp. Tinyj emb.fam*/ +#define EM_X86_64 62 /* AMD x86-64 architecture */ +#define EM_PDSP 63 /* Sony DSP Processor */ + +#define EM_FX66 66 /* Siemens FX66 microcontroller */ +#define EM_ST9PLUS 67 /* STMicroelectronics ST9+ 8/16 mc */ +#define EM_ST7 68 /* STmicroelectronics ST7 8 bit mc */ +#define EM_68HC16 69 /* Motorola MC68HC16 microcontroller */ +#define EM_68HC11 70 /* Motorola MC68HC11 microcontroller */ +#define EM_68HC08 71 /* Motorola MC68HC08 microcontroller */ +#define EM_68HC05 72 /* Motorola MC68HC05 microcontroller */ +#define EM_SVX 73 /* Silicon Graphics SVx */ +#define EM_ST19 74 /* STMicroelectronics ST19 8 bit mc */ +#define EM_VAX 75 /* Digital VAX */ +#define EM_CRIS 76 /* Axis Communications 32-bit embedded processor */ +#define EM_JAVELIN 77 /* Infineon Technologies 32-bit embedded processor */ +#define EM_FIREPATH 78 /* Element 14 64-bit DSP Processor */ +#define EM_ZSP 79 /* LSI Logic 16-bit DSP Processor */ +#define EM_MMIX 80 /* Donald Knuth's educational 64-bit processor */ +#define EM_HUANY 81 /* Harvard University machine-independent object files */ +#define EM_PRISM 82 /* SiTera Prism */ +#define EM_AVR 83 /* Atmel AVR 8-bit microcontroller */ +#define EM_FR30 84 /* Fujitsu FR30 */ +#define EM_D10V 85 /* Mitsubishi D10V */ +#define EM_D30V 86 /* Mitsubishi D30V */ +#define EM_V850 87 /* NEC v850 */ +#define EM_M32R 88 /* Mitsubishi M32R */ +#define EM_MN10300 89 /* Matsushita MN10300 */ +#define EM_MN10200 90 /* Matsushita MN10200 */ +#define EM_PJ 91 /* picoJava */ +#define EM_OPENRISC 92 /* OpenRISC 32-bit embedded processor */ +#define EM_ARC_A5 93 /* ARC Cores Tangent-A5 */ +#define EM_XTENSA 94 /* Tensilica Xtensa Architecture */ +#define EM_AARCH64 183 /* ARM AARCH64 */ +#define EM_LOONGARCH 258 /* Loongson Loongarch*/ +#define EM_NUM 184 + +/* If it is necessary to assign new unofficial EM_* values, please + pick large random numbers (0x8523, 0xa7f2, etc.) to minimize the + chances of collision with official or non-GNU unofficial values. */ + +#define EM_ALPHA 0x9026 + +/* Legal values for e_version (version). */ + +#define EV_NONE 0 /* Invalid ELF version */ +#define EV_CURRENT 1 /* Current version */ +#define EV_NUM 2 + +/* Section header. */ + +typedef struct +{ + Elf32_Word sh_name; /* Section name (string tbl index) */ + Elf32_Word sh_type; /* Section type */ + Elf32_Word sh_flags; /* Section flags */ + Elf32_Addr sh_addr; /* Section virtual addr at execution */ + Elf32_Off sh_offset; /* Section file offset */ + Elf32_Word sh_size; /* Section size in bytes */ + Elf32_Word sh_link; /* Link to another section */ + Elf32_Word sh_info; /* Additional section information */ + Elf32_Word sh_addralign; /* Section alignment */ + Elf32_Word sh_entsize; /* Entry size if section holds table */ +} Elf32_Shdr; + +typedef struct +{ + Elf64_Word sh_name; /* Section name (string tbl index) */ + Elf64_Word sh_type; /* Section type */ + Elf64_Xword sh_flags; /* Section flags */ + Elf64_Addr sh_addr; /* Section virtual addr at execution */ + Elf64_Off sh_offset; /* Section file offset */ + Elf64_Xword sh_size; /* Section size in bytes */ + Elf64_Word sh_link; /* Link to another section */ + Elf64_Word sh_info; /* Additional section information */ + Elf64_Xword sh_addralign; /* Section alignment */ + Elf64_Xword sh_entsize; /* Entry size if section holds table */ +} Elf64_Shdr; + +/* Special section indices. */ + +#define SHN_UNDEF 0 /* Undefined section */ +#define SHN_LORESERVE 0xff00 /* Start of reserved indices */ +#define SHN_LOPROC 0xff00 /* Start of processor-specific */ +#define SHN_HIPROC 0xff1f /* End of processor-specific */ +#define SHN_LOOS 0xff20 /* Start of OS-specific */ +#define SHN_HIOS 0xff3f /* End of OS-specific */ +#define SHN_ABS 0xfff1 /* Associated symbol is absolute */ +#define SHN_COMMON 0xfff2 /* Associated symbol is common */ +#define SHN_XINDEX 0xffff /* Index is in extra table. */ +#define SHN_HIRESERVE 0xffff /* End of reserved indices */ + +/* Legal values for sh_type (section type). */ + +#define SHT_NULL 0 /* Section header table entry unused */ +#define SHT_PROGBITS 1 /* Program data */ +#define SHT_SYMTAB 2 /* Symbol table */ +#define SHT_STRTAB 3 /* String table */ +#define SHT_RELA 4 /* Relocation entries with addends */ +#define SHT_HASH 5 /* Symbol hash table */ +#define SHT_DYNAMIC 6 /* Dynamic linking information */ +#define SHT_NOTE 7 /* Notes */ +#define SHT_NOBITS 8 /* Program space with no data (bss) */ +#define SHT_REL 9 /* Relocation entries, no addends */ +#define SHT_SHLIB 10 /* Reserved */ +#define SHT_DYNSYM 11 /* Dynamic linker symbol table */ +#define SHT_INIT_ARRAY 14 /* Array of constructors */ +#define SHT_FINI_ARRAY 15 /* Array of destructors */ +#define SHT_PREINIT_ARRAY 16 /* Array of pre-constructors */ +#define SHT_GROUP 17 /* Section group */ +#define SHT_SYMTAB_SHNDX 18 /* Extended section indeces */ +#define SHT_NUM 19 /* Number of defined types. */ +#define SHT_LOOS 0x60000000 /* Start OS-specific */ +#define SHT_GNU_LIBLIST 0x6ffffff7 /* Prelink library list */ +#define SHT_CHECKSUM 0x6ffffff8 /* Checksum for DSO content. */ +#define SHT_LOSUNW 0x6ffffffa /* Sun-specific low bound. */ +#define SHT_SUNW_move 0x6ffffffa +#define SHT_SUNW_COMDAT 0x6ffffffb +#define SHT_SUNW_syminfo 0x6ffffffc +#define SHT_GNU_verdef 0x6ffffffd /* Version definition section. */ +#define SHT_GNU_verneed 0x6ffffffe /* Version needs section. */ +#define SHT_GNU_versym 0x6fffffff /* Version symbol table. */ +#define SHT_HISUNW 0x6fffffff /* Sun-specific high bound. */ +#define SHT_HIOS 0x6fffffff /* End OS-specific type */ +#define SHT_LOPROC 0x70000000 /* Start of processor-specific */ +#define SHT_HIPROC 0x7fffffff /* End of processor-specific */ +#define SHT_LOUSER 0x80000000 /* Start of application-specific */ +#define SHT_HIUSER 0x8fffffff /* End of application-specific */ + +/* Legal values for sh_flags (section flags). */ + +#define SHF_WRITE (1 << 0) /* Writable */ +#define SHF_ALLOC (1 << 1) /* Occupies memory during execution */ +#define SHF_EXECINSTR (1 << 2) /* Executable */ +#define SHF_MERGE (1 << 4) /* Might be merged */ +#define SHF_STRINGS (1 << 5) /* Contains nul-terminated strings */ +#define SHF_INFO_LINK (1 << 6) /* `sh_info' contains SHT index */ +#define SHF_LINK_ORDER (1 << 7) /* Preserve order after combining */ +#define SHF_OS_NONCONFORMING (1 << 8) /* Non-standard OS specific handling + required */ +#define SHF_GROUP (1 << 9) /* Section is member of a group. */ +#define SHF_TLS (1 << 10) /* Section hold thread-local data. */ +#define SHF_MASKOS 0x0ff00000 /* OS-specific. */ +#define SHF_MASKPROC 0xf0000000 /* Processor-specific */ + +/* Section group handling. */ +#define GRP_COMDAT 0x1 /* Mark group as COMDAT. */ + +/* Symbol table entry. */ + +typedef struct +{ + Elf32_Word st_name; /* Symbol name (string tbl index) */ + Elf32_Addr st_value; /* Symbol value */ + Elf32_Word st_size; /* Symbol size */ + unsigned char st_info; /* Symbol type and binding */ + unsigned char st_other; /* No defined meaning, 0 */ + Elf32_Section st_shndx; /* Section index */ +} Elf32_Sym; + +typedef struct +{ + Elf64_Word st_name; /* Symbol name (string tbl index) */ + unsigned char st_info; /* Symbol type and binding */ + unsigned char st_other; /* No defined meaning, 0 */ + Elf64_Section st_shndx; /* Section index */ + Elf64_Addr st_value; /* Symbol value */ + Elf64_Xword st_size; /* Symbol size */ +} Elf64_Sym; + +/* The syminfo section if available contains additional information about + every dynamic symbol. */ + +typedef struct +{ + Elf32_Half si_boundto; /* Direct bindings, symbol bound to */ + Elf32_Half si_flags; /* Per symbol flags */ +} Elf32_Syminfo; + +typedef struct +{ + Elf64_Half si_boundto; /* Direct bindings, symbol bound to */ + Elf64_Half si_flags; /* Per symbol flags */ +} Elf64_Syminfo; + +/* Possible values for si_boundto. */ +#define SYMINFO_BT_SELF 0xffff /* Symbol bound to self */ +#define SYMINFO_BT_PARENT 0xfffe /* Symbol bound to parent */ +#define SYMINFO_BT_LOWRESERVE 0xff00 /* Beginning of reserved entries */ + +/* Possible bitmasks for si_flags. */ +#define SYMINFO_FLG_DIRECT 0x0001 /* Direct bound symbol */ +#define SYMINFO_FLG_PASSTHRU 0x0002 /* Pass-thru symbol for translator */ +#define SYMINFO_FLG_COPY 0x0004 /* Symbol is a copy-reloc */ +#define SYMINFO_FLG_LAZYLOAD 0x0008 /* Symbol bound to object to be lazy + loaded */ +/* Syminfo version values. */ +#define SYMINFO_NONE 0 +#define SYMINFO_CURRENT 1 +#define SYMINFO_NUM 2 + + +/* Special section index. */ + +#define SHN_UNDEF 0 /* No section, undefined symbol. */ + +/* How to extract and insert information held in the st_info field. */ + +#define ELF32_ST_BIND(val) (((unsigned char) (val)) >> 4) +#define ELF32_ST_TYPE(val) ((val) & 0xf) +#define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) + +/* Both Elf32_Sym and Elf64_Sym use the same one-byte st_info field. */ +#define ELF64_ST_BIND(val) ELF32_ST_BIND (val) +#define ELF64_ST_TYPE(val) ELF32_ST_TYPE (val) +#define ELF64_ST_INFO(bind, type) ELF32_ST_INFO ((bind), (type)) + +/* Legal values for ST_BIND subfield of st_info (symbol binding). */ + +#define STB_LOCAL 0 /* Local symbol */ +#define STB_GLOBAL 1 /* Global symbol */ +#define STB_WEAK 2 /* Weak symbol */ +#define STB_NUM 3 /* Number of defined types. */ +#define STB_LOOS 10 /* Start of OS-specific */ +#define STB_HIOS 12 /* End of OS-specific */ +#define STB_LOPROC 13 /* Start of processor-specific */ +#define STB_HIPROC 15 /* End of processor-specific */ + +/* Legal values for ST_TYPE subfield of st_info (symbol type). */ + +#define STT_NOTYPE 0 /* Symbol type is unspecified */ +#define STT_OBJECT 1 /* Symbol is a data object */ +#define STT_FUNC 2 /* Symbol is a code object */ +#define STT_SECTION 3 /* Symbol associated with a section */ +#define STT_FILE 4 /* Symbol's name is file name */ +#define STT_COMMON 5 /* Symbol is a common data object */ +#define STT_TLS 6 /* Symbol is thread-local data object*/ +#define STT_NUM 7 /* Number of defined types. */ +#define STT_LOOS 10 /* Start of OS-specific */ +#define STT_HIOS 12 /* End of OS-specific */ +#define STT_LOPROC 13 /* Start of processor-specific */ +#define STT_HIPROC 15 /* End of processor-specific */ + + +/* Symbol table indices are found in the hash buckets and chain table + of a symbol hash table section. This special index value indicates + the end of a chain, meaning no further symbols are found in that bucket. */ + +#define STN_UNDEF 0 /* End of a chain. */ + + +/* How to extract and insert information held in the st_other field. */ + +#define ELF32_ST_VISIBILITY(o) ((o) & 0x03) + +/* For ELF64 the definitions are the same. */ +#define ELF64_ST_VISIBILITY(o) ELF32_ST_VISIBILITY (o) + +/* Symbol visibility specification encoded in the st_other field. */ +#define STV_DEFAULT 0 /* Default symbol visibility rules */ +#define STV_INTERNAL 1 /* Processor specific hidden class */ +#define STV_HIDDEN 2 /* Sym unavailable in other modules */ +#define STV_PROTECTED 3 /* Not preemptible, not exported */ + + +/* Relocation table entry without addend (in section of type SHT_REL). */ + +typedef struct +{ + Elf32_Addr r_offset; /* Address */ + Elf32_Word r_info; /* Relocation type and symbol index */ +} Elf32_Rel; + +/* I have seen two different definitions of the Elf64_Rel and + Elf64_Rela structures, so we'll leave them out until Novell (or + whoever) gets their act together. */ +/* The following, at least, is used on Sparc v9, MIPS, and Alpha. */ + +typedef struct +{ + Elf64_Addr r_offset; /* Address */ + Elf64_Xword r_info; /* Relocation type and symbol index */ +} Elf64_Rel; + +/* Relocation table entry with addend (in section of type SHT_RELA). */ + +typedef struct +{ + Elf32_Addr r_offset; /* Address */ + Elf32_Word r_info; /* Relocation type and symbol index */ + Elf32_Sword r_addend; /* Addend */ +} Elf32_Rela; + +typedef struct +{ + Elf64_Addr r_offset; /* Address */ + Elf64_Xword r_info; /* Relocation type and symbol index */ + Elf64_Sxword r_addend; /* Addend */ +} Elf64_Rela; + +/* How to extract and insert information held in the r_info field. */ + +#define ELF32_R_SYM(val) ((val) >> 8) +#define ELF32_R_TYPE(val) ((val) & 0xff) +#define ELF32_R_INFO(sym, type) (((sym) << 8) + ((type) & 0xff)) + +#define ELF64_R_SYM(i) ((i) >> 32) +#define ELF64_R_TYPE(i) ((i) & 0xffffffff) +#define ELF64_R_INFO(sym,type) ((((Elf64_Xword) (sym)) << 32) + (type)) + +/* Program segment header. */ + +typedef struct +{ + Elf32_Word p_type; /* Segment type */ + Elf32_Off p_offset; /* Segment file offset */ + Elf32_Addr p_vaddr; /* Segment virtual address */ + Elf32_Addr p_paddr; /* Segment physical address */ + Elf32_Word p_filesz; /* Segment size in file */ + Elf32_Word p_memsz; /* Segment size in memory */ + Elf32_Word p_flags; /* Segment flags */ + Elf32_Word p_align; /* Segment alignment */ +} Elf32_Phdr; + +typedef struct +{ + Elf64_Word p_type; /* Segment type */ + Elf64_Word p_flags; /* Segment flags */ + Elf64_Off p_offset; /* Segment file offset */ + Elf64_Addr p_vaddr; /* Segment virtual address */ + Elf64_Addr p_paddr; /* Segment physical address */ + Elf64_Xword p_filesz; /* Segment size in file */ + Elf64_Xword p_memsz; /* Segment size in memory */ + Elf64_Xword p_align; /* Segment alignment */ +} Elf64_Phdr; + +/* Legal values for p_type (segment type). */ + +#define PT_NULL 0 /* Program header table entry unused */ +#define PT_LOAD 1 /* Loadable program segment */ +#define PT_DYNAMIC 2 /* Dynamic linking information */ +#define PT_INTERP 3 /* Program interpreter */ +#define PT_NOTE 4 /* Auxiliary information */ +#define PT_SHLIB 5 /* Reserved */ +#define PT_PHDR 6 /* Entry for header table itself */ +#define PT_TLS 7 /* Thread-local storage segment */ +#define PT_NUM 8 /* Number of defined types */ +#define PT_LOOS 0x60000000 /* Start of OS-specific */ +#define PT_GNU_EH_FRAME 0x6474e550 /* GCC .eh_frame_hdr segment */ +#define PT_GNU_STACK 0x6474e551 /* Indicates stack executability */ +#define PT_LOSUNW 0x6ffffffa +#define PT_SUNWBSS 0x6ffffffa /* Sun Specific segment */ +#define PT_SUNWSTACK 0x6ffffffb /* Stack segment */ +#define PT_HISUNW 0x6fffffff +#define PT_HIOS 0x6fffffff /* End of OS-specific */ +#define PT_LOPROC 0x70000000 /* Start of processor-specific */ +#define PT_HIPROC 0x7fffffff /* End of processor-specific */ + +/* Legal values for p_flags (segment flags). */ + +#define PF_X (1 << 0) /* Segment is executable */ +#define PF_W (1 << 1) /* Segment is writable */ +#define PF_R (1 << 2) /* Segment is readable */ +#define PF_MASKOS 0x0ff00000 /* OS-specific */ +#define PF_MASKPROC 0xf0000000 /* Processor-specific */ + +/* Legal values for note segment descriptor types for core files. */ + +#define NT_PRSTATUS 1 /* Contains copy of prstatus struct */ +#define NT_FPREGSET 2 /* Contains copy of fpregset struct */ +#define NT_PRPSINFO 3 /* Contains copy of prpsinfo struct */ +#define NT_PRXREG 4 /* Contains copy of prxregset struct */ +#define NT_TASKSTRUCT 4 /* Contains copy of task structure */ +#define NT_PLATFORM 5 /* String from sysinfo(SI_PLATFORM) */ +#define NT_AUXV 6 /* Contains copy of auxv array */ +#define NT_GWINDOWS 7 /* Contains copy of gwindows struct */ +#define NT_ASRS 8 /* Contains copy of asrset struct */ +#define NT_PSTATUS 10 /* Contains copy of pstatus struct */ +#define NT_PSINFO 13 /* Contains copy of psinfo struct */ +#define NT_PRCRED 14 /* Contains copy of prcred struct */ +#define NT_UTSNAME 15 /* Contains copy of utsname struct */ +#define NT_LWPSTATUS 16 /* Contains copy of lwpstatus struct */ +#define NT_LWPSINFO 17 /* Contains copy of lwpinfo struct */ +#define NT_PRFPXREG 20 /* Contains copy of fprxregset struct*/ + +/* Legal values for the note segment descriptor types for object files. */ + +#define NT_VERSION 1 /* Contains a version string. */ + + +/* Dynamic section entry. */ + +typedef struct +{ + Elf32_Sword d_tag; /* Dynamic entry type */ + union + { + Elf32_Word d_val; /* Integer value */ + Elf32_Addr d_ptr; /* Address value */ + } d_un; +} Elf32_Dyn; + +typedef struct +{ + Elf64_Sxword d_tag; /* Dynamic entry type */ + union + { + Elf64_Xword d_val; /* Integer value */ + Elf64_Addr d_ptr; /* Address value */ + } d_un; +} Elf64_Dyn; + +/* Legal values for d_tag (dynamic entry type). */ + +#define DT_NULL 0 /* Marks end of dynamic section */ +#define DT_NEEDED 1 /* Name of needed library */ +#define DT_PLTRELSZ 2 /* Size in bytes of PLT relocs */ +#define DT_PLTGOT 3 /* Processor defined value */ +#define DT_HASH 4 /* Address of symbol hash table */ +#define DT_STRTAB 5 /* Address of string table */ +#define DT_SYMTAB 6 /* Address of symbol table */ +#define DT_RELA 7 /* Address of Rela relocs */ +#define DT_RELASZ 8 /* Total size of Rela relocs */ +#define DT_RELAENT 9 /* Size of one Rela reloc */ +#define DT_STRSZ 10 /* Size of string table */ +#define DT_SYMENT 11 /* Size of one symbol table entry */ +#define DT_INIT 12 /* Address of init function */ +#define DT_FINI 13 /* Address of termination function */ +#define DT_SONAME 14 /* Name of shared object */ +#define DT_RPATH 15 /* Library search path */ +#define DT_SYMBOLIC 16 /* Start symbol search here */ +#define DT_REL 17 /* Address of Rel relocs */ +#define DT_RELSZ 18 /* Total size of Rel relocs */ +#define DT_RELENT 19 /* Size of one Rel reloc */ +#define DT_PLTREL 20 /* Type of reloc in PLT */ +#define DT_DEBUG 21 /* For debugging; unspecified */ +#define DT_TEXTREL 22 /* Reloc might modify .text */ +#define DT_JMPREL 23 /* Address of PLT relocs */ +#define DT_BIND_NOW 24 /* Process relocations of object */ +#define DT_INIT_ARRAY 25 /* Array with addresses of init fct */ +#define DT_FINI_ARRAY 26 /* Array with addresses of fini fct */ +#define DT_INIT_ARRAYSZ 27 /* Size in bytes of DT_INIT_ARRAY */ +#define DT_FINI_ARRAYSZ 28 /* Size in bytes of DT_FINI_ARRAY */ +#define DT_RUNPATH 29 /* Library search path */ +#define DT_FLAGS 30 /* Flags for the object being loaded */ +#define DT_ENCODING 32 /* Start of encoded range */ +#define DT_PREINIT_ARRAY 32 /* Array with addresses of preinit fct*/ +#define DT_PREINIT_ARRAYSZ 33 /* size in bytes of DT_PREINIT_ARRAY */ +#define DT_NUM 34 /* Number used */ +#define DT_LOOS 0x60000000 /* Start of OS-specific */ +#define DT_HIOS 0x6fffffff /* End of OS-specific */ +#define DT_LOPROC 0x70000000 /* Start of processor-specific */ +#define DT_HIPROC 0x7fffffff /* End of processor-specific */ +#define DT_PROCNUM DT_MIPS_NUM /* Most used by any processor */ + +/* DT_* entries which fall between DT_VALRNGHI & DT_VALRNGLO use the + Dyn.d_un.d_val field of the Elf*_Dyn structure. This follows Sun's + approach. */ +#define DT_VALRNGLO 0x6ffffd00 +#define DT_GNU_PRELINKED 0x6ffffdf5 /* Prelinking timestamp */ +#define DT_GNU_CONFLICTSZ 0x6ffffdf6 /* Size of conflict section */ +#define DT_GNU_LIBLISTSZ 0x6ffffdf7 /* Size of library list */ +#define DT_CHECKSUM 0x6ffffdf8 +#define DT_PLTPADSZ 0x6ffffdf9 +#define DT_MOVEENT 0x6ffffdfa +#define DT_MOVESZ 0x6ffffdfb +#define DT_FEATURE_1 0x6ffffdfc /* Feature selection (DTF_*). */ +#define DT_POSFLAG_1 0x6ffffdfd /* Flags for DT_* entries, effecting + the following DT_* entry. */ +#define DT_SYMINSZ 0x6ffffdfe /* Size of syminfo table (in bytes) */ +#define DT_SYMINENT 0x6ffffdff /* Entry size of syminfo */ +#define DT_VALRNGHI 0x6ffffdff +#define DT_VALTAGIDX(tag) (DT_VALRNGHI - (tag)) /* Reverse order! */ +#define DT_VALNUM 12 + +/* DT_* entries which fall between DT_ADDRRNGHI & DT_ADDRRNGLO use the + Dyn.d_un.d_ptr field of the Elf*_Dyn structure. + + If any adjustment is made to the ELF object after it has been + built these entries will need to be adjusted. */ +#define DT_ADDRRNGLO 0x6ffffe00 +#define DT_GNU_CONFLICT 0x6ffffef8 /* Start of conflict section */ +#define DT_GNU_LIBLIST 0x6ffffef9 /* Library list */ +#define DT_CONFIG 0x6ffffefa /* Configuration information. */ +#define DT_DEPAUDIT 0x6ffffefb /* Dependency auditing. */ +#define DT_AUDIT 0x6ffffefc /* Object auditing. */ +#define DT_PLTPAD 0x6ffffefd /* PLT padding. */ +#define DT_MOVETAB 0x6ffffefe /* Move table. */ +#define DT_SYMINFO 0x6ffffeff /* Syminfo table. */ +#define DT_ADDRRNGHI 0x6ffffeff +#define DT_ADDRTAGIDX(tag) (DT_ADDRRNGHI - (tag)) /* Reverse order! */ +#define DT_ADDRNUM 10 + +/* The versioning entry types. The next are defined as part of the + GNU extension. */ +#define DT_VERSYM 0x6ffffff0 + +#define DT_RELACOUNT 0x6ffffff9 +#define DT_RELCOUNT 0x6ffffffa + +/* These were chosen by Sun. */ +#define DT_FLAGS_1 0x6ffffffb /* State flags, see DF_1_* below. */ +#define DT_VERDEF 0x6ffffffc /* Address of version definition + table */ +#define DT_VERDEFNUM 0x6ffffffd /* Number of version definitions */ +#define DT_VERNEED 0x6ffffffe /* Address of table with needed + versions */ +#define DT_VERNEEDNUM 0x6fffffff /* Number of needed versions */ +#define DT_VERSIONTAGIDX(tag) (DT_VERNEEDNUM - (tag)) /* Reverse order! */ +#define DT_VERSIONTAGNUM 16 + +/* Sun added these machine-independent extensions in the "processor-specific" + range. Be compatible. */ +#define DT_AUXILIARY 0x7ffffffd /* Shared object to load before self */ +#define DT_FILTER 0x7fffffff /* Shared object to get values from */ +#define DT_EXTRATAGIDX(tag) ((Elf32_Word)-((Elf32_Sword) (tag) <<1>>1)-1) +#define DT_EXTRANUM 3 + +/* Values of `d_un.d_val' in the DT_FLAGS entry. */ +#define DF_ORIGIN 0x00000001 /* Object may use DF_ORIGIN */ +#define DF_SYMBOLIC 0x00000002 /* Symbol resolutions starts here */ +#define DF_TEXTREL 0x00000004 /* Object contains text relocations */ +#define DF_BIND_NOW 0x00000008 /* No lazy binding for this object */ +#define DF_STATIC_TLS 0x00000010 /* Module uses the static TLS model */ + +/* State flags selectable in the `d_un.d_val' element of the DT_FLAGS_1 + entry in the dynamic section. */ +#define DF_1_NOW 0x00000001 /* Set RTLD_NOW for this object. */ +#define DF_1_GLOBAL 0x00000002 /* Set RTLD_GLOBAL for this object. */ +#define DF_1_GROUP 0x00000004 /* Set RTLD_GROUP for this object. */ +#define DF_1_NODELETE 0x00000008 /* Set RTLD_NODELETE for this object.*/ +#define DF_1_LOADFLTR 0x00000010 /* Trigger filtee loading at runtime.*/ +#define DF_1_INITFIRST 0x00000020 /* Set RTLD_INITFIRST for this object*/ +#define DF_1_NOOPEN 0x00000040 /* Set RTLD_NOOPEN for this object. */ +#define DF_1_ORIGIN 0x00000080 /* $ORIGIN must be handled. */ +#define DF_1_DIRECT 0x00000100 /* Direct binding enabled. */ +#define DF_1_TRANS 0x00000200 +#define DF_1_INTERPOSE 0x00000400 /* Object is used to interpose. */ +#define DF_1_NODEFLIB 0x00000800 /* Ignore default lib search path. */ +#define DF_1_NODUMP 0x00001000 /* Object can't be dldump'ed. */ +#define DF_1_CONFALT 0x00002000 /* Configuration alternative created.*/ +#define DF_1_ENDFILTEE 0x00004000 /* Filtee terminates filters search. */ +#define DF_1_DISPRELDNE 0x00008000 /* Disp reloc applied at build time. */ +#define DF_1_DISPRELPND 0x00010000 /* Disp reloc applied at run-time. */ + +/* Flags for the feature selection in DT_FEATURE_1. */ +#define DTF_1_PARINIT 0x00000001 +#define DTF_1_CONFEXP 0x00000002 + +/* Flags in the DT_POSFLAG_1 entry effecting only the next DT_* entry. */ +#define DF_P1_LAZYLOAD 0x00000001 /* Lazyload following object. */ +#define DF_P1_GROUPPERM 0x00000002 /* Symbols from next object are not + generally available. */ + +/* Version definition sections. */ + +typedef struct +{ + Elf32_Half vd_version; /* Version revision */ + Elf32_Half vd_flags; /* Version information */ + Elf32_Half vd_ndx; /* Version Index */ + Elf32_Half vd_cnt; /* Number of associated aux entries */ + Elf32_Word vd_hash; /* Version name hash value */ + Elf32_Word vd_aux; /* Offset in bytes to verdaux array */ + Elf32_Word vd_next; /* Offset in bytes to next verdef + entry */ +} Elf32_Verdef; + +typedef struct +{ + Elf64_Half vd_version; /* Version revision */ + Elf64_Half vd_flags; /* Version information */ + Elf64_Half vd_ndx; /* Version Index */ + Elf64_Half vd_cnt; /* Number of associated aux entries */ + Elf64_Word vd_hash; /* Version name hash value */ + Elf64_Word vd_aux; /* Offset in bytes to verdaux array */ + Elf64_Word vd_next; /* Offset in bytes to next verdef + entry */ +} Elf64_Verdef; + + +/* Legal values for vd_version (version revision). */ +#define VER_DEF_NONE 0 /* No version */ +#define VER_DEF_CURRENT 1 /* Current version */ +#define VER_DEF_NUM 2 /* Given version number */ + +/* Legal values for vd_flags (version information flags). */ +#define VER_FLG_BASE 0x1 /* Version definition of file itself */ +#define VER_FLG_WEAK 0x2 /* Weak version identifier */ + +/* Versym symbol index values. */ +#define VER_NDX_LOCAL 0 /* Symbol is local. */ +#define VER_NDX_GLOBAL 1 /* Symbol is global. */ +#define VER_NDX_LORESERVE 0xff00 /* Beginning of reserved entries. */ +#define VER_NDX_ELIMINATE 0xff01 /* Symbol is to be eliminated. */ + +/* Auxialiary version information. */ + +typedef struct +{ + Elf32_Word vda_name; /* Version or dependency names */ + Elf32_Word vda_next; /* Offset in bytes to next verdaux + entry */ +} Elf32_Verdaux; + +typedef struct +{ + Elf64_Word vda_name; /* Version or dependency names */ + Elf64_Word vda_next; /* Offset in bytes to next verdaux + entry */ +} Elf64_Verdaux; + + +/* Version dependency section. */ + +typedef struct +{ + Elf32_Half vn_version; /* Version of structure */ + Elf32_Half vn_cnt; /* Number of associated aux entries */ + Elf32_Word vn_file; /* Offset of filename for this + dependency */ + Elf32_Word vn_aux; /* Offset in bytes to vernaux array */ + Elf32_Word vn_next; /* Offset in bytes to next verneed + entry */ +} Elf32_Verneed; + +typedef struct +{ + Elf64_Half vn_version; /* Version of structure */ + Elf64_Half vn_cnt; /* Number of associated aux entries */ + Elf64_Word vn_file; /* Offset of filename for this + dependency */ + Elf64_Word vn_aux; /* Offset in bytes to vernaux array */ + Elf64_Word vn_next; /* Offset in bytes to next verneed + entry */ +} Elf64_Verneed; + + +/* Legal values for vn_version (version revision). */ +#define VER_NEED_NONE 0 /* No version */ +#define VER_NEED_CURRENT 1 /* Current version */ +#define VER_NEED_NUM 2 /* Given version number */ + +/* Auxiliary needed version information. */ + +typedef struct +{ + Elf32_Word vna_hash; /* Hash value of dependency name */ + Elf32_Half vna_flags; /* Dependency specific information */ + Elf32_Half vna_other; /* Unused */ + Elf32_Word vna_name; /* Dependency name string offset */ + Elf32_Word vna_next; /* Offset in bytes to next vernaux + entry */ +} Elf32_Vernaux; + +typedef struct +{ + Elf64_Word vna_hash; /* Hash value of dependency name */ + Elf64_Half vna_flags; /* Dependency specific information */ + Elf64_Half vna_other; /* Unused */ + Elf64_Word vna_name; /* Dependency name string offset */ + Elf64_Word vna_next; /* Offset in bytes to next vernaux + entry */ +} Elf64_Vernaux; + + +/* Legal values for vna_flags. */ +#define VER_FLG_WEAK 0x2 /* Weak version identifier */ + + +/* Auxiliary vector. */ + +/* This vector is normally only used by the program interpreter. The + usual definition in an ABI supplement uses the name auxv_t. The + vector is not usually defined in a standard <elf.h> file, but it + can't hurt. We rename it to avoid conflicts. The sizes of these + types are an arrangement between the exec server and the program + interpreter, so we don't fully specify them here. */ + +typedef struct +{ + int a_type; /* Entry type */ + union + { + long int a_val; /* Integer value */ + void *a_ptr; /* Pointer value */ + void (*a_fcn) (void); /* Function pointer value */ + } a_un; +} Elf32_auxv_t; + +typedef struct +{ + long int a_type; /* Entry type */ + union + { + long int a_val; /* Integer value */ + void *a_ptr; /* Pointer value */ + void (*a_fcn) (void); /* Function pointer value */ + } a_un; +} Elf64_auxv_t; + +/* Legal values for a_type (entry type). */ + +#define AT_NULL 0 /* End of vector */ +#define AT_IGNORE 1 /* Entry should be ignored */ +#define AT_EXECFD 2 /* File descriptor of program */ +#define AT_PHDR 3 /* Program headers for program */ +#define AT_PHENT 4 /* Size of program header entry */ +#define AT_PHNUM 5 /* Number of program headers */ +#define AT_PAGESZ 6 /* System page size */ +#define AT_BASE 7 /* Base address of interpreter */ +#define AT_FLAGS 8 /* Flags */ +#define AT_ENTRY 9 /* Entry point of program */ +#define AT_NOTELF 10 /* Program is not ELF */ +#define AT_UID 11 /* Real uid */ +#define AT_EUID 12 /* Effective uid */ +#define AT_GID 13 /* Real gid */ +#define AT_EGID 14 /* Effective gid */ +#define AT_CLKTCK 17 /* Frequency of times() */ + +/* Some more special a_type values describing the hardware. */ +#define AT_PLATFORM 15 /* String identifying platform. */ +#define AT_HWCAP 16 /* Machine dependent hints about + processor capabilities. */ + +/* This entry gives some information about the FPU initialization + performed by the kernel. */ +#define AT_FPUCW 18 /* Used FPU control word. */ + +/* Cache block sizes. */ +#define AT_DCACHEBSIZE 19 /* Data cache block size. */ +#define AT_ICACHEBSIZE 20 /* Instruction cache block size. */ +#define AT_UCACHEBSIZE 21 /* Unified cache block size. */ + +/* A special ignored value for PPC, used by the kernel to control the + interpretation of the AUXV. Must be > 16. */ +#define AT_IGNOREPPC 22 /* Entry should be ignored. */ + +#define AT_SECURE 23 /* Boolean, was exec setuid-like? */ + +/* Pointer to the global system page used for system calls and other + nice things. */ +#define AT_SYSINFO 32 +#define AT_SYSINFO_EHDR 33 + + +/* Note section contents. Each entry in the note section begins with + a header of a fixed form. */ + +typedef struct +{ + Elf32_Word n_namesz; /* Length of the note's name. */ + Elf32_Word n_descsz; /* Length of the note's descriptor. */ + Elf32_Word n_type; /* Type of the note. */ +} Elf32_Nhdr; + +typedef struct +{ + Elf64_Word n_namesz; /* Length of the note's name. */ + Elf64_Word n_descsz; /* Length of the note's descriptor. */ + Elf64_Word n_type; /* Type of the note. */ +} Elf64_Nhdr; + +/* Known names of notes. */ + +/* Solaris entries in the note section have this name. */ +#define ELF_NOTE_SOLARIS "SUNW Solaris" + +/* Note entries for GNU systems have this name. */ +#define ELF_NOTE_GNU "GNU" + + +/* Defined types of notes for Solaris. */ + +/* Value of descriptor (one word) is desired pagesize for the binary. */ +#define ELF_NOTE_PAGESIZE_HINT 1 + + +/* Defined note types for GNU systems. */ + +/* ABI information. The descriptor consists of words: + word 0: OS descriptor + word 1: major version of the ABI + word 2: minor version of the ABI + word 3: subminor version of the ABI +*/ +#define ELF_NOTE_ABI 1 + +/* Known OSes. These value can appear in word 0 of an ELF_NOTE_ABI + note section entry. */ +#define ELF_NOTE_OS_LINUX 0 +#define ELF_NOTE_OS_GNU 1 +#define ELF_NOTE_OS_SOLARIS2 2 +#define ELF_NOTE_OS_FREEBSD 3 + + +/* Move records. */ +typedef struct +{ + Elf32_Xword m_value; /* Symbol value. */ + Elf32_Word m_info; /* Size and index. */ + Elf32_Word m_poffset; /* Symbol offset. */ + Elf32_Half m_repeat; /* Repeat count. */ + Elf32_Half m_stride; /* Stride info. */ +} Elf32_Move; + +typedef struct +{ + Elf64_Xword m_value; /* Symbol value. */ + Elf64_Xword m_info; /* Size and index. */ + Elf64_Xword m_poffset; /* Symbol offset. */ + Elf64_Half m_repeat; /* Repeat count. */ + Elf64_Half m_stride; /* Stride info. */ +} Elf64_Move; + +/* Macro to construct move records. */ +#define ELF32_M_SYM(info) ((info) >> 8) +#define ELF32_M_SIZE(info) ((unsigned char) (info)) +#define ELF32_M_INFO(sym, size) (((sym) << 8) + (unsigned char) (size)) + +#define ELF64_M_SYM(info) ELF32_M_SYM (info) +#define ELF64_M_SIZE(info) ELF32_M_SIZE (info) +#define ELF64_M_INFO(sym, size) ELF32_M_INFO (sym, size) + + +/* Motorola 68k specific definitions. */ + +/* Values for Elf32_Ehdr.e_flags. */ +#define EF_CPU32 0x00810000 + +/* m68k relocs. */ + +#define R_68K_NONE 0 /* No reloc */ +#define R_68K_32 1 /* Direct 32 bit */ +#define R_68K_16 2 /* Direct 16 bit */ +#define R_68K_8 3 /* Direct 8 bit */ +#define R_68K_PC32 4 /* PC relative 32 bit */ +#define R_68K_PC16 5 /* PC relative 16 bit */ +#define R_68K_PC8 6 /* PC relative 8 bit */ +#define R_68K_GOT32 7 /* 32 bit PC relative GOT entry */ +#define R_68K_GOT16 8 /* 16 bit PC relative GOT entry */ +#define R_68K_GOT8 9 /* 8 bit PC relative GOT entry */ +#define R_68K_GOT32O 10 /* 32 bit GOT offset */ +#define R_68K_GOT16O 11 /* 16 bit GOT offset */ +#define R_68K_GOT8O 12 /* 8 bit GOT offset */ +#define R_68K_PLT32 13 /* 32 bit PC relative PLT address */ +#define R_68K_PLT16 14 /* 16 bit PC relative PLT address */ +#define R_68K_PLT8 15 /* 8 bit PC relative PLT address */ +#define R_68K_PLT32O 16 /* 32 bit PLT offset */ +#define R_68K_PLT16O 17 /* 16 bit PLT offset */ +#define R_68K_PLT8O 18 /* 8 bit PLT offset */ +#define R_68K_COPY 19 /* Copy symbol at runtime */ +#define R_68K_GLOB_DAT 20 /* Create GOT entry */ +#define R_68K_JMP_SLOT 21 /* Create PLT entry */ +#define R_68K_RELATIVE 22 /* Adjust by program base */ +/* Keep this the last entry. */ +#define R_68K_NUM 23 + +/* Intel 80386 specific definitions. */ + +/* i386 relocs. */ + +#define R_386_NONE 0 /* No reloc */ +#define R_386_32 1 /* Direct 32 bit */ +#define R_386_PC32 2 /* PC relative 32 bit */ +#define R_386_GOT32 3 /* 32 bit GOT entry */ +#define R_386_PLT32 4 /* 32 bit PLT address */ +#define R_386_COPY 5 /* Copy symbol at runtime */ +#define R_386_GLOB_DAT 6 /* Create GOT entry */ +#define R_386_JMP_SLOT 7 /* Create PLT entry */ +#define R_386_RELATIVE 8 /* Adjust by program base */ +#define R_386_GOTOFF 9 /* 32 bit offset to GOT */ +#define R_386_GOTPC 10 /* 32 bit PC relative offset to GOT */ +#define R_386_32PLT 11 +#define R_386_TLS_TPOFF 14 /* Offset in static TLS block */ +#define R_386_TLS_IE 15 /* Address of GOT entry for static TLS + block offset */ +#define R_386_TLS_GOTIE 16 /* GOT entry for static TLS block + offset */ +#define R_386_TLS_LE 17 /* Offset relative to static TLS + block */ +#define R_386_TLS_GD 18 /* Direct 32 bit for GNU version of + general dynamic thread local data */ +#define R_386_TLS_LDM 19 /* Direct 32 bit for GNU version of + local dynamic thread local data + in LE code */ +#define R_386_16 20 +#define R_386_PC16 21 +#define R_386_8 22 +#define R_386_PC8 23 +#define R_386_TLS_GD_32 24 /* Direct 32 bit for general dynamic + thread local data */ +#define R_386_TLS_GD_PUSH 25 /* Tag for pushl in GD TLS code */ +#define R_386_TLS_GD_CALL 26 /* Relocation for call to + __tls_get_addr() */ +#define R_386_TLS_GD_POP 27 /* Tag for popl in GD TLS code */ +#define R_386_TLS_LDM_32 28 /* Direct 32 bit for local dynamic + thread local data in LE code */ +#define R_386_TLS_LDM_PUSH 29 /* Tag for pushl in LDM TLS code */ +#define R_386_TLS_LDM_CALL 30 /* Relocation for call to + __tls_get_addr() in LDM code */ +#define R_386_TLS_LDM_POP 31 /* Tag for popl in LDM TLS code */ +#define R_386_TLS_LDO_32 32 /* Offset relative to TLS block */ +#define R_386_TLS_IE_32 33 /* GOT entry for negated static TLS + block offset */ +#define R_386_TLS_LE_32 34 /* Negated offset relative to static + TLS block */ +#define R_386_TLS_DTPMOD32 35 /* ID of module containing symbol */ +#define R_386_TLS_DTPOFF32 36 /* Offset in TLS block */ +#define R_386_TLS_TPOFF32 37 /* Negated offset in static TLS block */ +/* Keep this the last entry. */ +#define R_386_NUM 38 + +/* SUN SPARC specific definitions. */ + +/* Legal values for ST_TYPE subfield of st_info (symbol type). */ + +#define STT_REGISTER 13 /* Global register reserved to app. */ + +/* Values for Elf64_Ehdr.e_flags. */ + +#define EF_SPARCV9_MM 3 +#define EF_SPARCV9_TSO 0 +#define EF_SPARCV9_PSO 1 +#define EF_SPARCV9_RMO 2 +#define EF_SPARC_LEDATA 0x800000 /* little endian data */ +#define EF_SPARC_EXT_MASK 0xFFFF00 +#define EF_SPARC_32PLUS 0x000100 /* generic V8+ features */ +#define EF_SPARC_SUN_US1 0x000200 /* Sun UltraSPARC1 extensions */ +#define EF_SPARC_HAL_R1 0x000400 /* HAL R1 extensions */ +#define EF_SPARC_SUN_US3 0x000800 /* Sun UltraSPARCIII extensions */ + +/* SPARC relocs. */ + +#define R_SPARC_NONE 0 /* No reloc */ +#define R_SPARC_8 1 /* Direct 8 bit */ +#define R_SPARC_16 2 /* Direct 16 bit */ +#define R_SPARC_32 3 /* Direct 32 bit */ +#define R_SPARC_DISP8 4 /* PC relative 8 bit */ +#define R_SPARC_DISP16 5 /* PC relative 16 bit */ +#define R_SPARC_DISP32 6 /* PC relative 32 bit */ +#define R_SPARC_WDISP30 7 /* PC relative 30 bit shifted */ +#define R_SPARC_WDISP22 8 /* PC relative 22 bit shifted */ +#define R_SPARC_HI22 9 /* High 22 bit */ +#define R_SPARC_22 10 /* Direct 22 bit */ +#define R_SPARC_13 11 /* Direct 13 bit */ +#define R_SPARC_LO10 12 /* Truncated 10 bit */ +#define R_SPARC_GOT10 13 /* Truncated 10 bit GOT entry */ +#define R_SPARC_GOT13 14 /* 13 bit GOT entry */ +#define R_SPARC_GOT22 15 /* 22 bit GOT entry shifted */ +#define R_SPARC_PC10 16 /* PC relative 10 bit truncated */ +#define R_SPARC_PC22 17 /* PC relative 22 bit shifted */ +#define R_SPARC_WPLT30 18 /* 30 bit PC relative PLT address */ +#define R_SPARC_COPY 19 /* Copy symbol at runtime */ +#define R_SPARC_GLOB_DAT 20 /* Create GOT entry */ +#define R_SPARC_JMP_SLOT 21 /* Create PLT entry */ +#define R_SPARC_RELATIVE 22 /* Adjust by program base */ +#define R_SPARC_UA32 23 /* Direct 32 bit unaligned */ + +/* Additional Sparc64 relocs. */ + +#define R_SPARC_PLT32 24 /* Direct 32 bit ref to PLT entry */ +#define R_SPARC_HIPLT22 25 /* High 22 bit PLT entry */ +#define R_SPARC_LOPLT10 26 /* Truncated 10 bit PLT entry */ +#define R_SPARC_PCPLT32 27 /* PC rel 32 bit ref to PLT entry */ +#define R_SPARC_PCPLT22 28 /* PC rel high 22 bit PLT entry */ +#define R_SPARC_PCPLT10 29 /* PC rel trunc 10 bit PLT entry */ +#define R_SPARC_10 30 /* Direct 10 bit */ +#define R_SPARC_11 31 /* Direct 11 bit */ +#define R_SPARC_64 32 /* Direct 64 bit */ +#define R_SPARC_OLO10 33 /* 10bit with secondary 13bit addend */ +#define R_SPARC_HH22 34 /* Top 22 bits of direct 64 bit */ +#define R_SPARC_HM10 35 /* High middle 10 bits of ... */ +#define R_SPARC_LM22 36 /* Low middle 22 bits of ... */ +#define R_SPARC_PC_HH22 37 /* Top 22 bits of pc rel 64 bit */ +#define R_SPARC_PC_HM10 38 /* High middle 10 bit of ... */ +#define R_SPARC_PC_LM22 39 /* Low miggle 22 bits of ... */ +#define R_SPARC_WDISP16 40 /* PC relative 16 bit shifted */ +#define R_SPARC_WDISP19 41 /* PC relative 19 bit shifted */ +#define R_SPARC_7 43 /* Direct 7 bit */ +#define R_SPARC_5 44 /* Direct 5 bit */ +#define R_SPARC_6 45 /* Direct 6 bit */ +#define R_SPARC_DISP64 46 /* PC relative 64 bit */ +#define R_SPARC_PLT64 47 /* Direct 64 bit ref to PLT entry */ +#define R_SPARC_HIX22 48 /* High 22 bit complemented */ +#define R_SPARC_LOX10 49 /* Truncated 11 bit complemented */ +#define R_SPARC_H44 50 /* Direct high 12 of 44 bit */ +#define R_SPARC_M44 51 /* Direct mid 22 of 44 bit */ +#define R_SPARC_L44 52 /* Direct low 10 of 44 bit */ +#define R_SPARC_REGISTER 53 /* Global register usage */ +#define R_SPARC_UA64 54 /* Direct 64 bit unaligned */ +#define R_SPARC_UA16 55 /* Direct 16 bit unaligned */ +#define R_SPARC_TLS_GD_HI22 56 +#define R_SPARC_TLS_GD_LO10 57 +#define R_SPARC_TLS_GD_ADD 58 +#define R_SPARC_TLS_GD_CALL 59 +#define R_SPARC_TLS_LDM_HI22 60 +#define R_SPARC_TLS_LDM_LO10 61 +#define R_SPARC_TLS_LDM_ADD 62 +#define R_SPARC_TLS_LDM_CALL 63 +#define R_SPARC_TLS_LDO_HIX22 64 +#define R_SPARC_TLS_LDO_LOX10 65 +#define R_SPARC_TLS_LDO_ADD 66 +#define R_SPARC_TLS_IE_HI22 67 +#define R_SPARC_TLS_IE_LO10 68 +#define R_SPARC_TLS_IE_LD 69 +#define R_SPARC_TLS_IE_LDX 70 +#define R_SPARC_TLS_IE_ADD 71 +#define R_SPARC_TLS_LE_HIX22 72 +#define R_SPARC_TLS_LE_LOX10 73 +#define R_SPARC_TLS_DTPMOD32 74 +#define R_SPARC_TLS_DTPMOD64 75 +#define R_SPARC_TLS_DTPOFF32 76 +#define R_SPARC_TLS_DTPOFF64 77 +#define R_SPARC_TLS_TPOFF32 78 +#define R_SPARC_TLS_TPOFF64 79 +/* Keep this the last entry. */ +#define R_SPARC_NUM 80 + +/* For Sparc64, legal values for d_tag of Elf64_Dyn. */ + +#define DT_SPARC_REGISTER 0x70000001 +#define DT_SPARC_NUM 2 + +/* Bits present in AT_HWCAP, primarily for Sparc32. */ + +#define HWCAP_SPARC_FLUSH 1 /* The cpu supports flush insn. */ +#define HWCAP_SPARC_STBAR 2 +#define HWCAP_SPARC_SWAP 4 +#define HWCAP_SPARC_MULDIV 8 +#define HWCAP_SPARC_V9 16 /* The cpu is v9, so v8plus is ok. */ +#define HWCAP_SPARC_ULTRA3 32 + +/* MIPS R3000 specific definitions. */ + +/* Legal values for e_flags field of Elf32_Ehdr. */ + +#define EF_MIPS_NOREORDER 1 /* A .noreorder directive was used */ +#define EF_MIPS_PIC 2 /* Contains PIC code */ +#define EF_MIPS_CPIC 4 /* Uses PIC calling sequence */ +#define EF_MIPS_XGOT 8 +#define EF_MIPS_64BIT_WHIRL 16 +#define EF_MIPS_ABI2 32 +#define EF_MIPS_ABI_ON32 64 +#define EF_MIPS_ARCH 0xf0000000 /* MIPS architecture level */ + +/* Legal values for MIPS architecture level. */ + +#define EF_MIPS_ARCH_1 0x00000000 /* -mips1 code. */ +#define EF_MIPS_ARCH_2 0x10000000 /* -mips2 code. */ +#define EF_MIPS_ARCH_3 0x20000000 /* -mips3 code. */ +#define EF_MIPS_ARCH_4 0x30000000 /* -mips4 code. */ +#define EF_MIPS_ARCH_5 0x40000000 /* -mips5 code. */ +#define EF_MIPS_ARCH_32 0x60000000 /* MIPS32 code. */ +#define EF_MIPS_ARCH_64 0x70000000 /* MIPS64 code. */ + +/* The following are non-official names and should not be used. */ + +#define E_MIPS_ARCH_1 0x00000000 /* -mips1 code. */ +#define E_MIPS_ARCH_2 0x10000000 /* -mips2 code. */ +#define E_MIPS_ARCH_3 0x20000000 /* -mips3 code. */ +#define E_MIPS_ARCH_4 0x30000000 /* -mips4 code. */ +#define E_MIPS_ARCH_5 0x40000000 /* -mips5 code. */ +#define E_MIPS_ARCH_32 0x60000000 /* MIPS32 code. */ +#define E_MIPS_ARCH_64 0x70000000 /* MIPS64 code. */ + +/* Special section indices. */ + +#define SHN_MIPS_ACOMMON 0xff00 /* Allocated common symbols */ +#define SHN_MIPS_TEXT 0xff01 /* Allocated test symbols. */ +#define SHN_MIPS_DATA 0xff02 /* Allocated data symbols. */ +#define SHN_MIPS_SCOMMON 0xff03 /* Small common symbols */ +#define SHN_MIPS_SUNDEFINED 0xff04 /* Small undefined symbols */ + +/* Legal values for sh_type field of Elf32_Shdr. */ + +#define SHT_MIPS_LIBLIST 0x70000000 /* Shared objects used in link */ +#define SHT_MIPS_MSYM 0x70000001 +#define SHT_MIPS_CONFLICT 0x70000002 /* Conflicting symbols */ +#define SHT_MIPS_GPTAB 0x70000003 /* Global data area sizes */ +#define SHT_MIPS_UCODE 0x70000004 /* Reserved for SGI/MIPS compilers */ +#define SHT_MIPS_DEBUG 0x70000005 /* MIPS ECOFF debugging information*/ +#define SHT_MIPS_REGINFO 0x70000006 /* Register usage information */ +#define SHT_MIPS_PACKAGE 0x70000007 +#define SHT_MIPS_PACKSYM 0x70000008 +#define SHT_MIPS_RELD 0x70000009 +#define SHT_MIPS_IFACE 0x7000000b +#define SHT_MIPS_CONTENT 0x7000000c +#define SHT_MIPS_OPTIONS 0x7000000d /* Miscellaneous options. */ +#define SHT_MIPS_SHDR 0x70000010 +#define SHT_MIPS_FDESC 0x70000011 +#define SHT_MIPS_EXTSYM 0x70000012 +#define SHT_MIPS_DENSE 0x70000013 +#define SHT_MIPS_PDESC 0x70000014 +#define SHT_MIPS_LOCSYM 0x70000015 +#define SHT_MIPS_AUXSYM 0x70000016 +#define SHT_MIPS_OPTSYM 0x70000017 +#define SHT_MIPS_LOCSTR 0x70000018 +#define SHT_MIPS_LINE 0x70000019 +#define SHT_MIPS_RFDESC 0x7000001a +#define SHT_MIPS_DELTASYM 0x7000001b +#define SHT_MIPS_DELTAINST 0x7000001c +#define SHT_MIPS_DELTACLASS 0x7000001d +#define SHT_MIPS_DWARF 0x7000001e /* DWARF debugging information. */ +#define SHT_MIPS_DELTADECL 0x7000001f +#define SHT_MIPS_SYMBOL_LIB 0x70000020 +#define SHT_MIPS_EVENTS 0x70000021 /* Event section. */ +#define SHT_MIPS_TRANSLATE 0x70000022 +#define SHT_MIPS_PIXIE 0x70000023 +#define SHT_MIPS_XLATE 0x70000024 +#define SHT_MIPS_XLATE_DEBUG 0x70000025 +#define SHT_MIPS_WHIRL 0x70000026 +#define SHT_MIPS_EH_REGION 0x70000027 +#define SHT_MIPS_XLATE_OLD 0x70000028 +#define SHT_MIPS_PDR_EXCEPTION 0x70000029 + +/* Legal values for sh_flags field of Elf32_Shdr. */ + +#define SHF_MIPS_GPREL 0x10000000 /* Must be part of global data area */ +#define SHF_MIPS_MERGE 0x20000000 +#define SHF_MIPS_ADDR 0x40000000 +#define SHF_MIPS_STRINGS 0x80000000 +#define SHF_MIPS_NOSTRIP 0x08000000 +#define SHF_MIPS_LOCAL 0x04000000 +#define SHF_MIPS_NAMES 0x02000000 +#define SHF_MIPS_NODUPE 0x01000000 + + +/* Symbol tables. */ + +/* MIPS specific values for `st_other'. */ +#define STO_MIPS_DEFAULT 0x0 +#define STO_MIPS_INTERNAL 0x1 +#define STO_MIPS_HIDDEN 0x2 +#define STO_MIPS_PROTECTED 0x3 +#define STO_MIPS_SC_ALIGN_UNUSED 0xff + +/* MIPS specific values for `st_info'. */ +#define STB_MIPS_SPLIT_COMMON 13 + +/* Entries found in sections of type SHT_MIPS_GPTAB. */ + +typedef union +{ + struct + { + Elf32_Word gt_current_g_value; /* -G value used for compilation */ + Elf32_Word gt_unused; /* Not used */ + } gt_header; /* First entry in section */ + struct + { + Elf32_Word gt_g_value; /* If this value were used for -G */ + Elf32_Word gt_bytes; /* This many bytes would be used */ + } gt_entry; /* Subsequent entries in section */ +} Elf32_gptab; + +/* Entry found in sections of type SHT_MIPS_REGINFO. */ + +typedef struct +{ + Elf32_Word ri_gprmask; /* General registers used */ + Elf32_Word ri_cprmask[4]; /* Coprocessor registers used */ + Elf32_Sword ri_gp_value; /* $gp register value */ +} Elf32_RegInfo; + +/* Entries found in sections of type SHT_MIPS_OPTIONS. */ + +typedef struct +{ + unsigned char kind; /* Determines interpretation of the + variable part of descriptor. */ + unsigned char size; /* Size of descriptor, including header. */ + Elf32_Section section; /* Section header index of section affected, + 0 for global options. */ + Elf32_Word info; /* Kind-specific information. */ +} Elf_Options; + +/* Values for `kind' field in Elf_Options. */ + +#define ODK_NULL 0 /* Undefined. */ +#define ODK_REGINFO 1 /* Register usage information. */ +#define ODK_EXCEPTIONS 2 /* Exception processing options. */ +#define ODK_PAD 3 /* Section padding options. */ +#define ODK_HWPATCH 4 /* Hardware workarounds performed */ +#define ODK_FILL 5 /* record the fill value used by the linker. */ +#define ODK_TAGS 6 /* reserve space for desktop tools to write. */ +#define ODK_HWAND 7 /* HW workarounds. 'AND' bits when merging. */ +#define ODK_HWOR 8 /* HW workarounds. 'OR' bits when merging. */ + +/* Values for `info' in Elf_Options for ODK_EXCEPTIONS entries. */ + +#define OEX_FPU_MIN 0x1f /* FPE's which MUST be enabled. */ +#define OEX_FPU_MAX 0x1f00 /* FPE's which MAY be enabled. */ +#define OEX_PAGE0 0x10000 /* page zero must be mapped. */ +#define OEX_SMM 0x20000 /* Force sequential memory mode? */ +#define OEX_FPDBUG 0x40000 /* Force floating point debug mode? */ +#define OEX_PRECISEFP OEX_FPDBUG +#define OEX_DISMISS 0x80000 /* Dismiss invalid address faults? */ + +#define OEX_FPU_INVAL 0x10 +#define OEX_FPU_DIV0 0x08 +#define OEX_FPU_OFLO 0x04 +#define OEX_FPU_UFLO 0x02 +#define OEX_FPU_INEX 0x01 + +/* Masks for `info' in Elf_Options for an ODK_HWPATCH entry. */ + +#define OHW_R4KEOP 0x1 /* R4000 end-of-page patch. */ +#define OHW_R8KPFETCH 0x2 /* may need R8000 prefetch patch. */ +#define OHW_R5KEOP 0x4 /* R5000 end-of-page patch. */ +#define OHW_R5KCVTL 0x8 /* R5000 cvt.[ds].l bug. clean=1. */ + +#define OPAD_PREFIX 0x1 +#define OPAD_POSTFIX 0x2 +#define OPAD_SYMBOL 0x4 + +/* Entry found in `.options' section. */ + +typedef struct +{ + Elf32_Word hwp_flags1; /* Extra flags. */ + Elf32_Word hwp_flags2; /* Extra flags. */ +} Elf_Options_Hw; + +/* Masks for `info' in ElfOptions for ODK_HWAND and ODK_HWOR entries. */ + +#define OHWA0_R4KEOP_CHECKED 0x00000001 +#define OHWA1_R4KEOP_CLEAN 0x00000002 + +/* MIPS relocs. */ + +#define R_MIPS_NONE 0 /* No reloc */ +#define R_MIPS_16 1 /* Direct 16 bit */ +#define R_MIPS_32 2 /* Direct 32 bit */ +#define R_MIPS_REL32 3 /* PC relative 32 bit */ +#define R_MIPS_26 4 /* Direct 26 bit shifted */ +#define R_MIPS_HI16 5 /* High 16 bit */ +#define R_MIPS_LO16 6 /* Low 16 bit */ +#define R_MIPS_GPREL16 7 /* GP relative 16 bit */ +#define R_MIPS_LITERAL 8 /* 16 bit literal entry */ +#define R_MIPS_GOT16 9 /* 16 bit GOT entry */ +#define R_MIPS_PC16 10 /* PC relative 16 bit */ +#define R_MIPS_CALL16 11 /* 16 bit GOT entry for function */ +#define R_MIPS_GPREL32 12 /* GP relative 32 bit */ + +#define R_MIPS_SHIFT5 16 +#define R_MIPS_SHIFT6 17 +#define R_MIPS_64 18 +#define R_MIPS_GOT_DISP 19 +#define R_MIPS_GOT_PAGE 20 +#define R_MIPS_GOT_OFST 21 +#define R_MIPS_GOT_HI16 22 +#define R_MIPS_GOT_LO16 23 +#define R_MIPS_SUB 24 +#define R_MIPS_INSERT_A 25 +#define R_MIPS_INSERT_B 26 +#define R_MIPS_DELETE 27 +#define R_MIPS_HIGHER 28 +#define R_MIPS_HIGHEST 29 +#define R_MIPS_CALL_HI16 30 +#define R_MIPS_CALL_LO16 31 +#define R_MIPS_SCN_DISP 32 +#define R_MIPS_REL16 33 +#define R_MIPS_ADD_IMMEDIATE 34 +#define R_MIPS_PJUMP 35 +#define R_MIPS_RELGOT 36 +#define R_MIPS_JALR 37 +/* Keep this the last entry. */ +#define R_MIPS_NUM 38 + +/* Legal values for p_type field of Elf32_Phdr. */ + +#define PT_MIPS_REGINFO 0x70000000 /* Register usage information */ +#define PT_MIPS_RTPROC 0x70000001 /* Runtime procedure table. */ +#define PT_MIPS_OPTIONS 0x70000002 + +/* Special program header types. */ + +#define PF_MIPS_LOCAL 0x10000000 + +/* Legal values for d_tag field of Elf32_Dyn. */ + +#define DT_MIPS_RLD_VERSION 0x70000001 /* Runtime linker interface version */ +#define DT_MIPS_TIME_STAMP 0x70000002 /* Timestamp */ +#define DT_MIPS_ICHECKSUM 0x70000003 /* Checksum */ +#define DT_MIPS_IVERSION 0x70000004 /* Version string (string tbl index) */ +#define DT_MIPS_FLAGS 0x70000005 /* Flags */ +#define DT_MIPS_BASE_ADDRESS 0x70000006 /* Base address */ +#define DT_MIPS_MSYM 0x70000007 +#define DT_MIPS_CONFLICT 0x70000008 /* Address of CONFLICT section */ +#define DT_MIPS_LIBLIST 0x70000009 /* Address of LIBLIST section */ +#define DT_MIPS_LOCAL_GOTNO 0x7000000a /* Number of local GOT entries */ +#define DT_MIPS_CONFLICTNO 0x7000000b /* Number of CONFLICT entries */ +#define DT_MIPS_LIBLISTNO 0x70000010 /* Number of LIBLIST entries */ +#define DT_MIPS_SYMTABNO 0x70000011 /* Number of DYNSYM entries */ +#define DT_MIPS_UNREFEXTNO 0x70000012 /* First external DYNSYM */ +#define DT_MIPS_GOTSYM 0x70000013 /* First GOT entry in DYNSYM */ +#define DT_MIPS_HIPAGENO 0x70000014 /* Number of GOT page table entries */ +#define DT_MIPS_RLD_MAP 0x70000016 /* Address of run time loader map. */ +#define DT_MIPS_DELTA_CLASS 0x70000017 /* Delta C++ class definition. */ +#define DT_MIPS_DELTA_CLASS_NO 0x70000018 /* Number of entries in + DT_MIPS_DELTA_CLASS. */ +#define DT_MIPS_DELTA_INSTANCE 0x70000019 /* Delta C++ class instances. */ +#define DT_MIPS_DELTA_INSTANCE_NO 0x7000001a /* Number of entries in + DT_MIPS_DELTA_INSTANCE. */ +#define DT_MIPS_DELTA_RELOC 0x7000001b /* Delta relocations. */ +#define DT_MIPS_DELTA_RELOC_NO 0x7000001c /* Number of entries in + DT_MIPS_DELTA_RELOC. */ +#define DT_MIPS_DELTA_SYM 0x7000001d /* Delta symbols that Delta + relocations refer to. */ +#define DT_MIPS_DELTA_SYM_NO 0x7000001e /* Number of entries in + DT_MIPS_DELTA_SYM. */ +#define DT_MIPS_DELTA_CLASSSYM 0x70000020 /* Delta symbols that hold the + class declaration. */ +#define DT_MIPS_DELTA_CLASSSYM_NO 0x70000021 /* Number of entries in + DT_MIPS_DELTA_CLASSSYM. */ +#define DT_MIPS_CXX_FLAGS 0x70000022 /* Flags indicating for C++ flavor. */ +#define DT_MIPS_PIXIE_INIT 0x70000023 +#define DT_MIPS_SYMBOL_LIB 0x70000024 +#define DT_MIPS_LOCALPAGE_GOTIDX 0x70000025 +#define DT_MIPS_LOCAL_GOTIDX 0x70000026 +#define DT_MIPS_HIDDEN_GOTIDX 0x70000027 +#define DT_MIPS_PROTECTED_GOTIDX 0x70000028 +#define DT_MIPS_OPTIONS 0x70000029 /* Address of .options. */ +#define DT_MIPS_INTERFACE 0x7000002a /* Address of .interface. */ +#define DT_MIPS_DYNSTR_ALIGN 0x7000002b +#define DT_MIPS_INTERFACE_SIZE 0x7000002c /* Size of the .interface section. */ +#define DT_MIPS_RLD_TEXT_RESOLVE_ADDR 0x7000002d /* Address of rld_text_rsolve + function stored in GOT. */ +#define DT_MIPS_PERF_SUFFIX 0x7000002e /* Default suffix of dso to be added + by rld on dlopen() calls. */ +#define DT_MIPS_COMPACT_SIZE 0x7000002f /* (O32)Size of compact rel section. */ +#define DT_MIPS_GP_VALUE 0x70000030 /* GP value for aux GOTs. */ +#define DT_MIPS_AUX_DYNAMIC 0x70000031 /* Address of aux .dynamic. */ +#define DT_MIPS_NUM 0x32 + +/* Legal values for DT_MIPS_FLAGS Elf32_Dyn entry. */ + +#define RHF_NONE 0 /* No flags */ +#define RHF_QUICKSTART (1 << 0) /* Use quickstart */ +#define RHF_NOTPOT (1 << 1) /* Hash size not power of 2 */ +#define RHF_NO_LIBRARY_REPLACEMENT (1 << 2) /* Ignore LD_LIBRARY_PATH */ +#define RHF_NO_MOVE (1 << 3) +#define RHF_SGI_ONLY (1 << 4) +#define RHF_GUARANTEE_INIT (1 << 5) +#define RHF_DELTA_C_PLUS_PLUS (1 << 6) +#define RHF_GUARANTEE_START_INIT (1 << 7) +#define RHF_PIXIE (1 << 8) +#define RHF_DEFAULT_DELAY_LOAD (1 << 9) +#define RHF_REQUICKSTART (1 << 10) +#define RHF_REQUICKSTARTED (1 << 11) +#define RHF_CORD (1 << 12) +#define RHF_NO_UNRES_UNDEF (1 << 13) +#define RHF_RLD_ORDER_SAFE (1 << 14) + +/* Entries found in sections of type SHT_MIPS_LIBLIST. */ + +typedef struct +{ + Elf32_Word l_name; /* Name (string table index) */ + Elf32_Word l_time_stamp; /* Timestamp */ + Elf32_Word l_checksum; /* Checksum */ + Elf32_Word l_version; /* Interface version */ + Elf32_Word l_flags; /* Flags */ +} Elf32_Lib; + +typedef struct +{ + Elf64_Word l_name; /* Name (string table index) */ + Elf64_Word l_time_stamp; /* Timestamp */ + Elf64_Word l_checksum; /* Checksum */ + Elf64_Word l_version; /* Interface version */ + Elf64_Word l_flags; /* Flags */ +} Elf64_Lib; + + +/* Legal values for l_flags. */ + +#define LL_NONE 0 +#define LL_EXACT_MATCH (1 << 0) /* Require exact match */ +#define LL_IGNORE_INT_VER (1 << 1) /* Ignore interface version */ +#define LL_REQUIRE_MINOR (1 << 2) +#define LL_EXPORTS (1 << 3) +#define LL_DELAY_LOAD (1 << 4) +#define LL_DELTA (1 << 5) + +/* Entries found in sections of type SHT_MIPS_CONFLICT. */ + +typedef Elf32_Addr Elf32_Conflict; + + +/* HPPA specific definitions. */ + +/* Legal values for e_flags field of Elf32_Ehdr. */ + +#define EF_PARISC_TRAPNL 1 /* Trap nil pointer dereference. */ +#define EF_PARISC_EXT 2 /* Program uses arch. extensions. */ +#define EF_PARISC_ARCH 0xffff0000 /* Architecture version. */ +/* Defined values are: + 0x020b PA-RISC 1.0 big-endian + 0x0210 PA-RISC 1.1 big-endian + 0x028b PA-RISC 1.0 little-endian + 0x0290 PA-RISC 1.1 little-endian +*/ + +/* Legal values for sh_type field of Elf32_Shdr. */ + +#define SHT_PARISC_GOT 0x70000000 /* GOT for external data. */ +#define SHT_PARISC_ARCH 0x70000001 /* Architecture extensions. */ +#define SHT_PARISC_GLOBAL 0x70000002 /* Definition of $global$. */ +#define SHT_PARISC_MILLI 0x70000003 /* Millicode routines. */ +#define SHT_PARISC_UNWIND 0x70000004 /* Unwind information. */ +#define SHT_PARISC_PLT 0x70000005 /* Procedure linkage table. */ +#define SHT_PARISC_SDATA 0x70000006 /* Short initialized data. */ +#define SHT_PARISC_SBSS 0x70000007 /* Short uninitialized data. */ +#define SHT_PARISC_SYMEXTN 0x70000008 /* Argument/relocation info. */ +#define SHT_PARISC_STUBS 0x70000009 /* Linker stubs. */ + +/* Legal values for sh_flags field of Elf32_Shdr. */ + +#define SHF_PARISC_GLOBAL 0x10000000 /* Section defines dp. */ +#define SHF_PARISC_SHORT 0x20000000 /* Section with short addressing. */ +#define SHF_PARISC_HUGE 0x40000000 /* Section far from gp. */ +#define SHF_PARISC_SBP 0x80000000 /* Static branch prediction code. */ + +/* Legal values for ST_TYPE subfield of st_info (symbol type). */ + +#define STT_PARISC_MILLICODE 13 /* Millicode function entry point. */ + +#define STT_HP_OPAQUE (STT_LOOS + 0x1) +#define STT_HP_STUB (STT_LOOS + 0x2) + +/* HPPA relocs. */ + +#define R_PARISC_NONE 0 /* No reloc. */ +#define R_PARISC_DIR32 1 /* Direct 32-bit reference. */ +#define R_PARISC_DIR21L 2 /* Left 21 bits of eff. address. */ +#define R_PARISC_DIR17R 3 /* Right 17 bits of eff. address. */ +#define R_PARISC_DIR14R 4 /* Right 14 bits of eff. address. */ +#define R_PARISC_PCREL21L 5 /* PC-relative, left 21 bits. */ +#define R_PARISC_PCREL14R 6 /* PC-relative, right 14 bits. */ +#define R_PARISC_PCREL17C 7 /* Conditional PC-relative, ignore + if displacement > 17bits. */ +#define R_PARISC_PCREL17F 8 /* Conditional PC-relative, must + fit in 17bits. */ +#define R_PARISC_DPREL21L 9 /* DP-relative, left 21 bits. */ +#define R_PARISC_DPREL14R 10 /* DP-relative, right 14 bits. */ +#define R_PARISC_DPREL14F 11 /* DP-relative, must bit in 14 bits. */ +#define R_PARISC_DLTREL21L 12 /* DLT-relative, left 21 bits. */ +#define R_PARISC_DLTREL14R 13 /* DLT-relative, right 14 bits. */ +#define R_PARISC_DLTREL14F 14 /* DLT-relative, must fit in 14 bits.*/ +#define R_PARISC_DLTIND21L 15 /* DLT-relative indirect, left + 21 bits. */ +#define R_PARISC_DLTIND14R 16 /* DLT-relative indirect, right + 14 bits. */ +#define R_PARISC_DLTIND14F 17 /* DLT-relative indirect, must fit + int 14 bits. */ +#define R_PARISC_PLABEL32 18 /* Direct 32-bit reference to proc. */ + +/* Alpha specific definitions. */ + +/* Legal values for e_flags field of Elf64_Ehdr. */ + +#define EF_ALPHA_32BIT 1 /* All addresses must be < 2GB. */ +#define EF_ALPHA_CANRELAX 2 /* Relocations for relaxing exist. */ + +/* Legal values for sh_type field of Elf64_Shdr. */ + +/* These two are primerily concerned with ECOFF debugging info. */ +#define SHT_ALPHA_DEBUG 0x70000001 +#define SHT_ALPHA_REGINFO 0x70000002 + +/* Legal values for sh_flags field of Elf64_Shdr. */ + +#define SHF_ALPHA_GPREL 0x10000000 + +/* Legal values for st_other field of Elf64_Sym. */ +#define STO_ALPHA_NOPV 0x80 /* No PV required. */ +#define STO_ALPHA_STD_GPLOAD 0x88 /* PV only used for initial ldgp. */ + +/* Alpha relocs. */ + +#define R_ALPHA_NONE 0 /* No reloc */ +#define R_ALPHA_REFLONG 1 /* Direct 32 bit */ +#define R_ALPHA_REFQUAD 2 /* Direct 64 bit */ +#define R_ALPHA_GPREL32 3 /* GP relative 32 bit */ +#define R_ALPHA_LITERAL 4 /* GP relative 16 bit w/optimization */ +#define R_ALPHA_LITUSE 5 /* Optimization hint for LITERAL */ +#define R_ALPHA_GPDISP 6 /* Add displacement to GP */ +#define R_ALPHA_BRADDR 7 /* PC+4 relative 23 bit shifted */ +#define R_ALPHA_HINT 8 /* PC+4 relative 16 bit shifted */ +#define R_ALPHA_SREL16 9 /* PC relative 16 bit */ +#define R_ALPHA_SREL32 10 /* PC relative 32 bit */ +#define R_ALPHA_SREL64 11 /* PC relative 64 bit */ +#define R_ALPHA_OP_PUSH 12 /* OP stack push */ +#define R_ALPHA_OP_STORE 13 /* OP stack pop and store */ +#define R_ALPHA_OP_PSUB 14 /* OP stack subtract */ +#define R_ALPHA_OP_PRSHIFT 15 /* OP stack right shift */ +#define R_ALPHA_GPVALUE 16 +#define R_ALPHA_GPRELHIGH 17 +#define R_ALPHA_GPRELLOW 18 +#define R_ALPHA_IMMED_GP_16 19 +#define R_ALPHA_IMMED_GP_HI32 20 +#define R_ALPHA_IMMED_SCN_HI32 21 +#define R_ALPHA_IMMED_BR_HI32 22 +#define R_ALPHA_IMMED_LO32 23 +#define R_ALPHA_COPY 24 /* Copy symbol at runtime */ +#define R_ALPHA_GLOB_DAT 25 /* Create GOT entry */ +#define R_ALPHA_JMP_SLOT 26 /* Create PLT entry */ +#define R_ALPHA_RELATIVE 27 /* Adjust by program base */ +#define R_ALPHA_TLS_GD_HI 28 +#define R_ALPHA_TLSGD 29 +#define R_ALPHA_TLS_LDM 30 +#define R_ALPHA_DTPMOD64 31 +#define R_ALPHA_GOTDTPREL 32 +#define R_ALPHA_DTPREL64 33 +#define R_ALPHA_DTPRELHI 34 +#define R_ALPHA_DTPRELLO 35 +#define R_ALPHA_DTPREL16 36 +#define R_ALPHA_GOTTPREL 37 +#define R_ALPHA_TPREL64 38 +#define R_ALPHA_TPRELHI 39 +#define R_ALPHA_TPRELLO 40 +#define R_ALPHA_TPREL16 41 +/* Keep this the last entry. */ +#define R_ALPHA_NUM 46 + +/* Magic values of the LITUSE relocation addend. */ +#define LITUSE_ALPHA_ADDR 0 +#define LITUSE_ALPHA_BASE 1 +#define LITUSE_ALPHA_BYTOFF 2 +#define LITUSE_ALPHA_JSR 3 +#define LITUSE_ALPHA_TLS_GD 4 +#define LITUSE_ALPHA_TLS_LDM 5 + + +/* PowerPC specific declarations */ + +/* Values for Elf32/64_Ehdr.e_flags. */ +#define EF_PPC_EMB 0x80000000 /* PowerPC embedded flag */ + +/* Cygnus local bits below */ +#define EF_PPC_RELOCATABLE 0x00010000 /* PowerPC -mrelocatable flag*/ +#define EF_PPC_RELOCATABLE_LIB 0x00008000 /* PowerPC -mrelocatable-lib + flag */ + +/* PowerPC relocations defined by the ABIs */ +#define R_PPC_NONE 0 +#define R_PPC_ADDR32 1 /* 32bit absolute address */ +#define R_PPC_ADDR24 2 /* 26bit address, 2 bits ignored. */ +#define R_PPC_ADDR16 3 /* 16bit absolute address */ +#define R_PPC_ADDR16_LO 4 /* lower 16bit of absolute address */ +#define R_PPC_ADDR16_HI 5 /* high 16bit of absolute address */ +#define R_PPC_ADDR16_HA 6 /* adjusted high 16bit */ +#define R_PPC_ADDR14 7 /* 16bit address, 2 bits ignored */ +#define R_PPC_ADDR14_BRTAKEN 8 +#define R_PPC_ADDR14_BRNTAKEN 9 +#define R_PPC_REL24 10 /* PC relative 26 bit */ +#define R_PPC_REL14 11 /* PC relative 16 bit */ +#define R_PPC_REL14_BRTAKEN 12 +#define R_PPC_REL14_BRNTAKEN 13 +#define R_PPC_GOT16 14 +#define R_PPC_GOT16_LO 15 +#define R_PPC_GOT16_HI 16 +#define R_PPC_GOT16_HA 17 +#define R_PPC_PLTREL24 18 +#define R_PPC_COPY 19 +#define R_PPC_GLOB_DAT 20 +#define R_PPC_JMP_SLOT 21 +#define R_PPC_RELATIVE 22 +#define R_PPC_LOCAL24PC 23 +#define R_PPC_UADDR32 24 +#define R_PPC_UADDR16 25 +#define R_PPC_REL32 26 +#define R_PPC_PLT32 27 +#define R_PPC_PLTREL32 28 +#define R_PPC_PLT16_LO 29 +#define R_PPC_PLT16_HI 30 +#define R_PPC_PLT16_HA 31 +#define R_PPC_SDAREL16 32 +#define R_PPC_SECTOFF 33 +#define R_PPC_SECTOFF_LO 34 +#define R_PPC_SECTOFF_HI 35 +#define R_PPC_SECTOFF_HA 36 + +/* PowerPC relocations defined for the TLS access ABI. */ +#define R_PPC_TLS 67 /* none (sym+add)@tls */ +#define R_PPC_DTPMOD32 68 /* word32 (sym+add)@dtpmod */ +#define R_PPC_TPREL16 69 /* half16* (sym+add)@tprel */ +#define R_PPC_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */ +#define R_PPC_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */ +#define R_PPC_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */ +#define R_PPC_TPREL32 73 /* word32 (sym+add)@tprel */ +#define R_PPC_DTPREL16 74 /* half16* (sym+add)@dtprel */ +#define R_PPC_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */ +#define R_PPC_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */ +#define R_PPC_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */ +#define R_PPC_DTPREL32 78 /* word32 (sym+add)@dtprel */ +#define R_PPC_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */ +#define R_PPC_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */ +#define R_PPC_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */ +#define R_PPC_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */ +#define R_PPC_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */ +#define R_PPC_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */ +#define R_PPC_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */ +#define R_PPC_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */ +#define R_PPC_GOT_TPREL16 87 /* half16* (sym+add)@got@tprel */ +#define R_PPC_GOT_TPREL16_LO 88 /* half16 (sym+add)@got@tprel@l */ +#define R_PPC_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */ +#define R_PPC_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */ +#define R_PPC_GOT_DTPREL16 91 /* half16* (sym+add)@got@dtprel */ +#define R_PPC_GOT_DTPREL16_LO 92 /* half16* (sym+add)@got@dtprel@l */ +#define R_PPC_GOT_DTPREL16_HI 93 /* half16* (sym+add)@got@dtprel@h */ +#define R_PPC_GOT_DTPREL16_HA 94 /* half16* (sym+add)@got@dtprel@ha */ + +/* Keep this the last entry. */ +#define R_PPC_NUM 95 + +/* The remaining relocs are from the Embedded ELF ABI, and are not + in the SVR4 ELF ABI. */ +#define R_PPC_EMB_NADDR32 101 +#define R_PPC_EMB_NADDR16 102 +#define R_PPC_EMB_NADDR16_LO 103 +#define R_PPC_EMB_NADDR16_HI 104 +#define R_PPC_EMB_NADDR16_HA 105 +#define R_PPC_EMB_SDAI16 106 +#define R_PPC_EMB_SDA2I16 107 +#define R_PPC_EMB_SDA2REL 108 +#define R_PPC_EMB_SDA21 109 /* 16 bit offset in SDA */ +#define R_PPC_EMB_MRKREF 110 +#define R_PPC_EMB_RELSEC16 111 +#define R_PPC_EMB_RELST_LO 112 +#define R_PPC_EMB_RELST_HI 113 +#define R_PPC_EMB_RELST_HA 114 +#define R_PPC_EMB_BIT_FLD 115 +#define R_PPC_EMB_RELSDA 116 /* 16 bit relative offset in SDA */ + +/* Diab tool relocations. */ +#define R_PPC_DIAB_SDA21_LO 180 /* like EMB_SDA21, but lower 16 bit */ +#define R_PPC_DIAB_SDA21_HI 181 /* like EMB_SDA21, but high 16 bit */ +#define R_PPC_DIAB_SDA21_HA 182 /* like EMB_SDA21, adjusted high 16 */ +#define R_PPC_DIAB_RELSDA_LO 183 /* like EMB_RELSDA, but lower 16 bit */ +#define R_PPC_DIAB_RELSDA_HI 184 /* like EMB_RELSDA, but high 16 bit */ +#define R_PPC_DIAB_RELSDA_HA 185 /* like EMB_RELSDA, adjusted high 16 */ + +/* This is a phony reloc to handle any old fashioned TOC16 references + that may still be in object files. */ +#define R_PPC_TOC16 255 + + +/* PowerPC64 relocations defined by the ABIs */ +#define R_PPC64_NONE R_PPC_NONE +#define R_PPC64_ADDR32 R_PPC_ADDR32 /* 32bit absolute address */ +#define R_PPC64_ADDR24 R_PPC_ADDR24 /* 26bit address, word aligned */ +#define R_PPC64_ADDR16 R_PPC_ADDR16 /* 16bit absolute address */ +#define R_PPC64_ADDR16_LO R_PPC_ADDR16_LO /* lower 16bits of address */ +#define R_PPC64_ADDR16_HI R_PPC_ADDR16_HI /* high 16bits of address. */ +#define R_PPC64_ADDR16_HA R_PPC_ADDR16_HA /* adjusted high 16bits. */ +#define R_PPC64_ADDR14 R_PPC_ADDR14 /* 16bit address, word aligned */ +#define R_PPC64_ADDR14_BRTAKEN R_PPC_ADDR14_BRTAKEN +#define R_PPC64_ADDR14_BRNTAKEN R_PPC_ADDR14_BRNTAKEN +#define R_PPC64_REL24 R_PPC_REL24 /* PC-rel. 26 bit, word aligned */ +#define R_PPC64_REL14 R_PPC_REL14 /* PC relative 16 bit */ +#define R_PPC64_REL14_BRTAKEN R_PPC_REL14_BRTAKEN +#define R_PPC64_REL14_BRNTAKEN R_PPC_REL14_BRNTAKEN +#define R_PPC64_GOT16 R_PPC_GOT16 +#define R_PPC64_GOT16_LO R_PPC_GOT16_LO +#define R_PPC64_GOT16_HI R_PPC_GOT16_HI +#define R_PPC64_GOT16_HA R_PPC_GOT16_HA + +#define R_PPC64_COPY R_PPC_COPY +#define R_PPC64_GLOB_DAT R_PPC_GLOB_DAT +#define R_PPC64_JMP_SLOT R_PPC_JMP_SLOT +#define R_PPC64_RELATIVE R_PPC_RELATIVE + +#define R_PPC64_UADDR32 R_PPC_UADDR32 +#define R_PPC64_UADDR16 R_PPC_UADDR16 +#define R_PPC64_REL32 R_PPC_REL32 +#define R_PPC64_PLT32 R_PPC_PLT32 +#define R_PPC64_PLTREL32 R_PPC_PLTREL32 +#define R_PPC64_PLT16_LO R_PPC_PLT16_LO +#define R_PPC64_PLT16_HI R_PPC_PLT16_HI +#define R_PPC64_PLT16_HA R_PPC_PLT16_HA + +#define R_PPC64_SECTOFF R_PPC_SECTOFF +#define R_PPC64_SECTOFF_LO R_PPC_SECTOFF_LO +#define R_PPC64_SECTOFF_HI R_PPC_SECTOFF_HI +#define R_PPC64_SECTOFF_HA R_PPC_SECTOFF_HA +#define R_PPC64_ADDR30 37 /* word30 (S + A - P) >> 2 */ +#define R_PPC64_ADDR64 38 /* doubleword64 S + A */ +#define R_PPC64_ADDR16_HIGHER 39 /* half16 #higher(S + A) */ +#define R_PPC64_ADDR16_HIGHERA 40 /* half16 #highera(S + A) */ +#define R_PPC64_ADDR16_HIGHEST 41 /* half16 #highest(S + A) */ +#define R_PPC64_ADDR16_HIGHESTA 42 /* half16 #highesta(S + A) */ +#define R_PPC64_UADDR64 43 /* doubleword64 S + A */ +#define R_PPC64_REL64 44 /* doubleword64 S + A - P */ +#define R_PPC64_PLT64 45 /* doubleword64 L + A */ +#define R_PPC64_PLTREL64 46 /* doubleword64 L + A - P */ +#define R_PPC64_TOC16 47 /* half16* S + A - .TOC */ +#define R_PPC64_TOC16_LO 48 /* half16 #lo(S + A - .TOC.) */ +#define R_PPC64_TOC16_HI 49 /* half16 #hi(S + A - .TOC.) */ +#define R_PPC64_TOC16_HA 50 /* half16 #ha(S + A - .TOC.) */ +#define R_PPC64_TOC 51 /* doubleword64 .TOC */ +#define R_PPC64_PLTGOT16 52 /* half16* M + A */ +#define R_PPC64_PLTGOT16_LO 53 /* half16 #lo(M + A) */ +#define R_PPC64_PLTGOT16_HI 54 /* half16 #hi(M + A) */ +#define R_PPC64_PLTGOT16_HA 55 /* half16 #ha(M + A) */ + +#define R_PPC64_ADDR16_DS 56 /* half16ds* (S + A) >> 2 */ +#define R_PPC64_ADDR16_LO_DS 57 /* half16ds #lo(S + A) >> 2 */ +#define R_PPC64_GOT16_DS 58 /* half16ds* (G + A) >> 2 */ +#define R_PPC64_GOT16_LO_DS 59 /* half16ds #lo(G + A) >> 2 */ +#define R_PPC64_PLT16_LO_DS 60 /* half16ds #lo(L + A) >> 2 */ +#define R_PPC64_SECTOFF_DS 61 /* half16ds* (R + A) >> 2 */ +#define R_PPC64_SECTOFF_LO_DS 62 /* half16ds #lo(R + A) >> 2 */ +#define R_PPC64_TOC16_DS 63 /* half16ds* (S + A - .TOC.) >> 2 */ +#define R_PPC64_TOC16_LO_DS 64 /* half16ds #lo(S + A - .TOC.) >> 2 */ +#define R_PPC64_PLTGOT16_DS 65 /* half16ds* (M + A) >> 2 */ +#define R_PPC64_PLTGOT16_LO_DS 66 /* half16ds #lo(M + A) >> 2 */ + +/* PowerPC64 relocations defined for the TLS access ABI. */ +#define R_PPC64_TLS 67 /* none (sym+add)@tls */ +#define R_PPC64_DTPMOD64 68 /* doubleword64 (sym+add)@dtpmod */ +#define R_PPC64_TPREL16 69 /* half16* (sym+add)@tprel */ +#define R_PPC64_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */ +#define R_PPC64_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */ +#define R_PPC64_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */ +#define R_PPC64_TPREL64 73 /* doubleword64 (sym+add)@tprel */ +#define R_PPC64_DTPREL16 74 /* half16* (sym+add)@dtprel */ +#define R_PPC64_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */ +#define R_PPC64_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */ +#define R_PPC64_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */ +#define R_PPC64_DTPREL64 78 /* doubleword64 (sym+add)@dtprel */ +#define R_PPC64_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */ +#define R_PPC64_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */ +#define R_PPC64_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */ +#define R_PPC64_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */ +#define R_PPC64_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */ +#define R_PPC64_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */ +#define R_PPC64_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */ +#define R_PPC64_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */ +#define R_PPC64_GOT_TPREL16_DS 87 /* half16ds* (sym+add)@got@tprel */ +#define R_PPC64_GOT_TPREL16_LO_DS 88 /* half16ds (sym+add)@got@tprel@l */ +#define R_PPC64_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */ +#define R_PPC64_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */ +#define R_PPC64_GOT_DTPREL16_DS 91 /* half16ds* (sym+add)@got@dtprel */ +#define R_PPC64_GOT_DTPREL16_LO_DS 92 /* half16ds (sym+add)@got@dtprel@l */ +#define R_PPC64_GOT_DTPREL16_HI 93 /* half16 (sym+add)@got@dtprel@h */ +#define R_PPC64_GOT_DTPREL16_HA 94 /* half16 (sym+add)@got@dtprel@ha */ +#define R_PPC64_TPREL16_DS 95 /* half16ds* (sym+add)@tprel */ +#define R_PPC64_TPREL16_LO_DS 96 /* half16ds (sym+add)@tprel@l */ +#define R_PPC64_TPREL16_HIGHER 97 /* half16 (sym+add)@tprel@higher */ +#define R_PPC64_TPREL16_HIGHERA 98 /* half16 (sym+add)@tprel@highera */ +#define R_PPC64_TPREL16_HIGHEST 99 /* half16 (sym+add)@tprel@highest */ +#define R_PPC64_TPREL16_HIGHESTA 100 /* half16 (sym+add)@tprel@highesta */ +#define R_PPC64_DTPREL16_DS 101 /* half16ds* (sym+add)@dtprel */ +#define R_PPC64_DTPREL16_LO_DS 102 /* half16ds (sym+add)@dtprel@l */ +#define R_PPC64_DTPREL16_HIGHER 103 /* half16 (sym+add)@dtprel@higher */ +#define R_PPC64_DTPREL16_HIGHERA 104 /* half16 (sym+add)@dtprel@highera */ +#define R_PPC64_DTPREL16_HIGHEST 105 /* half16 (sym+add)@dtprel@highest */ +#define R_PPC64_DTPREL16_HIGHESTA 106 /* half16 (sym+add)@dtprel@highesta */ +#define R_PPC64_REL16 249 /* half16 (sym+add-.) */ +#define R_PPC64_REL16_LO 250 /* half16 (sym+add-.)@l */ +#define R_PPC64_REL16_HI 251 /* half16 (sym+add-.)@h */ +#define R_PPC64_REL16_HA 252 /* half16 (sym+add-.)@ha */ + +/* Keep this the last entry. */ +#define R_PPC64_NUM 107 + +/* PowerPC64 specific values for the Dyn d_tag field. */ +#define DT_PPC64_GLINK (DT_LOPROC + 0) +#define DT_PPC64_NUM 1 + + +/* ARM specific declarations */ + +/* Processor specific flags for the ELF header e_flags field. */ +#define EF_ARM_RELEXEC 0x01 +#define EF_ARM_HASENTRY 0x02 +#define EF_ARM_INTERWORK 0x04 +#define EF_ARM_APCS_26 0x08 +#define EF_ARM_APCS_FLOAT 0x10 +#define EF_ARM_PIC 0x20 +#define EF_ARM_ALIGN8 0x40 /* 8-bit structure alignment is in use */ +#define EF_ARM_NEW_ABI 0x80 +#define EF_ARM_OLD_ABI 0x100 + +/* Other constants defined in the ARM ELF spec. version B-01. */ +/* NB. These conflict with values defined above. */ +#define EF_ARM_SYMSARESORTED 0x04 +#define EF_ARM_DYNSYMSUSESEGIDX 0x08 +#define EF_ARM_MAPSYMSFIRST 0x10 +#define EF_ARM_EABIMASK 0XFF000000 + +#define EF_ARM_EABI_VERSION(flags) ((flags) & EF_ARM_EABIMASK) +#define EF_ARM_EABI_UNKNOWN 0x00000000 +#define EF_ARM_EABI_VER1 0x01000000 +#define EF_ARM_EABI_VER2 0x02000000 + +/* Additional symbol types for Thumb */ +#define STT_ARM_TFUNC 0xd + +/* ARM-specific values for sh_flags */ +#define SHF_ARM_ENTRYSECT 0x10000000 /* Section contains an entry point */ +#define SHF_ARM_COMDEF 0x80000000 /* Section may be multiply defined + in the input to a link step */ + +/* ARM-specific program header flags */ +#define PF_ARM_SB 0x10000000 /* Segment contains the location + addressed by the static base */ + +/* ARM relocs. */ +#define R_ARM_NONE 0 /* No reloc */ +#define R_ARM_PC24 1 /* PC relative 26 bit branch */ +#define R_ARM_ABS32 2 /* Direct 32 bit */ +#define R_ARM_REL32 3 /* PC relative 32 bit */ +#define R_ARM_PC13 4 +#define R_ARM_ABS16 5 /* Direct 16 bit */ +#define R_ARM_ABS12 6 /* Direct 12 bit */ +#define R_ARM_THM_ABS5 7 +#define R_ARM_ABS8 8 /* Direct 8 bit */ +#define R_ARM_SBREL32 9 +#define R_ARM_THM_PC22 10 +#define R_ARM_THM_PC8 11 +#define R_ARM_AMP_VCALL9 12 +#define R_ARM_SWI24 13 +#define R_ARM_THM_SWI8 14 +#define R_ARM_XPC25 15 +#define R_ARM_THM_XPC22 16 +#define R_ARM_COPY 20 /* Copy symbol at runtime */ +#define R_ARM_GLOB_DAT 21 /* Create GOT entry */ +#define R_ARM_JUMP_SLOT 22 /* Create PLT entry */ +#define R_ARM_RELATIVE 23 /* Adjust by program base */ +#define R_ARM_GOTOFF 24 /* 32 bit offset to GOT */ +#define R_ARM_GOTPC 25 /* 32 bit PC relative offset to GOT */ +#define R_ARM_GOT32 26 /* 32 bit GOT entry */ +#define R_ARM_PLT32 27 /* 32 bit PLT address */ +#define R_ARM_ALU_PCREL_7_0 32 +#define R_ARM_ALU_PCREL_15_8 33 +#define R_ARM_ALU_PCREL_23_15 34 +#define R_ARM_LDR_SBREL_11_0 35 +#define R_ARM_ALU_SBREL_19_12 36 +#define R_ARM_ALU_SBREL_27_20 37 +#define R_ARM_GNU_VTENTRY 100 +#define R_ARM_GNU_VTINHERIT 101 +#define R_ARM_THM_PC11 102 /* thumb unconditional branch */ +#define R_ARM_THM_PC9 103 /* thumb conditional branch */ +#define R_ARM_RXPC25 249 +#define R_ARM_RSBREL32 250 +#define R_ARM_THM_RPC22 251 +#define R_ARM_RREL32 252 +#define R_ARM_RABS22 253 +#define R_ARM_RPC24 254 +#define R_ARM_RBASE 255 +/* Keep this the last entry. */ +#define R_ARM_NUM 256 + +/* IA-64 specific declarations. */ + +/* Processor specific flags for the Ehdr e_flags field. */ +#define EF_IA_64_MASKOS 0x0000000f /* os-specific flags */ +#define EF_IA_64_ABI64 0x00000010 /* 64-bit ABI */ +#define EF_IA_64_ARCH 0xff000000 /* arch. version mask */ + +/* Processor specific values for the Phdr p_type field. */ +#define PT_IA_64_ARCHEXT (PT_LOPROC + 0) /* arch extension bits */ +#define PT_IA_64_UNWIND (PT_LOPROC + 1) /* ia64 unwind bits */ + +/* Processor specific flags for the Phdr p_flags field. */ +#define PF_IA_64_NORECOV 0x80000000 /* spec insns w/o recovery */ + +/* Processor specific values for the Shdr sh_type field. */ +#define SHT_IA_64_EXT (SHT_LOPROC + 0) /* extension bits */ +#define SHT_IA_64_UNWIND (SHT_LOPROC + 1) /* unwind bits */ + +/* Processor specific flags for the Shdr sh_flags field. */ +#define SHF_IA_64_SHORT 0x10000000 /* section near gp */ +#define SHF_IA_64_NORECOV 0x20000000 /* spec insns w/o recovery */ + +/* Processor specific values for the Dyn d_tag field. */ +#define DT_IA_64_PLT_RESERVE (DT_LOPROC + 0) +#define DT_IA_64_NUM 1 + +/* IA-64 relocations. */ +#define R_IA64_NONE 0x00 /* none */ +#define R_IA64_IMM14 0x21 /* symbol + addend, add imm14 */ +#define R_IA64_IMM22 0x22 /* symbol + addend, add imm22 */ +#define R_IA64_IMM64 0x23 /* symbol + addend, mov imm64 */ +#define R_IA64_DIR32MSB 0x24 /* symbol + addend, data4 MSB */ +#define R_IA64_DIR32LSB 0x25 /* symbol + addend, data4 LSB */ +#define R_IA64_DIR64MSB 0x26 /* symbol + addend, data8 MSB */ +#define R_IA64_DIR64LSB 0x27 /* symbol + addend, data8 LSB */ +#define R_IA64_GPREL22 0x2a /* @gprel(sym + add), add imm22 */ +#define R_IA64_GPREL64I 0x2b /* @gprel(sym + add), mov imm64 */ +#define R_IA64_GPREL32MSB 0x2c /* @gprel(sym + add), data4 MSB */ +#define R_IA64_GPREL32LSB 0x2d /* @gprel(sym + add), data4 LSB */ +#define R_IA64_GPREL64MSB 0x2e /* @gprel(sym + add), data8 MSB */ +#define R_IA64_GPREL64LSB 0x2f /* @gprel(sym + add), data8 LSB */ +#define R_IA64_LTOFF22 0x32 /* @ltoff(sym + add), add imm22 */ +#define R_IA64_LTOFF64I 0x33 /* @ltoff(sym + add), mov imm64 */ +#define R_IA64_PLTOFF22 0x3a /* @pltoff(sym + add), add imm22 */ +#define R_IA64_PLTOFF64I 0x3b /* @pltoff(sym + add), mov imm64 */ +#define R_IA64_PLTOFF64MSB 0x3e /* @pltoff(sym + add), data8 MSB */ +#define R_IA64_PLTOFF64LSB 0x3f /* @pltoff(sym + add), data8 LSB */ +#define R_IA64_FPTR64I 0x43 /* @fptr(sym + add), mov imm64 */ +#define R_IA64_FPTR32MSB 0x44 /* @fptr(sym + add), data4 MSB */ +#define R_IA64_FPTR32LSB 0x45 /* @fptr(sym + add), data4 LSB */ +#define R_IA64_FPTR64MSB 0x46 /* @fptr(sym + add), data8 MSB */ +#define R_IA64_FPTR64LSB 0x47 /* @fptr(sym + add), data8 LSB */ +#define R_IA64_PCREL60B 0x48 /* @pcrel(sym + add), brl */ +#define R_IA64_PCREL21B 0x49 /* @pcrel(sym + add), ptb, call */ +#define R_IA64_PCREL21M 0x4a /* @pcrel(sym + add), chk.s */ +#define R_IA64_PCREL21F 0x4b /* @pcrel(sym + add), fchkf */ +#define R_IA64_PCREL32MSB 0x4c /* @pcrel(sym + add), data4 MSB */ +#define R_IA64_PCREL32LSB 0x4d /* @pcrel(sym + add), data4 LSB */ +#define R_IA64_PCREL64MSB 0x4e /* @pcrel(sym + add), data8 MSB */ +#define R_IA64_PCREL64LSB 0x4f /* @pcrel(sym + add), data8 LSB */ +#define R_IA64_LTOFF_FPTR22 0x52 /* @ltoff(@fptr(s+a)), imm22 */ +#define R_IA64_LTOFF_FPTR64I 0x53 /* @ltoff(@fptr(s+a)), imm64 */ +#define R_IA64_LTOFF_FPTR32MSB 0x54 /* @ltoff(@fptr(s+a)), data4 MSB */ +#define R_IA64_LTOFF_FPTR32LSB 0x55 /* @ltoff(@fptr(s+a)), data4 LSB */ +#define R_IA64_LTOFF_FPTR64MSB 0x56 /* @ltoff(@fptr(s+a)), data8 MSB */ +#define R_IA64_LTOFF_FPTR64LSB 0x57 /* @ltoff(@fptr(s+a)), data8 LSB */ +#define R_IA64_SEGREL32MSB 0x5c /* @segrel(sym + add), data4 MSB */ +#define R_IA64_SEGREL32LSB 0x5d /* @segrel(sym + add), data4 LSB */ +#define R_IA64_SEGREL64MSB 0x5e /* @segrel(sym + add), data8 MSB */ +#define R_IA64_SEGREL64LSB 0x5f /* @segrel(sym + add), data8 LSB */ +#define R_IA64_SECREL32MSB 0x64 /* @secrel(sym + add), data4 MSB */ +#define R_IA64_SECREL32LSB 0x65 /* @secrel(sym + add), data4 LSB */ +#define R_IA64_SECREL64MSB 0x66 /* @secrel(sym + add), data8 MSB */ +#define R_IA64_SECREL64LSB 0x67 /* @secrel(sym + add), data8 LSB */ +#define R_IA64_REL32MSB 0x6c /* data 4 + REL */ +#define R_IA64_REL32LSB 0x6d /* data 4 + REL */ +#define R_IA64_REL64MSB 0x6e /* data 8 + REL */ +#define R_IA64_REL64LSB 0x6f /* data 8 + REL */ +#define R_IA64_LTV32MSB 0x74 /* symbol + addend, data4 MSB */ +#define R_IA64_LTV32LSB 0x75 /* symbol + addend, data4 LSB */ +#define R_IA64_LTV64MSB 0x76 /* symbol + addend, data8 MSB */ +#define R_IA64_LTV64LSB 0x77 /* symbol + addend, data8 LSB */ +#define R_IA64_PCREL21BI 0x79 /* @pcrel(sym + add), 21bit inst */ +#define R_IA64_PCREL22 0x7a /* @pcrel(sym + add), 22bit inst */ +#define R_IA64_PCREL64I 0x7b /* @pcrel(sym + add), 64bit inst */ +#define R_IA64_IPLTMSB 0x80 /* dynamic reloc, imported PLT, MSB */ +#define R_IA64_IPLTLSB 0x81 /* dynamic reloc, imported PLT, LSB */ +#define R_IA64_COPY 0x84 /* copy relocation */ +#define R_IA64_SUB 0x85 /* Addend and symbol difference */ +#define R_IA64_LTOFF22X 0x86 /* LTOFF22, relaxable. */ +#define R_IA64_LDXMOV 0x87 /* Use of LTOFF22X. */ +#define R_IA64_TPREL14 0x91 /* @tprel(sym + add), imm14 */ +#define R_IA64_TPREL22 0x92 /* @tprel(sym + add), imm22 */ +#define R_IA64_TPREL64I 0x93 /* @tprel(sym + add), imm64 */ +#define R_IA64_TPREL64MSB 0x96 /* @tprel(sym + add), data8 MSB */ +#define R_IA64_TPREL64LSB 0x97 /* @tprel(sym + add), data8 LSB */ +#define R_IA64_LTOFF_TPREL22 0x9a /* @ltoff(@tprel(s+a)), imm2 */ +#define R_IA64_DTPMOD64MSB 0xa6 /* @dtpmod(sym + add), data8 MSB */ +#define R_IA64_DTPMOD64LSB 0xa7 /* @dtpmod(sym + add), data8 LSB */ +#define R_IA64_LTOFF_DTPMOD22 0xaa /* @ltoff(@dtpmod(sym + add)), imm22 */ +#define R_IA64_DTPREL14 0xb1 /* @dtprel(sym + add), imm14 */ +#define R_IA64_DTPREL22 0xb2 /* @dtprel(sym + add), imm22 */ +#define R_IA64_DTPREL64I 0xb3 /* @dtprel(sym + add), imm64 */ +#define R_IA64_DTPREL32MSB 0xb4 /* @dtprel(sym + add), data4 MSB */ +#define R_IA64_DTPREL32LSB 0xb5 /* @dtprel(sym + add), data4 LSB */ +#define R_IA64_DTPREL64MSB 0xb6 /* @dtprel(sym + add), data8 MSB */ +#define R_IA64_DTPREL64LSB 0xb7 /* @dtprel(sym + add), data8 LSB */ +#define R_IA64_LTOFF_DTPREL22 0xba /* @ltoff(@dtprel(s+a)), imm22 */ + +/* SH specific declarations */ + +/* SH relocs. */ +#define R_SH_NONE 0 +#define R_SH_DIR32 1 +#define R_SH_REL32 2 +#define R_SH_DIR8WPN 3 +#define R_SH_IND12W 4 +#define R_SH_DIR8WPL 5 +#define R_SH_DIR8WPZ 6 +#define R_SH_DIR8BP 7 +#define R_SH_DIR8W 8 +#define R_SH_DIR8L 9 +#define R_SH_SWITCH16 25 +#define R_SH_SWITCH32 26 +#define R_SH_USES 27 +#define R_SH_COUNT 28 +#define R_SH_ALIGN 29 +#define R_SH_CODE 30 +#define R_SH_DATA 31 +#define R_SH_LABEL 32 +#define R_SH_SWITCH8 33 +#define R_SH_GNU_VTINHERIT 34 +#define R_SH_GNU_VTENTRY 35 +#define R_SH_TLS_GD_32 144 +#define R_SH_TLS_LD_32 145 +#define R_SH_TLS_LDO_32 146 +#define R_SH_TLS_IE_32 147 +#define R_SH_TLS_LE_32 148 +#define R_SH_TLS_DTPMOD32 149 +#define R_SH_TLS_DTPOFF32 150 +#define R_SH_TLS_TPOFF32 151 +#define R_SH_GOT32 160 +#define R_SH_PLT32 161 +#define R_SH_COPY 162 +#define R_SH_GLOB_DAT 163 +#define R_SH_JMP_SLOT 164 +#define R_SH_RELATIVE 165 +#define R_SH_GOTOFF 166 +#define R_SH_GOTPC 167 +/* Keep this the last entry. */ +#define R_SH_NUM 256 + +/* Additional s390 relocs */ + +#define R_390_NONE 0 /* No reloc. */ +#define R_390_8 1 /* Direct 8 bit. */ +#define R_390_12 2 /* Direct 12 bit. */ +#define R_390_16 3 /* Direct 16 bit. */ +#define R_390_32 4 /* Direct 32 bit. */ +#define R_390_PC32 5 /* PC relative 32 bit. */ +#define R_390_GOT12 6 /* 12 bit GOT offset. */ +#define R_390_GOT32 7 /* 32 bit GOT offset. */ +#define R_390_PLT32 8 /* 32 bit PC relative PLT address. */ +#define R_390_COPY 9 /* Copy symbol at runtime. */ +#define R_390_GLOB_DAT 10 /* Create GOT entry. */ +#define R_390_JMP_SLOT 11 /* Create PLT entry. */ +#define R_390_RELATIVE 12 /* Adjust by program base. */ +#define R_390_GOTOFF32 13 /* 32 bit offset to GOT. */ +#define R_390_GOTPC 14 /* 32 bit PC relative offset to GOT. */ +#define R_390_GOT16 15 /* 16 bit GOT offset. */ +#define R_390_PC16 16 /* PC relative 16 bit. */ +#define R_390_PC16DBL 17 /* PC relative 16 bit shifted by 1. */ +#define R_390_PLT16DBL 18 /* 16 bit PC rel. PLT shifted by 1. */ +#define R_390_PC32DBL 19 /* PC relative 32 bit shifted by 1. */ +#define R_390_PLT32DBL 20 /* 32 bit PC rel. PLT shifted by 1. */ +#define R_390_GOTPCDBL 21 /* 32 bit PC rel. GOT shifted by 1. */ +#define R_390_64 22 /* Direct 64 bit. */ +#define R_390_PC64 23 /* PC relative 64 bit. */ +#define R_390_GOT64 24 /* 64 bit GOT offset. */ +#define R_390_PLT64 25 /* 64 bit PC relative PLT address. */ +#define R_390_GOTENT 26 /* 32 bit PC rel. to GOT entry >> 1. */ +#define R_390_GOTOFF16 27 /* 16 bit offset to GOT. */ +#define R_390_GOTOFF64 28 /* 64 bit offset to GOT. */ +#define R_390_GOTPLT12 29 /* 12 bit offset to jump slot. */ +#define R_390_GOTPLT16 30 /* 16 bit offset to jump slot. */ +#define R_390_GOTPLT32 31 /* 32 bit offset to jump slot. */ +#define R_390_GOTPLT64 32 /* 64 bit offset to jump slot. */ +#define R_390_GOTPLTENT 33 /* 32 bit rel. offset to jump slot. */ +#define R_390_PLTOFF16 34 /* 16 bit offset from GOT to PLT. */ +#define R_390_PLTOFF32 35 /* 32 bit offset from GOT to PLT. */ +#define R_390_PLTOFF64 36 /* 16 bit offset from GOT to PLT. */ +#define R_390_TLS_LOAD 37 /* Tag for load insn in TLS code. */ +#define R_390_TLS_GDCALL 38 /* Tag for function call in general + dynamic TLS code. */ +#define R_390_TLS_LDCALL 39 /* Tag for function call in local + dynamic TLS code. */ +#define R_390_TLS_GD32 40 /* Direct 32 bit for general dynamic + thread local data. */ +#define R_390_TLS_GD64 41 /* Direct 64 bit for general dynamic + thread local data. */ +#define R_390_TLS_GOTIE12 42 /* 12 bit GOT offset for static TLS + block offset. */ +#define R_390_TLS_GOTIE32 43 /* 32 bit GOT offset for static TLS + block offset. */ +#define R_390_TLS_GOTIE64 44 /* 64 bit GOT offset for static TLS + block offset. */ +#define R_390_TLS_LDM32 45 /* Direct 32 bit for local dynamic + thread local data in LE code. */ +#define R_390_TLS_LDM64 46 /* Direct 64 bit for local dynamic + thread local data in LE code. */ +#define R_390_TLS_IE32 47 /* 32 bit address of GOT entry for + negated static TLS block offset. */ +#define R_390_TLS_IE64 48 /* 64 bit address of GOT entry for + negated static TLS block offset. */ +#define R_390_TLS_IEENT 49 /* 32 bit rel. offset to GOT entry for + negated static TLS block offset. */ +#define R_390_TLS_LE32 50 /* 32 bit negated offset relative to + static TLS block. */ +#define R_390_TLS_LE64 51 /* 64 bit negated offset relative to + static TLS block. */ +#define R_390_TLS_LDO32 52 /* 32 bit offset relative to TLS + block. */ +#define R_390_TLS_LDO64 53 /* 64 bit offset relative to TLS + block. */ +#define R_390_TLS_DTPMOD 54 /* ID of module containing symbol. */ +#define R_390_TLS_DTPOFF 55 /* Offset in TLS block. */ +#define R_390_TLS_TPOFF 56 /* Negated offset in static TLS + block. */ +#define R_390_20 57 /* Direct 20 bit. */ +#define R_390_GOT20 58 /* 20 bit GOT offset. */ +#define R_390_GOTPLT20 59 /* 20 bit offset to jump slot. */ +#define R_390_TLS_GOTIE20 60 /* 20 bit GOT offset for static TLS + block offset. */ +/* Keep this the last entry. */ +#define R_390_NUM 61 + +/* CRIS relocations. */ +#define R_CRIS_NONE 0 +#define R_CRIS_8 1 +#define R_CRIS_16 2 +#define R_CRIS_32 3 +#define R_CRIS_8_PCREL 4 +#define R_CRIS_16_PCREL 5 +#define R_CRIS_32_PCREL 6 +#define R_CRIS_GNU_VTINHERIT 7 +#define R_CRIS_GNU_VTENTRY 8 +#define R_CRIS_COPY 9 +#define R_CRIS_GLOB_DAT 10 +#define R_CRIS_JUMP_SLOT 11 +#define R_CRIS_RELATIVE 12 +#define R_CRIS_16_GOT 13 +#define R_CRIS_32_GOT 14 +#define R_CRIS_16_GOTPLT 15 +#define R_CRIS_32_GOTPLT 16 +#define R_CRIS_32_GOTREL 17 +#define R_CRIS_32_PLT_GOTREL 18 +#define R_CRIS_32_PLT_PCREL 19 + +#define R_CRIS_NUM 20 + +/* AMD x86-64 relocations. */ +#define R_X86_64_NONE 0 /* No reloc */ +#define R_X86_64_64 1 /* Direct 64 bit */ +#define R_X86_64_PC32 2 /* PC relative 32 bit signed */ +#define R_X86_64_GOT32 3 /* 32 bit GOT entry */ +#define R_X86_64_PLT32 4 /* 32 bit PLT address */ +#define R_X86_64_COPY 5 /* Copy symbol at runtime */ +#define R_X86_64_GLOB_DAT 6 /* Create GOT entry */ +#define R_X86_64_JUMP_SLOT 7 /* Create PLT entry */ +#define R_X86_64_RELATIVE 8 /* Adjust by program base */ +#define R_X86_64_GOTPCREL 9 /* 32 bit signed PC relative + offset to GOT */ +#define R_X86_64_32 10 /* Direct 32 bit zero extended */ +#define R_X86_64_32S 11 /* Direct 32 bit sign extended */ +#define R_X86_64_16 12 /* Direct 16 bit zero extended */ +#define R_X86_64_PC16 13 /* 16 bit sign extended pc relative */ +#define R_X86_64_8 14 /* Direct 8 bit sign extended */ +#define R_X86_64_PC8 15 /* 8 bit sign extended pc relative */ +#define R_X86_64_DTPMOD64 16 /* ID of module containing symbol */ +#define R_X86_64_DTPOFF64 17 /* Offset in module's TLS block */ +#define R_X86_64_TPOFF64 18 /* Offset in initial TLS block */ +#define R_X86_64_TLSGD 19 /* 32 bit signed PC relative offset + to two GOT entries for GD symbol */ +#define R_X86_64_TLSLD 20 /* 32 bit signed PC relative offset + to two GOT entries for LD symbol */ +#define R_X86_64_DTPOFF32 21 /* Offset in TLS block */ +#define R_X86_64_GOTTPOFF 22 /* 32 bit signed PC relative offset + to GOT entry for IE symbol */ +#define R_X86_64_TPOFF32 23 /* Offset in initial TLS block */ + +#define R_X86_64_NUM 24 + +#endif /* elf.h */ diff --git a/include/image.h b/include/image.h new file mode 100644 index 0000000..7a4bccf --- /dev/null +++ b/include/image.h @@ -0,0 +1,174 @@ +/* + * (C) Copyright 2000-2005 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 + * + ******************************************************************** + * NOTE: This header file defines an interface to U-Boot. Including + * this (unmodified) header file in another file is considered normal + * use of U-Boot, and does *not* fall under the heading of "derived + * work". + ******************************************************************** + */ + +#ifndef __IMAGE_H__ +#define __IMAGE_H__ + +/* + * Operating System Codes + */ +#define IH_OS_INVALID 0 /* Invalid OS */ +#define IH_OS_OPENBSD 1 /* OpenBSD */ +#define IH_OS_NETBSD 2 /* NetBSD */ +#define IH_OS_FREEBSD 3 /* FreeBSD */ +#define IH_OS_4_4BSD 4 /* 4.4BSD */ +#define IH_OS_LINUX 5 /* Linux */ +#define IH_OS_SVR4 6 /* SVR4 */ +#define IH_OS_ESIX 7 /* Esix */ +#define IH_OS_SOLARIS 8 /* Solaris */ +#define IH_OS_IRIX 9 /* Irix */ +#define IH_OS_SCO 10 /* SCO */ +#define IH_OS_DELL 11 /* Dell */ +#define IH_OS_NCR 12 /* NCR */ +#define IH_OS_LYNXOS 13 /* LynxOS */ +#define IH_OS_VXWORKS 14 /* VxWorks */ +#define IH_OS_PSOS 15 /* pSOS */ +#define IH_OS_QNX 16 /* QNX */ +#define IH_OS_U_BOOT 17 /* Firmware */ +#define IH_OS_RTEMS 18 /* RTEMS */ +#define IH_OS_ARTOS 19 /* ARTOS */ +#define IH_OS_UNITY 20 /* Unity OS */ +#define IH_OS_INTEGRITY 21 /* INTEGRITY */ + +/* + * CPU Architecture Codes (supported by Linux) + */ +#define IH_ARCH_INVALID 0 /* Invalid CPU */ +#define IH_ARCH_ALPHA 1 /* Alpha */ +#define IH_ARCH_ARM 2 /* ARM */ +#define IH_ARCH_I386 3 /* Intel x86 */ +#define IH_ARCH_IA64 4 /* IA64 */ +#define IH_ARCH_MIPS 5 /* MIPS */ +#define IH_ARCH_MIPS64 6 /* MIPS 64 Bit */ +#define IH_ARCH_PPC 7 /* PowerPC */ +#define IH_ARCH_S390 8 /* IBM S390 */ +#define IH_ARCH_SH 9 /* SuperH */ +#define IH_ARCH_SPARC 10 /* Sparc */ +#define IH_ARCH_SPARC64 11 /* Sparc 64 Bit */ +#define IH_ARCH_M68K 12 /* M68K */ +#define IH_ARCH_NIOS 13 /* Nios-32 */ +#define IH_ARCH_MICROBLAZE 14 /* MicroBlaze */ +#define IH_ARCH_NIOS2 15 /* Nios-II */ +#define IH_ARCH_BLACKFIN 16 /* Blackfin */ +#define IH_ARCH_AVR32 17 /* AVR32 */ +#define IH_ARCH_ST200 18 /* STMicroelectronics ST200 */ +#define IH_ARCH_SANDBOX 19 /* Sandbox architecture (test only) */ +#define IH_ARCH_NDS32 20 /* ANDES Technology - NDS32 */ +#define IH_ARCH_OPENRISC 21 /* OpenRISC 1000 */ +#define IH_ARCH_ARM64 22 /* ARM64 */ +#define IH_ARCH_ARC 23 /* Synopsys DesignWare ARC */ +#define IH_ARCH_X86_64 24 /* AMD x86_64, Intel and Via */ +#define IH_ARCH_XTENSA 25 /* Xtensa */ +#define IH_ARCH_LOONGARCH 26 /* LoongArch Loongson */ + +/* + * Image Types + * + * "Standalone Programs" are directly runnable in the environment + * provided by U-Boot; it is expected that (if they behave + * well) you can continue to work in U-Boot after return from + * the Standalone Program. + * "OS Kernel Images" are usually images of some Embedded OS which + * will take over control completely. Usually these programs + * will install their own set of exception handlers, device + * drivers, set up the MMU, etc. - this means, that you cannot + * expect to re-enter U-Boot except by resetting the CPU. + * "RAMDisk Images" are more or less just data blocks, and their + * parameters (address, size) are passed to an OS kernel that is + * being started. + * "Multi-File Images" contain several images, typically an OS + * (Linux) kernel image and one or more data images like + * RAMDisks. This construct is useful for instance when you want + * to boot over the network using BOOTP etc., where the boot + * server provides just a single image file, but you want to get + * for instance an OS kernel and a RAMDisk image. + * + * "Multi-File Images" start with a list of image sizes, each + * image size (in bytes) specified by an "uint32_t" in network + * byte order. This list is terminated by an "(uint32_t)0". + * Immediately after the terminating 0 follow the images, one by + * one, all aligned on "uint32_t" boundaries (size rounded up to + * a multiple of 4 bytes - except for the last file). + * + * "Firmware Images" are binary images containing firmware (like + * U-Boot or FPGA images) which usually will be programmed to + * flash memory. + * + * "Script files" are command sequences that will be executed by + * U-Boot's command interpreter; this feature is especially + * useful when you configure U-Boot to use a real shell (hush) + * as command interpreter (=> Shell Scripts). + */ + +#define IH_TYPE_INVALID 0 /* Invalid Image */ +#define IH_TYPE_STANDALONE 1 /* Standalone Program */ +#define IH_TYPE_KERNEL 2 /* OS Kernel Image */ +#define IH_TYPE_RAMDISK 3 /* RAMDisk Image */ +#define IH_TYPE_MULTI 4 /* Multi-File Image */ +#define IH_TYPE_FIRMWARE 5 /* Firmware Image */ +#define IH_TYPE_SCRIPT 6 /* Script file */ +#define IH_TYPE_FILESYSTEM 7 /* Filesystem Image (any type) */ +#define IH_TYPE_FLATDT 8 /* Binary Flat Device Tree Blob */ +#define IH_TYPE_KWBIMAGE 9 /* Kirkwood Boot Image */ +#define IH_TYPE_KERNEL_NOLOAD 14 /* OS Kernel Image, can run */ + /* from any load address */ + +/* + * Compression Types + */ +#define IH_COMP_NONE 0 /* No Compression Used */ +#define IH_COMP_GZIP 1 /* gzip Compression Used */ +#define IH_COMP_BZIP2 2 /* bzip2 Compression Used */ +#define IH_COMP_LZMA 3 /* lzma Compression Used */ +#define IH_COMP_LZO 4 /* lzo Compression Used */ + +#define IH_MAGIC 0x27051956 /* Image Magic Number */ +#define IH_NMLEN 32 /* Image Name Length */ + +/* + * all data in network byte order (aka natural aka bigendian) + */ + +typedef struct image_header { + uint32_t ih_magic; /* Image Header Magic Number */ + uint32_t ih_hcrc; /* Image Header CRC Checksum */ + uint32_t ih_time; /* Image Creation Timestamp */ + uint32_t ih_size; /* Image Data Size */ + uint32_t ih_load; /* Data Load Address */ + uint32_t ih_ep; /* Entry Point Address */ + uint32_t ih_dcrc; /* Image Data CRC Checksum */ + uint8_t ih_os; /* Operating System */ + uint8_t ih_arch; /* CPU architecture */ + uint8_t ih_type; /* Image Type */ + uint8_t ih_comp; /* Compression Type */ + uint8_t ih_name[IH_NMLEN]; /* Image Name */ +} image_header_t; + +#endif /* __IMAGE_H__ */ diff --git a/include/kexec-pe-zboot.h b/include/kexec-pe-zboot.h new file mode 100644 index 0000000..e2e0448 --- /dev/null +++ b/include/kexec-pe-zboot.h @@ -0,0 +1,15 @@ +#ifndef __KEXEC_PE_ZBOOT_H__ +#define __KEXEC_PE_ZBOOT_H__ + +/* see drivers/firmware/efi/libstub/zboot-header.S */ +struct linux_pe_zboot_header { + uint32_t mz_magic; + uint32_t image_type; + uint32_t payload_offset; + uint32_t payload_size; + uint32_t reserved[2]; + uint32_t compress_type; +}; + +int pez_prepare(const char *crude_buf, off_t buf_sz, int *kernel_fd); +#endif diff --git a/include/kexec-uImage.h b/include/kexec-uImage.h new file mode 100644 index 0000000..983d63f --- /dev/null +++ b/include/kexec-uImage.h @@ -0,0 +1,15 @@ +#ifndef __KEXEC_UIMAGE_H__ +#define __KEXEC_UIMAGE_H__ + +struct Image_info { + const char *buf; + off_t len; + unsigned int base; + unsigned int ep; +}; + +int uImage_probe(const char *buf, off_t len, unsigned int arch); +int uImage_probe_kernel(const char *buf, off_t len, unsigned int arch); +int uImage_probe_ramdisk(const char *buf, off_t len, unsigned int arch); +int uImage_load(const char *buf, off_t len, struct Image_info *info); +#endif diff --git a/include/unused.h b/include/unused.h new file mode 100644 index 0000000..7b40190 --- /dev/null +++ b/include/unused.h @@ -0,0 +1,15 @@ +#ifndef UNUSED_H +#define UNUSED_H + +/* http://sourcefrog.net/weblog/software/languages/C/unused.html */ + +#ifdef UNUSED +#elif defined(__GNUC__) +# define UNUSED(x) UNUSED_ ## x __attribute__((unused)) +#elif defined(__LCLINT__) +# define UNUSED(x) /*@unused@*/ x +#else +# define UNUSED(x) x +#endif + +#endif diff --git a/include/x86/mb_header.h b/include/x86/mb_header.h new file mode 100644 index 0000000..9bf5e6c --- /dev/null +++ b/include/x86/mb_header.h @@ -0,0 +1,90 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2000 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * MultiBoot Header description + */ + +struct multiboot_header +{ + /* Must be MULTIBOOT_MAGIC - see below. */ + uint32_t magic; + + /* Feature flags - see below. */ + uint32_t flags; + + /* + * Checksum + * + * The above fields plus this one must equal 0 mod 2^32. + */ + uint32_t checksum; + + /* These are only valid if MULTIBOOT_AOUT_KLUDGE is set. */ + uint32_t header_addr; + uint32_t load_addr; + uint32_t load_end_addr; + uint32_t bss_end_addr; + uint32_t entry_addr; + + /* These are only valid if MULTIBOOT_VIDEO_MODE is set. */ + uint32_t mode_type; + uint32_t width; + uint32_t height; + uint32_t depth; +}; + +/* + * The entire multiboot_header must be contained + * within the first MULTIBOOT_SEARCH bytes of the kernel image. + */ +#define MULTIBOOT_SEARCH 8192 +#define MULTIBOOT_FOUND(addr, len) \ + (! ((addr) & 0x3) \ + && (len) >= 12 \ + && *((int *) (addr)) == MULTIBOOT_MAGIC \ + && ! (*((uint32_t *) (addr)) + *((uint32_t *) (addr + 4)) \ + + *((uint32_t *) (addr + 8))) \ + && (! (MULTIBOOT_AOUT_KLUDGE & *((int *) (addr + 4))) || (len) >= 32) \ + && (! (MULTIBOOT_VIDEO_MODE & *((int *) (addr + 4))) || (len) >= 48)) + +/* Magic value identifying the multiboot_header. */ +#define MULTIBOOT_MAGIC 0x1BADB002 + +/* + * Features flags for 'flags'. + * If a boot loader sees a flag in MULTIBOOT_MUSTKNOW set + * and it doesn't understand it, it must fail. + */ +#define MULTIBOOT_MUSTKNOW 0x0000FFFF + +/* currently unsupported flags... this is a kind of version number. */ +#define MULTIBOOT_UNSUPPORTED 0x0000FFF8 + +/* Align all boot modules on i386 page (4KB) boundaries. */ +#define MULTIBOOT_PAGE_ALIGN 0x00000001 + +/* Must pass memory information to OS. */ +#define MULTIBOOT_MEMORY_INFO 0x00000002 + +/* Must pass video information to OS. */ +#define MULTIBOOT_VIDEO_MODE 0x00000004 + +/* This flag indicates the use of the address fields in the header. */ +#define MULTIBOOT_AOUT_KLUDGE 0x00010000 diff --git a/include/x86/mb_info.h b/include/x86/mb_info.h new file mode 100644 index 0000000..fd1bb1d --- /dev/null +++ b/include/x86/mb_info.h @@ -0,0 +1,246 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2000 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * The structure type "mod_list" is used by the "multiboot_info" structure. + */ + +struct mod_list +{ + /* the memory used goes from bytes 'mod_start' to 'mod_end-1' inclusive */ + uint32_t mod_start; + uint32_t mod_end; + + /* Module command line */ + uint32_t cmdline; + + /* padding to take it to 16 bytes (must be zero) */ + uint32_t pad; +}; + + +/* + * INT-15, AX=E820 style "AddressRangeDescriptor" + * ...with a "size" parameter on the front which is the structure size - 4, + * pointing to the next one, up until the full buffer length of the memory + * map has been reached. + */ + +struct AddrRangeDesc +{ + uint32_t size; + uint32_t base_addr_low; + uint32_t base_addr_high; + uint32_t length_low; + uint32_t length_high; + uint32_t Type; + + /* unspecified optional padding... */ +}; + +/* usable memory "Type", all others are reserved. */ +#define MB_ARD_MEMORY 1 + + +/* Drive Info structure. */ +struct drive_info +{ + /* The size of this structure. */ + uint32_t size; + + /* The BIOS drive number. */ + uint8_t drive_number; + + /* The access mode (see below). */ + uint8_t drive_mode; + + /* The BIOS geometry. */ + uint16_t drive_cylinders; + uint8_t drive_heads; + uint8_t drive_sectors; + + /* The array of I/O ports used for the drive. */ + uint16_t drive_ports[0]; +}; + +/* Drive Mode. */ +#define MB_DI_CHS_MODE 0 +#define MB_DI_LBA_MODE 1 + + +/* APM BIOS info. */ +struct apm_info +{ + uint16_t version; + uint16_t cseg; + uint32_t offset; + uint32_t cseg_16; + uint32_t dseg_16; + uint32_t cseg_len; + uint32_t cseg_16_len; + uint32_t dseg_16_len; +}; + + +/* + * MultiBoot Info description + * + * This is the struct passed to the boot image. This is done by placing + * its address in the EAX register. + */ + +struct multiboot_info +{ + /* MultiBoot info version number */ + uint32_t flags; + + /* Available memory from BIOS */ + uint32_t mem_lower; + uint32_t mem_upper; + + /* "root" partition */ + uint32_t boot_device; + + /* Kernel command line */ + uint32_t cmdline; + + /* Boot-Module list */ + uint32_t mods_count; + uint32_t mods_addr; + + union + { + struct + { + /* (a.out) Kernel symbol table info */ + uint32_t tabsize; + uint32_t strsize; + uint32_t addr; + uint32_t pad; + } + a; + + struct + { + /* (ELF) Kernel section header table */ + uint32_t num; + uint32_t size; + uint32_t addr; + uint32_t shndx; + } + e; + } + syms; + + /* Memory Mapping buffer */ + uint32_t mmap_length; + uint32_t mmap_addr; + + /* Drive Info buffer */ + uint32_t drives_length; + uint32_t drives_addr; + + /* ROM configuration table */ + uint32_t config_table; + + /* Boot Loader Name */ + uint32_t boot_loader_name; + + /* APM table */ + uint32_t apm_table; + + /* Video */ + uint32_t vbe_control_info; + uint32_t vbe_mode_info; + uint16_t vbe_mode; + uint16_t vbe_interface_seg; + uint16_t vbe_interface_off; + uint16_t vbe_interface_len; + + uint64_t framebuffer_addr; + uint32_t framebuffer_pitch; + uint32_t framebuffer_width; + uint32_t framebuffer_height; + uint8_t framebuffer_bpp; + uint8_t framebuffer_type; + + union { + struct { + uint32_t framebuffer_palette_addr; + uint16_t framebuffer_palette_num_color; + }; + struct { + uint8_t framebuffer_red_field_position; + uint8_t framebuffer_red_mask_size; + uint8_t framebuffer_green_field_position; + uint8_t framebuffer_green_mask_size; + uint8_t framebuffer_blue_field_position; + uint8_t framebuffer_blue_mask_size; + }; + }; +}; + +/* + * Flags to be set in the 'flags' parameter above + */ + +/* is there basic lower/upper memory information? */ +#define MB_INFO_MEMORY 0x00000001 +/* is there a boot device set? */ +#define MB_INFO_BOOTDEV 0x00000002 +/* is the command-line defined? */ +#define MB_INFO_CMDLINE 0x00000004 +/* are there modules to do something with? */ +#define MB_INFO_MODS 0x00000008 + +/* These next two are mutually exclusive */ + +/* is there a symbol table loaded? */ +#define MB_INFO_AOUT_SYMS 0x00000010 +/* is there an ELF section header table? */ +#define MB_INFO_ELF_SHDR 0x00000020 + +/* is there a full memory map? */ +#define MB_INFO_MEM_MAP 0x00000040 + +/* Is there drive info? */ +#define MB_INFO_DRIVE_INFO 0x00000080 + +/* Is there a config table? */ +#define MB_INFO_CONFIG_TABLE 0x00000100 + +/* Is there a boot loader name? */ +#define MB_INFO_BOOT_LOADER_NAME 0x00000200 + +/* Is there a APM table? */ +#define MB_INFO_APM_TABLE 0x00000400 + +/* Is there video information? */ +#define MB_INFO_VIDEO_INFO 0x00000800 +#define MB_INFO_FRAMEBUFFER_INFO 0x00001000 + +#define MB_FRAMEBUFFER_TYPE_INDEXED 0 +#define MB_FRAMEBUFFER_TYPE_RGB 1 +#define MB_FRAMEBUFFER_TYPE_EGA_TEXT 2 + +/* + * The following value must be present in the EAX register. + */ + +#define MULTIBOOT_VALID 0x2BADB002 diff --git a/include/x86/multiboot2.h b/include/x86/multiboot2.h new file mode 100644 index 0000000..5693923 --- /dev/null +++ b/include/x86/multiboot2.h @@ -0,0 +1,416 @@ +/* multiboot2.h - Multiboot 2 header file. */ +/* Copyright (C) 1999,2003,2007,2008,2009,2010 Free Software Foundation, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ANY + * DEVELOPER OR DISTRIBUTOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR + * IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef MULTIBOOT_HEADER +#define MULTIBOOT_HEADER 1 + +/* How many bytes from the start of the file we search for the header. */ +#define MULTIBOOT_SEARCH 32768 +#define MULTIBOOT_HEADER_ALIGN 8 + +/* The magic field should contain this. */ +#define MULTIBOOT2_HEADER_MAGIC 0xe85250d6 + +/* This should be in %eax. */ +#define MULTIBOOT2_BOOTLOADER_MAGIC 0x36d76289 + +/* Alignment of multiboot modules. */ +#define MULTIBOOT_MOD_ALIGN 0x00001000 + +/* Alignment of the multiboot info structure. */ +#define MULTIBOOT_INFO_ALIGN 0x00000008 + +/* Flags set in the 'flags' member of the multiboot header. */ + +#define MULTIBOOT_TAG_ALIGN 8 +#define MULTIBOOT_TAG_TYPE_END 0 +#define MULTIBOOT_TAG_TYPE_CMDLINE 1 +#define MULTIBOOT_TAG_TYPE_BOOT_LOADER_NAME 2 +#define MULTIBOOT_TAG_TYPE_MODULE 3 +#define MULTIBOOT_TAG_TYPE_BASIC_MEMINFO 4 +#define MULTIBOOT_TAG_TYPE_BOOTDEV 5 +#define MULTIBOOT_TAG_TYPE_MMAP 6 +#define MULTIBOOT_TAG_TYPE_VBE 7 +#define MULTIBOOT_TAG_TYPE_FRAMEBUFFER 8 +#define MULTIBOOT_TAG_TYPE_ELF_SECTIONS 9 +#define MULTIBOOT_TAG_TYPE_APM 10 +#define MULTIBOOT_TAG_TYPE_EFI32 11 +#define MULTIBOOT_TAG_TYPE_EFI64 12 +#define MULTIBOOT_TAG_TYPE_SMBIOS 13 +#define MULTIBOOT_TAG_TYPE_ACPI_OLD 14 +#define MULTIBOOT_TAG_TYPE_ACPI_NEW 15 +#define MULTIBOOT_TAG_TYPE_NETWORK 16 +#define MULTIBOOT_TAG_TYPE_EFI_MMAP 17 +#define MULTIBOOT_TAG_TYPE_EFI_BS 18 +#define MULTIBOOT_TAG_TYPE_EFI32_IH 19 +#define MULTIBOOT_TAG_TYPE_EFI64_IH 20 +#define MULTIBOOT_TAG_TYPE_LOAD_BASE_ADDR 21 + +#define MULTIBOOT_HEADER_TAG_END 0 +#define MULTIBOOT_HEADER_TAG_INFORMATION_REQUEST 1 +#define MULTIBOOT_HEADER_TAG_ADDRESS 2 +#define MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS 3 +#define MULTIBOOT_HEADER_TAG_CONSOLE_FLAGS 4 +#define MULTIBOOT_HEADER_TAG_FRAMEBUFFER 5 +#define MULTIBOOT_HEADER_TAG_MODULE_ALIGN 6 +#define MULTIBOOT_HEADER_TAG_EFI_BS 7 +#define MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS_EFI64 9 +#define MULTIBOOT_HEADER_TAG_RELOCATABLE 10 + +#define MULTIBOOT2_ARCHITECTURE_I386 0 +#define MULTIBOOT2_ARCHITECTURE_MIPS32 4 +#define MULTIBOOT_HEADER_TAG_OPTIONAL 1 + +#define MULTIBOOT_LOAD_PREFERENCE_NONE 0 +#define MULTIBOOT_LOAD_PREFERENCE_LOW 1 +#define MULTIBOOT_LOAD_PREFERENCE_HIGH 2 + +#define MULTIBOOT_CONSOLE_FLAGS_CONSOLE_REQUIRED 1 +#define MULTIBOOT_CONSOLE_FLAGS_EGA_TEXT_SUPPORTED 2 + +#ifndef ASM_FILE + +typedef unsigned char multiboot_uint8_t; +typedef unsigned short multiboot_uint16_t; +typedef unsigned int multiboot_uint32_t; +typedef unsigned long long multiboot_uint64_t; + +struct multiboot_header +{ + /* Must be MULTIBOOT_MAGIC - see above. */ + multiboot_uint32_t magic; + + /* ISA */ + multiboot_uint32_t architecture; + + /* Total header length. */ + multiboot_uint32_t header_length; + + /* The above fields plus this one must equal 0 mod 2^32. */ + multiboot_uint32_t checksum; +}; + +struct multiboot_header_tag +{ + multiboot_uint16_t type; + multiboot_uint16_t flags; + multiboot_uint32_t size; +}; + +struct multiboot_header_tag_information_request +{ + multiboot_uint16_t type; + multiboot_uint16_t flags; + multiboot_uint32_t size; + multiboot_uint32_t requests[0]; +}; + +struct multiboot_header_tag_address +{ + multiboot_uint16_t type; + multiboot_uint16_t flags; + multiboot_uint32_t size; + multiboot_uint32_t header_addr; + multiboot_uint32_t load_addr; + multiboot_uint32_t load_end_addr; + multiboot_uint32_t bss_end_addr; +}; + +struct multiboot_header_tag_entry_address +{ + multiboot_uint16_t type; + multiboot_uint16_t flags; + multiboot_uint32_t size; + multiboot_uint32_t entry_addr; +}; + +struct multiboot_header_tag_console_flags +{ + multiboot_uint16_t type; + multiboot_uint16_t flags; + multiboot_uint32_t size; + multiboot_uint32_t console_flags; +}; + +struct multiboot_header_tag_framebuffer +{ + multiboot_uint16_t type; + multiboot_uint16_t flags; + multiboot_uint32_t size; + multiboot_uint32_t width; + multiboot_uint32_t height; + multiboot_uint32_t depth; +}; + +struct multiboot_header_tag_module_align +{ + multiboot_uint16_t type; + multiboot_uint16_t flags; + multiboot_uint32_t size; +}; + +struct multiboot_header_tag_relocatable +{ + multiboot_uint16_t type; + multiboot_uint16_t flags; + multiboot_uint32_t size; + multiboot_uint32_t min_addr; + multiboot_uint32_t max_addr; + multiboot_uint32_t align; + multiboot_uint32_t preference; +}; + +struct multiboot_color +{ + multiboot_uint8_t red; + multiboot_uint8_t green; + multiboot_uint8_t blue; +}; + +struct multiboot_mmap_entry +{ + multiboot_uint64_t addr; + multiboot_uint64_t len; +#define MULTIBOOT_MEMORY_AVAILABLE 1 +#define MULTIBOOT_MEMORY_RESERVED 2 +#define MULTIBOOT_MEMORY_ACPI_RECLAIMABLE 3 +#define MULTIBOOT_MEMORY_NVS 4 +#define MULTIBOOT_MEMORY_BADRAM 5 + multiboot_uint32_t type; + multiboot_uint32_t zero; +}; +typedef struct multiboot_mmap_entry multiboot_memory_map_t; + +struct multiboot_tag +{ + multiboot_uint32_t type; + multiboot_uint32_t size; +}; + +struct multiboot_tag_string +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + char string[0]; +}; + +struct multiboot_tag_module +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint32_t mod_start; + multiboot_uint32_t mod_end; + char cmdline[0]; +}; + +struct multiboot_tag_basic_meminfo +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint32_t mem_lower; + multiboot_uint32_t mem_upper; +}; + +struct multiboot_tag_bootdev +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint32_t biosdev; + multiboot_uint32_t slice; + multiboot_uint32_t part; +}; + +struct multiboot_tag_mmap +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint32_t entry_size; + multiboot_uint32_t entry_version; + struct multiboot_mmap_entry entries[0]; +}; + +struct multiboot_vbe_info_block +{ + multiboot_uint8_t external_specification[512]; +}; + +struct multiboot_vbe_mode_info_block +{ + multiboot_uint8_t external_specification[256]; +}; + +struct multiboot_tag_vbe +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + + multiboot_uint16_t vbe_mode; + multiboot_uint16_t vbe_interface_seg; + multiboot_uint16_t vbe_interface_off; + multiboot_uint16_t vbe_interface_len; + + struct multiboot_vbe_info_block vbe_control_info; + struct multiboot_vbe_mode_info_block vbe_mode_info; +}; + +struct multiboot_tag_framebuffer_common +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + + multiboot_uint64_t framebuffer_addr; + multiboot_uint32_t framebuffer_pitch; + multiboot_uint32_t framebuffer_width; + multiboot_uint32_t framebuffer_height; + multiboot_uint8_t framebuffer_bpp; +#define MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED 0 +#define MULTIBOOT_FRAMEBUFFER_TYPE_RGB 1 +#define MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT 2 + multiboot_uint8_t framebuffer_type; + multiboot_uint16_t reserved; +}; + +struct multiboot_tag_framebuffer +{ + struct multiboot_tag_framebuffer_common common; + + union + { + struct + { + multiboot_uint16_t framebuffer_palette_num_colors; + struct multiboot_color framebuffer_palette[0]; + }; + struct + { + multiboot_uint8_t framebuffer_red_field_position; + multiboot_uint8_t framebuffer_red_mask_size; + multiboot_uint8_t framebuffer_green_field_position; + multiboot_uint8_t framebuffer_green_mask_size; + multiboot_uint8_t framebuffer_blue_field_position; + multiboot_uint8_t framebuffer_blue_mask_size; + }; + }; +}; + +struct multiboot_tag_elf_sections +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint32_t num; + multiboot_uint32_t entsize; + multiboot_uint32_t shndx; + char sections[0]; +}; + +struct multiboot_tag_apm +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint16_t version; + multiboot_uint16_t cseg; + multiboot_uint32_t offset; + multiboot_uint16_t cseg_16; + multiboot_uint16_t dseg; + multiboot_uint16_t flags; + multiboot_uint16_t cseg_len; + multiboot_uint16_t cseg_16_len; + multiboot_uint16_t dseg_len; +}; + +struct multiboot_tag_efi32 +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint32_t pointer; +}; + +struct multiboot_tag_efi64 +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint64_t pointer; +}; + +struct multiboot_tag_smbios +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint8_t major; + multiboot_uint8_t minor; + multiboot_uint8_t reserved[6]; + multiboot_uint8_t tables[0]; +}; + +struct multiboot_tag_old_acpi +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint8_t rsdp[0]; +}; + +struct multiboot_tag_new_acpi +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint8_t rsdp[0]; +}; + +struct multiboot_tag_network +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint8_t dhcpack[0]; +}; + +struct multiboot_tag_efi_mmap +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint32_t descr_size; + multiboot_uint32_t descr_vers; + multiboot_uint8_t efi_mmap[0]; +}; + +struct multiboot_tag_efi32_ih +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint32_t pointer; +}; + +struct multiboot_tag_efi64_ih +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint64_t pointer; +}; + +struct multiboot_tag_load_base_addr +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint32_t load_base_addr; +}; + +#endif /* ! ASM_FILE */ + +#endif /* ! MULTIBOOT_HEADER */ diff --git a/include/x86/x86-linux.h b/include/x86/x86-linux.h new file mode 100644 index 0000000..9646102 --- /dev/null +++ b/include/x86/x86-linux.h @@ -0,0 +1,271 @@ +#ifndef X86_LINUX_H +#define X86_LINUX_H + +#define TENATIVE 0 /* Code that is tenatively correct but hasn't yet been officially accepted */ + +#define E820MAP 0x2d0 /* our map */ +#define E820NR 0x1e8 /* # entries in E820MAP */ + +#ifndef E820MAX +#define E820MAX 128 /* number of entries in E820MAP */ +#endif + +#ifndef ASSEMBLY + +#ifndef E820_RAM +struct e820entry { + uint64_t addr; /* start of memory segment */ + uint64_t size; /* size of memory segment */ + uint32_t type; /* type of memory segment */ +#define E820_RAM 1 +#define E820_RESERVED 2 +#define E820_ACPI 3 /* usable as RAM once ACPI tables have been read */ +#define E820_NVS 4 +#define E820_PMEM 7 +#define E820_PRAM 12 +} __attribute__((packed)); +#endif + +/* FIXME expand on drive_info_)struct... */ +struct drive_info_struct { + uint8_t dummy[32]; +}; +struct sys_desc_table { + uint16_t length; + uint8_t table[30]; +}; + +struct apm_bios_info { + uint16_t version; /* 0x40 */ + uint16_t cseg; /* 0x42 */ + uint32_t offset; /* 0x44 */ + uint16_t cseg_16; /* 0x48 */ + uint16_t dseg; /* 0x4a */ + uint16_t flags; /* 0x4c */ + uint16_t cseg_len; /* 0x4e */ + uint16_t cseg_16_len; /* 0x50 */ + uint16_t dseg_len; /* 0x52 */ +}; + +/* + * EDD stuff + */ + +#define EDD_MBR_SIG_MAX 16 +#define EDDMAXNR 6 /* number of edd_info structs starting at EDDBUF */ + +#define EDD_EXT_FIXED_DISK_ACCESS (1 << 0) +#define EDD_EXT_DEVICE_LOCKING_AND_EJECTING (1 << 1) +#define EDD_EXT_ENHANCED_DISK_DRIVE_SUPPORT (1 << 2) +#define EDD_EXT_64BIT_EXTENSIONS (1 << 3) + +#define EDD_DEVICE_PARAM_SIZE 74 + +struct edd_info { + uint8_t device; + uint8_t version; + uint16_t interface_support; + uint16_t legacy_max_cylinder; + uint8_t legacy_max_head; + uint8_t legacy_sectors_per_track; + uint8_t edd_device_params[EDD_DEVICE_PARAM_SIZE]; +} __attribute__ ((packed)); + +struct x86_linux_param_header { + uint8_t orig_x; /* 0x00 */ + uint8_t orig_y; /* 0x01 */ + uint16_t ext_mem_k; /* 0x02 -- EXT_MEM_K sits here */ + uint16_t orig_video_page; /* 0x04 */ + uint8_t orig_video_mode; /* 0x06 */ + uint8_t orig_video_cols; /* 0x07 */ + uint16_t unused2; /* 0x08 */ + uint16_t orig_video_ega_bx; /* 0x0a */ + uint16_t unused3; /* 0x0c */ + uint8_t orig_video_lines; /* 0x0e */ + uint8_t orig_video_isVGA; /* 0x0f */ + uint16_t orig_video_points; /* 0x10 */ + + /* VESA graphic mode -- linear frame buffer */ + uint16_t lfb_width; /* 0x12 */ + uint16_t lfb_height; /* 0x14 */ + uint16_t lfb_depth; /* 0x16 */ + uint32_t lfb_base; /* 0x18 */ + uint32_t lfb_size; /* 0x1c */ + uint16_t cl_magic; /* 0x20 */ +#define CL_MAGIC_VALUE 0xA33F + uint16_t cl_offset; /* 0x22 */ + uint16_t lfb_linelength; /* 0x24 */ + uint8_t red_size; /* 0x26 */ + uint8_t red_pos; /* 0x27 */ + uint8_t green_size; /* 0x28 */ + uint8_t green_pos; /* 0x29 */ + uint8_t blue_size; /* 0x2a */ + uint8_t blue_pos; /* 0x2b */ + uint8_t rsvd_size; /* 0x2c */ + uint8_t rsvd_pos; /* 0x2d */ + uint16_t vesapm_seg; /* 0x2e */ + uint16_t vesapm_off; /* 0x30 */ + uint16_t pages; /* 0x32 */ + uint16_t vesa_attributes; /* 0x34 */ + uint32_t capabilities; /* 0x36 */ + uint32_t ext_lfb_base; /* 0x3a */ + uint8_t reserved4[2]; /* 0x3e -- 0x3f reserved for future expansion */ + + struct apm_bios_info apm_bios_info; /* 0x40 */ + uint8_t reserved4_1[28]; /* 0x54 */ + uint64_t acpi_rsdp_addr; /* 0x70 */ + uint8_t reserved4_2[8]; /* 0x78 */ + struct drive_info_struct drive_info; /* 0x80 */ + struct sys_desc_table sys_desc_table; /* 0xa0 */ + uint32_t ext_ramdisk_image; /* 0xc0 */ + uint32_t ext_ramdisk_size; /* 0xc4 */ + uint32_t ext_cmd_line_ptr; /* 0xc8 */ + uint8_t reserved4_3[0x1c0 - 0xcc]; /* 0xe4 */ + uint8_t efi_info[32]; /* 0x1c0 */ + uint32_t alt_mem_k; /* 0x1e0 */ + uint8_t reserved5[4]; /* 0x1e4 */ + uint8_t e820_map_nr; /* 0x1e8 */ + uint8_t eddbuf_entries; /* 0x1e9 */ + uint8_t edd_mbr_sig_buf_entries; /* 0x1ea */ + uint8_t reserved6[6]; /* 0x1eb */ + uint8_t setup_sects; /* 0x1f1 */ + uint16_t mount_root_rdonly; /* 0x1f2 */ + uint16_t syssize; /* 0x1f4 */ + uint16_t swapdev; /* 0x1f6 */ + uint16_t ramdisk_flags; /* 0x1f8 */ +#define RAMDISK_IMAGE_START_MASK 0x07FF +#define RAMDISK_PROMPT_FLAG 0x8000 +#define RAMDISK_LOAD_FLAG 0x4000 + uint16_t vid_mode; /* 0x1fa */ + uint16_t root_dev; /* 0x1fc */ + uint8_t reserved9[1]; /* 0x1fe */ + uint8_t aux_device_info; /* 0x1ff */ + /* 2.00+ */ + uint8_t reserved10[2]; /* 0x200 */ + uint8_t header_magic[4]; /* 0x202 */ + uint16_t protocol_version; /* 0x206 */ + uint16_t rmode_switch_ip; /* 0x208 */ + uint16_t rmode_switch_cs; /* 0x20a */ + uint8_t reserved11[4]; /* 0x208 */ + uint8_t loader_type; /* 0x210 */ +#define LOADER_TYPE_LOADLIN 1 +#define LOADER_TYPE_BOOTSECT_LOADER 2 +#define LOADER_TYPE_SYSLINUX 3 +#define LOADER_TYPE_ETHERBOOT 4 +#define LOADER_TYPE_KEXEC 0x0D +#define LOADER_TYPE_UNKNOWN 0xFF + uint8_t loader_flags; /* 0x211 */ + uint8_t reserved12[2]; /* 0x212 */ + uint32_t kernel_start; /* 0x214 */ + uint32_t initrd_start; /* 0x218 */ + uint32_t initrd_size; /* 0x21c */ + uint8_t reserved13[4]; /* 0x220 */ + /* 2.01+ */ + uint16_t heap_end_ptr; /* 0x224 */ + uint8_t reserved14[2]; /* 0x226 */ + /* 2.02+ */ + uint32_t cmd_line_ptr; /* 0x228 */ + /* 2.03+ */ + uint32_t initrd_addr_max; /* 0x22c */ +#if TENATIVE + /* 2.04+ */ + uint16_t entry32_off; /* 0x230 */ + uint16_t internal_cmdline_off; /* 0x232 */ + uint32_t low_base; /* 0x234 */ + uint32_t low_memsz; /* 0x238 */ + uint32_t low_filesz; /* 0x23c */ + uint32_t real_base; /* 0x240 */ + uint32_t real_memsz; /* 0x244 */ + uint32_t real_filesz; /* 0x248 */ + uint32_t high_base; /* 0x24C */ + uint32_t high_memsz; /* 0x250 */ + uint32_t high_filesz; /* 0x254 */ + uint8_t reserved15[0x2d0 - 0x258]; /* 0x258 */ +#else + /* 2.04+ */ + uint32_t kernel_alignment; /* 0x230 */ + uint8_t relocatable_kernel; /* 0x234 */ + uint8_t min_alignment; /* 0x235 */ + uint16_t xloadflags; /* 0x236 */ + uint32_t cmdline_size; /* 0x238 */ + uint32_t hardware_subarch; /* 0x23C */ + uint64_t hardware_subarch_data; /* 0x240 */ + uint32_t payload_offset; /* 0x248 */ + uint32_t payload_length; /* 0x24C */ + uint64_t setup_data; /* 0x250 */ + uint64_t pref_address; /* 0x258 */ + uint32_t init_size; /* 0x260 */ + uint32_t handover_offset; /* 0x264 */ + uint8_t reserved16[0x290 - 0x268]; /* 0x268 */ + uint32_t edd_mbr_sig_buffer[EDD_MBR_SIG_MAX]; /* 0x290 */ +#endif + struct e820entry e820_map[E820MAX]; /* 0x2d0 */ + uint8_t _pad8[48]; /* 0xcd0 */ + struct edd_info eddbuf[EDDMAXNR]; /* 0xd00 */ + /* 0xeec */ +#define COMMAND_LINE_SIZE 2048 +} __attribute__((packed)); + +struct x86_linux_faked_param_header { + struct x86_linux_param_header hdr; /* 0x00 */ + uint8_t command_line[COMMAND_LINE_SIZE]; /* 0x1000 */ + // uint8_t reserved18[0x200]; /* 0x1800 - 0x2000 */ +}; + +struct x86_linux_header { + uint8_t reserved1[0xc0]; /* 0x000 */ + uint32_t ext_ramdisk_image; /* 0x0c0 */ + uint32_t ext_ramdisk_size; /* 0x0c4 */ + uint32_t ext_cmd_line_ptr; /* 0x0c8 */ + uint8_t reserved1_1[0x1f1-0xcc]; /* 0x0cc */ + uint8_t setup_sects; /* 0x1f1 */ + uint16_t root_flags; /* 0x1f2 */ + uint32_t syssize; /* 0x1f4 */ + uint16_t ram_size; /* 0x1f8 */ + uint16_t vid_mode; /* 0x1fa */ + uint16_t root_dev; /* 0x1fc */ + uint16_t boot_sector_magic; /* 0x1fe */ + /* 2.00+ */ + uint16_t jump; /* 0x200 */ + uint8_t header_magic[4]; /* 0x202 */ + uint16_t protocol_version; /* 0x206 */ + uint32_t realmode_swtch; /* 0x208 */ + uint16_t start_sys; /* 0x20c */ + uint16_t kver_addr; /* 0x20e */ + uint8_t type_of_loader; /* 0x210 */ + uint8_t loadflags; /* 0x211 */ + uint16_t setup_move_size; /* 0x212 */ + uint32_t code32_start; /* 0x214 */ + uint32_t ramdisk_image; /* 0x218 */ + uint32_t ramdisk_size; /* 0x21c */ + uint32_t bootsect_kludge; /* 0x220 */ + /* 2.01+ */ + uint16_t heap_end_ptr; /* 0x224 */ + uint8_t ext_loader_ver; /* 0x226 */ + uint8_t ext_loader_type; /* 0x227 */ + /* 2.02+ */ + uint32_t cmd_line_ptr; /* 0x228 */ + /* 2.03+ */ + uint32_t initrd_addr_max; /* 0x22c */ + + uint32_t kernel_alignment; /* 0x230 */ + uint8_t relocatable_kernel; /* 0x234 */ + uint8_t min_alignment; /* 0x235 */ + uint16_t xloadflags; /* 0x236 */ + uint32_t cmdline_size; /* 0x238 */ + uint32_t hardware_subarch; /* 0x23C */ + uint64_t hardware_subarch_data; /* 0x240 */ + uint32_t payload_offset; /* 0x248 */ + uint32_t payload_size; /* 0x24C */ + uint64_t setup_data; /* 0x250 */ + uint64_t pref_address; /* 0x258 */ + uint32_t init_size; /* 0x260 */ + uint32_t handover_offset; /* 0x264 */ +} __attribute__((packed)); + +#endif /* ASSEMBLY */ + +#define DEFAULT_INITRD_ADDR_MAX 0x37FFFFFF +#define DEFAULT_BZIMAGE_ADDR_MAX 0x37FFFFFF + +#endif /* X86_LINUX_H */ diff --git a/kexec-tools.spec b/kexec-tools.spec new file mode 100644 index 0000000..169aceb --- /dev/null +++ b/kexec-tools.spec @@ -0,0 +1,42 @@ +Summary: Load one kernel from another +Name: kexec-tools +Version: 2.0.27 +Release: 0 +License: GPL +Group: Development/Tools +Source0:%{name}-%{version}.tar.gz +Packager: Eric Biederman <ebiederman@xmission.com> +BuildRoot: %{_tmppath}/%{name} + +%description +/sbin/kexec is a user space utility for loading another kernel +and asking the currently running kernel to do something with it. +A currently running kernel may be asked to start the loaded +kernel on reboot, or to start the loaded kernel after it panics. + +The panic case is useful for having an intact kernel for writing +crash dumps. But other uses may be imagined. + +%prep +%setup -q -n %{name}-%{version} + +%build +%configure +make + +%install +make install DESTDIR=${RPM_BUILD_ROOT} + +%files +%defattr(-,root,root) +%{_sbindir}/kexec +%{_sbindir}/vmcore-dmesg +%doc News +%doc COPYING +%doc TODO +%{_mandir}/man8/kexec.8.gz +%{_mandir}/man8/vmcore-dmesg.8.gz + +%changelog +* Tue Dec 16 2004 Eric Biederman <ebiederman@lnxi.com> +- kexec-tools initialy packaged as an rpm. diff --git a/kexec-tools.spec.in b/kexec-tools.spec.in new file mode 100644 index 0000000..3e57a22 --- /dev/null +++ b/kexec-tools.spec.in @@ -0,0 +1,42 @@ +Summary: Load one kernel from another +Name: kexec-tools +Version: +Release: 0 +License: GPL +Group: Development/Tools +Source0:%{name}-%{version}.tar.gz +Packager: Eric Biederman <ebiederman@xmission.com> +BuildRoot: %{_tmppath}/%{name} + +%description +/sbin/kexec is a user space utility for loading another kernel +and asking the currently running kernel to do something with it. +A currently running kernel may be asked to start the loaded +kernel on reboot, or to start the loaded kernel after it panics. + +The panic case is useful for having an intact kernel for writing +crash dumps. But other uses may be imagined. + +%prep +%setup -q -n %{name}-%{version} + +%build +%configure +make + +%install +make install DESTDIR=${RPM_BUILD_ROOT} + +%files +%defattr(-,root,root) +%{_sbindir}/kexec +%{_sbindir}/vmcore-dmesg +%doc News +%doc COPYING +%doc TODO +%{_mandir}/man8/kexec.8.gz +%{_mandir}/man8/vmcore-dmesg.8.gz + +%changelog +* Tue Dec 16 2004 Eric Biederman <ebiederman@lnxi.com> +- kexec-tools initialy packaged as an rpm. diff --git a/kexec/Makefile b/kexec/Makefile new file mode 100644 index 0000000..11682bf --- /dev/null +++ b/kexec/Makefile @@ -0,0 +1,126 @@ +# +# kexec (linux booting linux) +# +PURGATORY_HEX_C = kexec/purgatory.c + +$(PURGATORY_HEX_C): $(PURGATORY) $(BIN_TO_HEX) + $(MKDIR) -p $(@D) + $(BIN_TO_HEX) purgatory < $(PURGATORY) > $@ + +KEXEC_SRCS = $(KEXEC_SRCS_base) +KEXEC_GENERATED_SRCS = + +KEXEC_SRCS_base += kexec/kexec.c +KEXEC_SRCS_base += kexec/ifdown.c +KEXEC_SRCS_base += kexec/kexec-elf.c +KEXEC_SRCS_base += kexec/kexec-elf-exec.c +KEXEC_SRCS_base += kexec/kexec-elf-core.c +KEXEC_SRCS_base += kexec/kexec-elf-rel.c +KEXEC_SRCS_base += kexec/kexec-elf-boot.c +KEXEC_SRCS_base += kexec/kexec-pe-zboot.c +KEXEC_SRCS_base += kexec/kexec-iomem.c +KEXEC_SRCS_base += kexec/firmware_memmap.c +KEXEC_SRCS_base += kexec/crashdump.c +KEXEC_SRCS_base += kexec/crashdump-xen.c +KEXEC_SRCS_base += kexec/phys_arch.c +KEXEC_SRCS_base += kexec/lzma.c +KEXEC_SRCS_base += kexec/zlib.c +KEXEC_SRCS_base += kexec/kexec-xen.c +KEXEC_SRCS_base += kexec/symbols.c + +KEXEC_GENERATED_SRCS += $(PURGATORY_HEX_C) + +dist += kexec/Makefile \ + $(KEXEC_SRCS_base) kexec/crashdump-elf.c \ + kexec/crashdump.h kexec/firmware_memmap.h \ + kexec/kexec-elf-boot.h \ + kexec/kexec-elf.h kexec/kexec-sha256.h \ + kexec/kexec-zlib.h kexec/kexec-lzma.h \ + kexec/kexec-xen.h \ + kexec/kexec-syscall.h kexec/kexec.h kexec/kexec.8 + +dist += kexec/proc_iomem.c +$(ARCH)_PROC_IOMEM = kexec/proc_iomem.c +KEXEC_SRCS += $($(ARCH)_PROC_IOMEM) + +dist += kexec/virt_to_phys.c +$(ARCH)_VIRT_TO_PHYS = kexec/virt_to_phys.c +KEXEC_SRCS += $($(ARCH)_VIRT_TO_PHYS) + +dist += kexec/phys_to_virt.c +$(ARCH)_PHYS_TO_VIRT = kexec/phys_to_virt.c +KEXEC_SRCS += $($(ARCH)_PHYS_TO_VIRT) + +dist += kexec/add_segment.c +$(ARCH)_ADD_SEGMENT = kexec/add_segment.c +KEXEC_SRCS += $($(ARCH)_ADD_SEGMENT) + +dist += kexec/add_buffer.c +$(ARCH)_ADD_BUFFER = kexec/add_buffer.c +KEXEC_SRCS += $($(ARCH)_ADD_BUFFER) + +dist += kexec/arch_reuse_initrd.c +$(ARCH)_ARCH_REUSE_INITRD = kexec/arch_reuse_initrd.c +KEXEC_SRCS += $($(ARCH)_ARCH_REUSE_INITRD) + +dist += kexec/kexec-uImage.c +$(ARCH)_UIMAGE = +KEXEC_SRCS += $($(ARCH)_UIMAGE) + +dist += kexec/fs2dt.c kexec/fs2dt.h +$(ARCH)_FS2DT = +KEXEC_SRCS += $($(ARCH)_FS2DT) + +dist += kexec/mem_regions.c kexec/mem_regions.h +$(ARCH)_MEM_REGIONS = +KEXEC_SRCS += $($(ARCH)_MEM_REGIONS) + +dist += kexec/dt-ops.c kexec/dt-ops.h +$(ARCH)_DT_OPS = +KEXEC_SRCS += $($(ARCH)_DT_OPS) + +include $(srcdir)/kexec/arch/alpha/Makefile +include $(srcdir)/kexec/arch/arm/Makefile +include $(srcdir)/kexec/arch/arm64/Makefile +include $(srcdir)/kexec/arch/i386/Makefile +include $(srcdir)/kexec/arch/ia64/Makefile +include $(srcdir)/kexec/arch/m68k/Makefile +include $(srcdir)/kexec/arch/mips/Makefile +include $(srcdir)/kexec/arch/cris/Makefile +include $(srcdir)/kexec/arch/ppc/Makefile +include $(srcdir)/kexec/arch/ppc64/Makefile +include $(srcdir)/kexec/arch/s390/Makefile +include $(srcdir)/kexec/arch/sh/Makefile +include $(srcdir)/kexec/arch/x86_64/Makefile +include $(srcdir)/kexec/arch/hppa/Makefile +include $(srcdir)/kexec/arch/loongarch/Makefile + +KEXEC_SRCS += $($(ARCH)_KEXEC_SRCS) + +KEXEC_OBJS = $(call objify, $(KEXEC_SRCS) $(KEXEC_GENERATED_SRCS)) +KEXEC_DEPS = $(call depify, $(KEXEC_OBJS)) + +clean += $(KEXEC_OBJS) $(KEXEC_DEPS) $(KEXEC_GENERATED_SRCS) \ + $(KEXEC) $(KEXEC_MANPAGE) + +KEXEC = $(SBINDIR)/kexec +KEXEC_MANPAGE = $(MANDIR)/man8/kexec.8 + +-include $(KEXEC_DEPS) + +$(KEXEC): $(KEXEC_OBJS) $(UTIL_LIB) + @$(MKDIR) -p $(@D) + $(LINK.o) -o $@ $^ $(CFLAGS) $(LIBS) + +$(KEXEC): CPPFLAGS+=-I$(srcdir)/kexec/arch/$(ARCH)/include + +kexec/fs2dt.o: CPPFLAGS+=$($(ARCH)_FS2DT_INCLUDE) + +$(KEXEC_MANPAGE): kexec/kexec.8 + @$(MKDIR) -p $(MANDIR)/man8 + cp $^ $(KEXEC_MANPAGE) +echo:: + @echo "KEXEC_SRCS $(KEXEC_SRCS)" + @echo "KEXEC_DEPS $(KEXEC_DEPS)" + @echo "KEXEC_OBJS $(KEXEC_OBJS)" + diff --git a/kexec/add_buffer.c b/kexec/add_buffer.c new file mode 100644 index 0000000..4d4a55f --- /dev/null +++ b/kexec/add_buffer.c @@ -0,0 +1,14 @@ +#include "kexec.h" + +unsigned long add_buffer(struct kexec_info *info, + const void *buf, + unsigned long bufsz, + unsigned long memsz, + unsigned long buf_align, + unsigned long buf_min, + unsigned long buf_max, + int buf_end) +{ + return add_buffer_virt(info, buf, bufsz, memsz, buf_align, + buf_min, buf_max, buf_end); +} diff --git a/kexec/add_segment.c b/kexec/add_segment.c new file mode 100644 index 0000000..029c376 --- /dev/null +++ b/kexec/add_segment.c @@ -0,0 +1,8 @@ +#include "kexec.h" + +void add_segment(struct kexec_info *info, + const void *buf, size_t bufsz, + unsigned long base, size_t memsz) +{ + return add_segment_phys_virt(info, buf, bufsz, base, memsz, 0); +} diff --git a/kexec/arch/alpha/Makefile b/kexec/arch/alpha/Makefile new file mode 100644 index 0000000..4575d61 --- /dev/null +++ b/kexec/arch/alpha/Makefile @@ -0,0 +1,4 @@ +alpha_KEXEC_SRCS= +dist += kexec/arch/alpha/Makefile kexec/arch/alpha/include/arch/options.h + $(alpha_KEXEC_SRCS) + diff --git a/kexec/arch/alpha/include/arch/options.h b/kexec/arch/alpha/include/arch/options.h new file mode 100644 index 0000000..a012c8a --- /dev/null +++ b/kexec/arch/alpha/include/arch/options.h @@ -0,0 +1,20 @@ +#ifndef KEXEC_ARCH_ALPHA_OPTIONS_H +#define KEXEC_ARCH_ALPHA_OPTIONS_H + +#define OPT_ARCH_MAX (OPT_MAX+0) + +/* Options relevant to the architecture (excluding loader-specific ones), + * in this case none: + */ +#define KEXEC_ARCH_OPTIONS \ + KEXEC_OPTIONS \ + +#define KEXEC_ARCH_OPT_STR KEXEC_OPT_STR "" + +/* See the other architectures for details of these; Alpha has no + * loader-specific options yet. + */ +#define KEXEC_ALL_OPTIONS KEXEC_ARCH_OPTIONS +#define KEXEC_ALL_OPT_STR KEXEC_ARCH_OPT_STR + +#endif /* KEXEC_ARCH_ALPHA_OPTIONS_H */ diff --git a/kexec/arch/arm/Makefile b/kexec/arch/arm/Makefile new file mode 100644 index 0000000..4454f47 --- /dev/null +++ b/kexec/arch/arm/Makefile @@ -0,0 +1,34 @@ +# +# kexec arm (linux booting linux) +# +include $(srcdir)/kexec/libfdt/Makefile.libfdt + +arm_FS2DT = kexec/fs2dt.c +arm_FS2DT_INCLUDE = -include $(srcdir)/kexec/arch/arm/crashdump-arm.h \ + -include $(srcdir)/kexec/arch/arm/kexec-arm.h + +arm_MEM_REGIONS = kexec/mem_regions.c + +arm_KEXEC_SRCS= kexec/arch/arm/kexec-elf-rel-arm.c +arm_KEXEC_SRCS+= kexec/arch/arm/kexec-zImage-arm.c +arm_KEXEC_SRCS+= kexec/arch/arm/kexec-uImage-arm.c +arm_KEXEC_SRCS+= kexec/arch/arm/kexec-arm.c +arm_KEXEC_SRCS+= kexec/arch/arm/crashdump-arm.c +arm_KEXEC_SRCS+= kexec/fs2dt.c + +libfdt_SRCS += $(LIBFDT_SRCS:%=kexec/libfdt/%) + +arm_CPPFLAGS = -I$(srcdir)/kexec/libfdt + +# We want 64-bit file IO for kdump to work correctly on LPAE systems +arm_CPPFLAGS += -D_FILE_OFFSET_BITS=64 + +arm_KEXEC_SRCS += $(libfdt_SRCS) + +arm_UIMAGE = kexec/kexec-uImage.c +arm_PHYS_TO_VIRT = kexec/arch/arm/phys_to_virt.c + +dist += kexec/arch/arm/Makefile $(arm_KEXEC_SRCS) $(arm_PHYS_TO_VIRT) \ + kexec/arch/arm/iomem.h kexec/arch/arm/phys_to_virt.h \ + kexec/arch/arm/crashdump-arm.h kexec/arch/arm/kexec-arm.h \ + kexec/arch/arm/include/arch/options.h diff --git a/kexec/arch/arm/crashdump-arm.c b/kexec/arch/arm/crashdump-arm.c new file mode 100644 index 0000000..1ec1826 --- /dev/null +++ b/kexec/arch/arm/crashdump-arm.c @@ -0,0 +1,388 @@ +/* + * kexec: Linux boots Linux + * + * Copyright (C) Nokia Corporation, 2010. + * Author: Mika Westerberg + * + * Based on x86 implementation + * Copyright (C) IBM Corporation, 2005. All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation (version 2 of the License). + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include <limits.h> +#include <elf.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include "../../kexec.h" +#include "../../kexec-elf.h" +#include "../../crashdump.h" +#include "../../mem_regions.h" +#include "crashdump-arm.h" +#include "iomem.h" +#include "phys_to_virt.h" + +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define ELFDATANATIVE ELFDATA2LSB +#elif __BYTE_ORDER == __BIG_ENDIAN +#define ELFDATANATIVE ELFDATA2MSB +#else +#error "Unknown machine endian" +#endif + +/* + * Used to save various memory ranges/regions needed for the captured + * kernel to boot. (lime memmap= option in other archs) + */ +static struct memory_range crash_memory_ranges[CRASH_MAX_MEMORY_RANGES]; +struct memory_ranges usablemem_rgns = { + .max_size = CRASH_MAX_MEMORY_RANGES, + .ranges = crash_memory_ranges, +}; + +/* The boot-time physical memory range reserved for crashkernel region */ +struct memory_range crash_kernel_mem; + +/* reserved regions */ +#define CRASH_MAX_RESERVED_RANGES 2 +static struct memory_range crash_reserved_ranges[CRASH_MAX_RESERVED_RANGES]; +static struct memory_ranges crash_reserved_rgns = { + .max_size = CRASH_MAX_RESERVED_RANGES, + .ranges = crash_reserved_ranges, +}; + +struct memory_range elfcorehdr_mem; + +static struct crash_elf_info elf_info = { + .class = ELFCLASS32, + .data = ELFDATANATIVE, + .machine = EM_ARM, + .page_offset = DEFAULT_PAGE_OFFSET, +}; + +extern unsigned long long user_page_offset; + +static int get_kernel_page_offset(struct kexec_info *info, + struct crash_elf_info *elf_info) +{ + unsigned long long stext_sym_addr = get_kernel_sym("_stext"); + if (stext_sym_addr == 0) { + if (user_page_offset != (-1ULL)) { + elf_info->page_offset = user_page_offset; + dbgprintf("Unable to get _stext symbol from /proc/kallsyms, " + "use user provided vaule: %llx\n", + elf_info->page_offset); + return 0; + } + elf_info->page_offset = (unsigned long long)DEFAULT_PAGE_OFFSET; + dbgprintf("Unable to get _stext symbol from /proc/kallsyms, " + "use default: %llx\n", + elf_info->page_offset); + return 0; + } else if ((user_page_offset != (-1ULL)) && + (user_page_offset != stext_sym_addr)) { + fprintf(stderr, "PAGE_OFFSET is set to %llx " + "instead of user provided value %llx\n", + stext_sym_addr & (~KVBASE_MASK), + user_page_offset); + } + elf_info->page_offset = stext_sym_addr & (~KVBASE_MASK); + return 0; +} + +/** + * crash_get_memory_ranges() - read system physical memory + * + * Function reads through system physical memory and stores found memory regions + * in @crash_memory_ranges. Number of memory regions found is placed in + * @crash_memory_nr_ranges. Regions are sorted in ascending order. + * + * Returns %0 in case of success and %-1 otherwise (errno is set). + */ +static int crash_get_memory_ranges(void) +{ + int i; + + if (usablemem_rgns.size < 1) { + errno = EINVAL; + return -1; + } + + dbgprint_mem_range("Reserved memory ranges", + crash_reserved_rgns.ranges, + crash_reserved_rgns.size); + + /* + * Exclude all reserved memory from the usable memory regions. + * We want to avoid dumping the crashkernel region itself. Note + * that this may result memory regions in usablemem_rgns being + * split. + */ + for (i = 0; i < crash_reserved_rgns.size; i++) { + if (mem_regions_exclude(&usablemem_rgns, + &crash_reserved_rgns.ranges[i])) { + fprintf(stderr, + "Error: Number of crash memory ranges excedeed the max limit\n"); + errno = ENOMEM; + return -1; + } + } + + /* + * Make sure that the memory regions are sorted. + */ + mem_regions_sort(&usablemem_rgns); + + dbgprint_mem_range("Coredump memory ranges", + usablemem_rgns.ranges, usablemem_rgns.size); + + return 0; +} + +/** + * cmdline_add_elfcorehdr() - adds elfcorehdr= to @cmdline + * @cmdline: buffer where parameter is placed + * @elfcorehdr: physical address of elfcorehdr + * + * Function appends 'elfcorehdr=start' at the end of the command line given in + * @cmdline. Note that @cmdline must be at least %COMMAND_LINE_SIZE bytes long + * (inclunding %NUL). + */ +static void cmdline_add_elfcorehdr(char *cmdline, unsigned long elfcorehdr) +{ + char buf[COMMAND_LINE_SIZE]; + int buflen; + + buflen = snprintf(buf, sizeof(buf), "%s elfcorehdr=%#lx", + cmdline, elfcorehdr); + if (buflen < 0) + die("Failed to construct elfcorehdr= command line parameter\n"); + if (buflen >= sizeof(buf)) + die("Command line overflow\n"); + + (void) strncpy(cmdline, buf, COMMAND_LINE_SIZE); + cmdline[COMMAND_LINE_SIZE - 1] = '\0'; +} + +/** + * cmdline_add_mem() - adds mem= parameter to kernel command line + * @cmdline: buffer where parameter is placed + * @size: size of the kernel reserved memory (in bytes) + * + * This function appends 'mem=size' at the end of the command line given in + * @cmdline. Note that @cmdline must be at least %COMMAND_LINE_SIZE bytes long + * (including %NUL). + */ +static void cmdline_add_mem(char *cmdline, unsigned long size) +{ + char buf[COMMAND_LINE_SIZE]; + int buflen; + + buflen = snprintf(buf, sizeof(buf), "%s mem=%ldK", cmdline, size >> 10); + if (buflen < 0) + die("Failed to construct mem= command line parameter\n"); + if (buflen >= sizeof(buf)) + die("Command line overflow\n"); + + (void) strncpy(cmdline, buf, COMMAND_LINE_SIZE); + cmdline[COMMAND_LINE_SIZE - 1] = '\0'; +} + +static unsigned long long range_size(const struct memory_range *r) +{ + return r->end - r->start + 1; +} + +static void dump_memory_ranges(void) +{ + int i; + + if (!kexec_debug) + return; + + dbgprintf("crashkernel: [%#llx - %#llx] (%ldM)\n", + crash_kernel_mem.start, crash_kernel_mem.end, + (unsigned long)range_size(&crash_kernel_mem) >> 20); + + for (i = 0; i < usablemem_rgns.size; i++) { + struct memory_range *r = usablemem_rgns.ranges + i; + dbgprintf("memory range: [%#llx - %#llx] (%ldM)\n", + r->start, r->end, (unsigned long)range_size(r) >> 20); + } +} + +/** + * load_crashdump_segments() - loads additional segments needed for kdump + * @info: kexec info structure + * @mod_cmdline: kernel command line + * + * This function loads additional segments which are needed for the dump capture + * kernel. It also updates kernel command line passed in @mod_cmdline to have + * right parameters for the dump capture kernel. + * + * Return %0 in case of success and %-1 in case of error. + */ +int load_crashdump_segments(struct kexec_info *info, char *mod_cmdline) +{ + unsigned long elfcorehdr; + unsigned long bufsz; + void *buf; + int err; + int last_ranges; + + /* + * First fetch all the memory (RAM) ranges that we are going to pass to + * the crashdump kernel during panic. + */ + err = crash_get_memory_ranges(); + if (err) + return err; + + /* + * Now that we have memory regions sorted, we can use first memory + * region as PHYS_OFFSET. + */ + phys_offset = usablemem_rgns.ranges->start; + + if (get_kernel_page_offset(info, &elf_info)) + return -1; + + dbgprintf("phys offset = %#llx, page offset = %llx\n", + phys_offset, elf_info.page_offset); + + /* + * Ensure that the crash kernel memory range is sane. The crash kernel + * must be located within memory which is visible during booting. + */ + if (crash_kernel_mem.end > ARM_MAX_VIRTUAL) { + fprintf(stderr, + "Crash kernel memory [0x%llx-0x%llx] is inaccessible at boot - unable to load crash kernel\n", + crash_kernel_mem.start, crash_kernel_mem.end); + return -1; + } + + last_ranges = usablemem_rgns.size - 1; + if (last_ranges < 0) + last_ranges = 0; + + if (crash_memory_ranges[last_ranges].end > UINT32_MAX) { + dbgprintf("Using 64-bit ELF core format\n"); + + /* for support LPAE enabled kernel*/ + elf_info.class = ELFCLASS64; + + err = crash_create_elf64_headers(info, &elf_info, + usablemem_rgns.ranges, + usablemem_rgns.size, &buf, &bufsz, + ELF_CORE_HEADER_ALIGN); + } else { + dbgprintf("Using 32-bit ELF core format\n"); + err = crash_create_elf32_headers(info, &elf_info, + usablemem_rgns.ranges, + usablemem_rgns.size, &buf, &bufsz, + ELF_CORE_HEADER_ALIGN); + } + if (err) + return err; + + /* + * We allocate ELF core header from the end of the memory area reserved + * for the crashkernel. We align the header to SECTION_SIZE (which is + * 1MB) so that available memory passed in kernel command line will be + * aligned to 1MB. This is because kernel create_mapping() wants memory + * regions to be aligned to SECTION_SIZE. + */ + elfcorehdr = add_buffer_phys_virt(info, buf, bufsz, bufsz, 1 << 20, + crash_kernel_mem.start, + crash_kernel_mem.end, -1, 0); + + elfcorehdr_mem.start = elfcorehdr; + elfcorehdr_mem.end = elfcorehdr + bufsz - 1; + + dbgprintf("elfcorehdr 0x%llx-0x%llx\n", elfcorehdr_mem.start, + elfcorehdr_mem.end); + cmdline_add_elfcorehdr(mod_cmdline, elfcorehdr); + + /* + * Add 'mem=size' parameter to dump capture kernel command line. This + * prevents the dump capture kernel from using any other memory regions + * which belong to the primary kernel. + */ + cmdline_add_mem(mod_cmdline, elfcorehdr - crash_kernel_mem.start); + + dump_memory_ranges(); + dbgprintf("kernel command line: \"%s\"\n", mod_cmdline); + + return 0; +} + +/** + * iomem_range_callback() - callback called for each iomem region + * @data: not used + * @nr: not used + * @str: name of the memory region (not NULL terminated) + * @base: start address of the memory region + * @length: size of the memory region + * + * This function is called for each memory range in /proc/iomem, stores + * the location of the crash kernel range into @crash_kernel_mem, and + * stores the system RAM into @usablemem_rgns. + */ +static int iomem_range_callback(void *UNUSED(data), int UNUSED(nr), + char *str, unsigned long long base, + unsigned long long length) +{ + if (strncmp(str, CRASH_KERNEL_BOOT, strlen(CRASH_KERNEL_BOOT)) == 0) { + crash_kernel_mem.start = base; + crash_kernel_mem.end = base + length - 1; + crash_kernel_mem.type = RANGE_RAM; + return mem_regions_add(&crash_reserved_rgns, + base, length, RANGE_RAM); + } + else if (strncmp(str, CRASH_KERNEL, strlen(CRASH_KERNEL)) == 0) { + if (crash_kernel_mem.start == crash_kernel_mem.end) { + crash_kernel_mem.start = base; + crash_kernel_mem.end = base + length - 1; + crash_kernel_mem.type = RANGE_RAM; + } + return mem_regions_add(&crash_reserved_rgns, + base, length, RANGE_RAM); + } + else if (strncmp(str, SYSTEM_RAM, strlen(SYSTEM_RAM)) == 0) { + return mem_regions_add(&usablemem_rgns, + base, length, RANGE_RAM); + } + return 0; +} + +/** + * is_crashkernel_mem_reserved() - check for the crashkernel reserved region + * + * Check for the crashkernel reserved region in /proc/iomem, and return + * true if it is present, or false otherwise. We use this to store the + * location of this region, and system RAM regions. + */ +int is_crashkernel_mem_reserved(void) +{ + kexec_iomem_for_each_line(NULL, iomem_range_callback, NULL); + + return crash_kernel_mem.start != crash_kernel_mem.end; +} + +int get_crash_kernel_load_range(uint64_t *start, uint64_t *end) +{ + return parse_iomem_single("Crash kernel\n", start, end); +} diff --git a/kexec/arch/arm/crashdump-arm.h b/kexec/arch/arm/crashdump-arm.h new file mode 100644 index 0000000..bbdf8bf --- /dev/null +++ b/kexec/arch/arm/crashdump-arm.h @@ -0,0 +1,27 @@ +#ifndef CRASHDUMP_ARM_H +#define CRASHDUMP_ARM_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define COMMAND_LINE_SIZE 1024 +#define DEFAULT_PAGE_OFFSET (0xc0000000) +#define KVBASE_MASK (0x1ffffff) +#define CRASH_MAX_MEMORY_RANGES 32 +#define ARM_MAX_VIRTUAL UINT32_MAX + + +extern struct memory_ranges usablemem_rgns; +extern struct memory_range crash_kernel_mem; +extern struct memory_range elfcorehdr_mem; + +struct kexec_info; + +extern int load_crashdump_segments(struct kexec_info *, char *); + +#ifdef __cplusplus +} +#endif + +#endif /* CRASHDUMP_ARM_H */ diff --git a/kexec/arch/arm/include/arch/options.h b/kexec/arch/arm/include/arch/options.h new file mode 100644 index 0000000..6fabfb7 --- /dev/null +++ b/kexec/arch/arm/include/arch/options.h @@ -0,0 +1,52 @@ +#ifndef KEXEC_ARCH_ARM_OPTIONS_H +#define KEXEC_ARCH_ARM_OPTIONS_H + +#define OPT_DT_NO_OLD_ROOT (OPT_MAX+0) +#define OPT_ARCH_MAX (OPT_MAX+1) + +#define OPT_DTB (OPT_ARCH_MAX+0) +#define OPT_ATAGS (OPT_ARCH_MAX+1) +#define OPT_IMAGE_SIZE (OPT_ARCH_MAX+2) +#define OPT_PAGE_OFFSET (OPT_ARCH_MAX+3) +#define OPT_APPEND (OPT_ARCH_MAX+4) +#define OPT_RAMDISK (OPT_ARCH_MAX+5) + +/* Options relevant to the architecture (excluding loader-specific ones), + * in this case none: + */ +#define KEXEC_ARCH_OPTIONS \ + KEXEC_OPTIONS \ + { "dt-no-old-root", 0, 0, OPT_DT_NO_OLD_ROOT }, \ + +#define KEXEC_ARCH_OPT_STR KEXEC_OPT_STR "" + +/* The following two #defines list ALL of the options added by all of the + * architecture's loaders. + * o main() uses this complete list to scan for its options, ignoring + * arch-specific/loader-specific ones. + * o Then, arch_process_options() uses this complete list to scan for its + * options, ignoring general/loader-specific ones. + * o Then, the file_type[n].load re-scans for options, using + * KEXEC_ARCH_OPTIONS plus its loader-specific options subset. + * Any unrecognised options cause an error here. + * + * This is done so that main()'s/arch_process_options()'s getopt_long() calls + * don't choose a kernel filename from random arguments to options they don't + * recognise -- as they now recognise (if not act upon) all possible options. + */ +#define KEXEC_ALL_OPTIONS \ + KEXEC_ARCH_OPTIONS \ + { "command-line", 1, 0, OPT_APPEND }, \ + { "append", 1, 0, OPT_APPEND }, \ + { "initrd", 1, 0, OPT_RAMDISK }, \ + { "ramdisk", 1, 0, OPT_RAMDISK }, \ + { "dtb", 1, 0, OPT_DTB }, \ + { "atags", 0, 0, OPT_ATAGS }, \ + { "image-size", 1, 0, OPT_IMAGE_SIZE }, \ + { "page-offset", 1, 0, OPT_PAGE_OFFSET }, + +#define KEXEC_ALL_OPT_STR KEXEC_ARCH_OPT_STR "" + +extern unsigned int kexec_arm_image_size; + +#endif /* KEXEC_ARCH_ARM_OPTIONS_H */ diff --git a/kexec/arch/arm/iomem.h b/kexec/arch/arm/iomem.h new file mode 100644 index 0000000..85f958e --- /dev/null +++ b/kexec/arch/arm/iomem.h @@ -0,0 +1,9 @@ +#ifndef IOMEM_H +#define IOMEM_H + +#define SYSTEM_RAM "System RAM\n" +#define SYSTEM_RAM_BOOT "System RAM (boot alias)\n" +#define CRASH_KERNEL "Crash kernel\n" +#define CRASH_KERNEL_BOOT "Crash kernel (boot alias)\n" + +#endif diff --git a/kexec/arch/arm/kexec-arm.c b/kexec/arch/arm/kexec-arm.c new file mode 100644 index 0000000..49f35b1 --- /dev/null +++ b/kexec/arch/arm/kexec-arm.c @@ -0,0 +1,150 @@ +/* + * kexec: Linux boots Linux + * + * modified from kexec-ppc.c + * + */ + +#define _GNU_SOURCE +#include <stddef.h> +#include <stdio.h> +#include <errno.h> +#include <stdint.h> +#include <string.h> +#include <getopt.h> +#include <unistd.h> +#include "../../kexec.h" +#include "../../kexec-syscall.h" +#include "kexec-arm.h" +#include <arch/options.h> +#include "../../fs2dt.h" +#include "iomem.h" + +#define MAX_MEMORY_RANGES 64 +#define MAX_LINE 160 +static struct memory_range memory_range[MAX_MEMORY_RANGES]; + +/* Return a sorted list of available memory ranges. */ +int get_memory_ranges(struct memory_range **range, int *ranges, + unsigned long UNUSED(kexec_flags)) +{ + const char *iomem = proc_iomem(); + int memory_ranges = 0; + char line[MAX_LINE]; + FILE *fp; + fp = fopen(iomem, "r"); + if (!fp) { + fprintf(stderr, "Cannot open %s: %s\n", + iomem, strerror(errno)); + return -1; + } + + while(fgets(line, sizeof(line), fp) != 0) { + unsigned long long start, end; + char *str; + int type; + int consumed; + int count; + if (memory_ranges >= MAX_MEMORY_RANGES) + break; + count = sscanf(line, "%llx-%llx : %n", + &start, &end, &consumed); + if (count != 2) + continue; + str = line + consumed; + + if (memcmp(str, SYSTEM_RAM_BOOT, strlen(SYSTEM_RAM_BOOT)) == 0 || + memcmp(str, SYSTEM_RAM, strlen(SYSTEM_RAM)) == 0) { + type = RANGE_RAM; + } + else if (memcmp(str, "reserved\n", 9) == 0) { + type = RANGE_RESERVED; + } + else { + continue; + } + + memory_range[memory_ranges].start = start; + memory_range[memory_ranges].end = end; + memory_range[memory_ranges].type = type; + memory_ranges++; + } + fclose(fp); + *range = memory_range; + *ranges = memory_ranges; + + dbgprint_mem_range("MEMORY RANGES", *range, *ranges); + + return 0; +} + +/* Supported file types and callbacks */ +struct file_type file_type[] = { + /* uImage is probed before zImage because the latter also accepts + uncompressed images. */ + {"uImage", uImage_arm_probe, uImage_arm_load, zImage_arm_usage}, + {"zImage", zImage_arm_probe, zImage_arm_load, zImage_arm_usage}, +}; +int file_types = sizeof(file_type) / sizeof(file_type[0]); + +void arch_usage(void) +{ + printf(" --image-size=<size>\n" + " Specify the assumed total image size of\n" + " the kernel that is about to be loaded,\n" + " including the .bss section, as reported\n" + " by 'arm-linux-size vmlinux'. If not\n" + " specified, this value is implicitly set\n" + " to the compressed images size * 4.\n" + " --dt-no-old-root\n" + " do not reuse old kernel root= param.\n" + " while creating flatten device tree.\n"); +} + +int arch_process_options(int argc, char **argv) +{ + /* We look for all options so getopt_long doesn't start reordering + * argv[] before file_type[n].load() gets a look in. + */ + static const struct option options[] = { + KEXEC_ALL_OPTIONS + { 0, 0, NULL, 0 }, + }; + static const char short_options[] = KEXEC_ALL_OPT_STR; + int opt; + + opterr = 0; /* Don't complain about unrecognized options here */ + while((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) { + switch(opt) { + case OPT_DT_NO_OLD_ROOT: + dt_no_old_root = 1; + break; + default: + break; + } + } + /* Reset getopt for the next pass; called in other source modules */ + opterr = 1; + optind = 1; + return 0; +} + +const struct arch_map_entry arches[] = { + { "arm", KEXEC_ARCH_ARM }, + { NULL, 0 }, +}; + +int arch_compat_trampoline(struct kexec_info *UNUSED(info)) +{ + return 0; +} + +void arch_update_purgatory(struct kexec_info *UNUSED(info)) +{ +} + +/* return 1 if /sys/firmware/fdt exists, otherwise return 0 */ +int have_sysfs_fdt(void) +{ + return !access(SYSFS_FDT, F_OK); +} diff --git a/kexec/arch/arm/kexec-arm.h b/kexec/arch/arm/kexec-arm.h new file mode 100644 index 0000000..a74cce2 --- /dev/null +++ b/kexec/arch/arm/kexec-arm.h @@ -0,0 +1,22 @@ +#ifndef KEXEC_ARM_H +#define KEXEC_ARM_H + +#include <sys/types.h> + +#define SYSFS_FDT "/sys/firmware/fdt" +#define BOOT_BLOCK_VERSION 17 +#define BOOT_BLOCK_LAST_COMP_VERSION 16 + +extern off_t initrd_base, initrd_size; + +int zImage_arm_probe(const char *buf, off_t len); +int zImage_arm_load(int argc, char **argv, const char *buf, off_t len, + struct kexec_info *info); +void zImage_arm_usage(void); + +int uImage_arm_probe(const char *buf, off_t len); +int uImage_arm_load(int argc, char **argv, const char *buf, off_t len, + struct kexec_info *info); +extern int have_sysfs_fdt(void); + +#endif /* KEXEC_ARM_H */ diff --git a/kexec/arch/arm/kexec-elf-rel-arm.c b/kexec/arch/arm/kexec-elf-rel-arm.c new file mode 100644 index 0000000..a939cf4 --- /dev/null +++ b/kexec/arch/arm/kexec-elf-rel-arm.c @@ -0,0 +1,36 @@ +#include <stdio.h> +#include <elf.h> +#include "../../kexec.h" +#include "../../kexec-elf.h" + +int machine_verify_elf_rel(struct mem_ehdr *ehdr) +{ + if (ehdr->ei_data != ELFDATA2MSB) { + return 0; + } + if (ehdr->ei_class != ELFCLASS32) { + return 0; + } + if (ehdr->e_machine != EM_ARM) + { + return 0; + } + return 1; +} + +void machine_apply_elf_rel(struct mem_ehdr *UNUSED(ehdr), + struct mem_sym *UNUSED(sym), unsigned long r_type, void *location, + unsigned long address, unsigned long value) +{ + switch(r_type) { + case R_ARM_ABS32: + *((uint32_t *)location) += value; + break; + case R_ARM_REL32: + *((uint32_t *)location) += value - address; + break; + default: + die("Unknown rel relocation: %lu\n", r_type); + break; + } +} diff --git a/kexec/arch/arm/kexec-uImage-arm.c b/kexec/arch/arm/kexec-uImage-arm.c new file mode 100644 index 0000000..03c2f4d --- /dev/null +++ b/kexec/arch/arm/kexec-uImage-arm.c @@ -0,0 +1,22 @@ +/* + * uImage support added by Marc Andre Tanner <mat@brain-dump.org> + */ +#include <stdint.h> +#include <string.h> +#include <sys/types.h> +#include <image.h> +#include <kexec-uImage.h> +#include "../../kexec.h" +#include "kexec-arm.h" + +int uImage_arm_probe(const char *buf, off_t len) +{ + return uImage_probe_kernel(buf, len, IH_ARCH_ARM); +} + +int uImage_arm_load(int argc, char **argv, const char *buf, off_t len, + struct kexec_info *info) +{ + return zImage_arm_load(argc, argv, buf + sizeof(struct image_header), + len - sizeof(struct image_header), info); +} diff --git a/kexec/arch/arm/kexec-zImage-arm.c b/kexec/arch/arm/kexec-zImage-arm.c new file mode 100644 index 0000000..8b474dd --- /dev/null +++ b/kexec/arch/arm/kexec-zImage-arm.c @@ -0,0 +1,914 @@ +/* + * - 08/21/2007 ATAG support added by Uli Luckas <u.luckas@road.de> + * + */ +#define _GNU_SOURCE +#define _XOPEN_SOURCE +#include <stdbool.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <limits.h> +#include <stdint.h> +#include <unistd.h> +#include <getopt.h> +#include <unistd.h> +#include <libfdt.h> +#include <arch/options.h> +#include "../../kexec.h" +#include "../../kexec-syscall.h" +#include "kexec-arm.h" +#include "../../fs2dt.h" +#include "crashdump-arm.h" +#include "iomem.h" + +#define BOOT_PARAMS_SIZE 1536 + +off_t initrd_base, initrd_size; +unsigned int kexec_arm_image_size = 0; +unsigned long long user_page_offset = (-1ULL); + +struct zimage_header { + uint32_t instr[9]; + uint32_t magic; +#define ZIMAGE_MAGIC cpu_to_le32(0x016f2818) + uint32_t start; + uint32_t end; + uint32_t endian; + + /* Extension to the data passed to the boot agent. The offset + * points at a tagged table following a similar format to the + * ATAGs. + */ + uint32_t magic2; +#define ZIMAGE_MAGIC2 (0x45454545) + uint32_t extension_tag_offset; +}; + +struct android_image { + char magic[8]; + uint32_t kernel_size; + uint32_t kernel_addr; + uint32_t ramdisk_size; + uint32_t ramdisk_addr; + uint32_t stage2_size; + uint32_t stage2_addr; + uint32_t tags_addr; + uint32_t page_size; + uint32_t reserved1; + uint32_t reserved2; + char name[16]; + char command_line[512]; + uint32_t chksum[8]; +}; + +struct tag_header { + uint32_t size; + uint32_t tag; +}; + +/* The list must start with an ATAG_CORE node */ +#define ATAG_CORE 0x54410001 + +struct tag_core { + uint32_t flags; /* bit 0 = read-only */ + uint32_t pagesize; + uint32_t rootdev; +}; + +/* it is allowed to have multiple ATAG_MEM nodes */ +#define ATAG_MEM 0x54410002 + +struct tag_mem32 { + uint32_t size; + uint32_t start; /* physical start address */ +}; + +/* describes where the compressed ramdisk image lives (virtual address) */ +/* + * this one accidentally used virtual addresses - as such, + * it's deprecated. + */ +#define ATAG_INITRD 0x54410005 + +/* describes where the compressed ramdisk image lives (physical address) */ +#define ATAG_INITRD2 0x54420005 + +struct tag_initrd { + uint32_t start; /* physical start address */ + uint32_t size; /* size of compressed ramdisk image in bytes */ +}; + +/* command line: \0 terminated string */ +#define ATAG_CMDLINE 0x54410009 + +struct tag_cmdline { + char cmdline[1]; /* this is the minimum size */ +}; + +/* The list ends with an ATAG_NONE node. */ +#define ATAG_NONE 0x00000000 + +struct tag { + struct tag_header hdr; + union { + struct tag_core core; + struct tag_mem32 mem; + struct tag_initrd initrd; + struct tag_cmdline cmdline; + } u; +}; + +#define tag_next(t) ((struct tag *)((uint32_t *)(t) + (t)->hdr.size)) +#define byte_size(t) ((t)->hdr.size << 2) +#define tag_size(type) ((sizeof(struct tag_header) + sizeof(struct type) + 3) >> 2) + +struct zimage_tag { + struct tag_header hdr; + union { +#define ZIMAGE_TAG_KRNL_SIZE cpu_to_le32(0x5a534c4b) + struct zimage_krnl_size { + uint32_t size_ptr; + uint32_t bss_size; + } krnl_size; + } u; +}; + +int zImage_arm_probe(const char *UNUSED(buf), off_t UNUSED(len)) +{ + /* + * Only zImage loading is supported. Do not check if + * the buffer is valid kernel image + */ + return 0; +} + +void zImage_arm_usage(void) +{ + printf( " --command-line=STRING Set the kernel command line to STRING.\n" + " --append=STRING Set the kernel command line to STRING.\n" + " --initrd=FILE Use FILE as the kernel's initial ramdisk.\n" + " --ramdisk=FILE Use FILE as the kernel's initial ramdisk.\n" + " --dtb=FILE Use FILE as the fdt blob.\n" + " --atags Use ATAGs instead of device-tree.\n" + " --page-offset=PAGE_OFFSET\n" + " Set PAGE_OFFSET of crash dump vmcore\n" + ); +} + +static +struct tag * atag_read_tags(void) +{ + static unsigned long buf[BOOT_PARAMS_SIZE]; + const char fn[]= "/proc/atags"; + FILE *fp; + fp = fopen(fn, "r"); + if (!fp) { + fprintf(stderr, "Cannot open %s: %s\n", + fn, strerror(errno)); + return NULL; + } + + if (!fread(buf, sizeof(buf[1]), BOOT_PARAMS_SIZE, fp)) { + fclose(fp); + return NULL; + } + + if (ferror(fp)) { + fprintf(stderr, "Cannot read %s: %s\n", + fn, strerror(errno)); + fclose(fp); + return NULL; + } + + fclose(fp); + return (struct tag *) buf; +} + +static +int create_mem32_tag(struct tag_mem32 *tag_mem32) +{ + const char fn[]= "/proc/device-tree/memory/reg"; + uint32_t tmp[2]; + FILE *fp; + + fp = fopen(fn, "r"); + if (!fp) { + fprintf(stderr, "Cannot open %s: %m\n", fn); + return -1; + } + + if (fread(tmp, sizeof(tmp[0]), 2, fp) != 2) { + fprintf(stderr, "Short read %s\n", fn); + fclose(fp); + return -1; + } + + if (ferror(fp)) { + fprintf(stderr, "Cannot read %s: %m\n", fn); + fclose(fp); + return -1; + } + + /* atags_mem32 has base/size fields reversed! */ + tag_mem32->size = be32_to_cpu(tmp[1]); + tag_mem32->start = be32_to_cpu(tmp[0]); + + fclose(fp); + return 0; +} + +static +int atag_arm_load(struct kexec_info *info, unsigned long base, + const char *command_line, off_t command_line_len, const char *initrd) +{ + struct tag *saved_tags = atag_read_tags(); + char *buf; + off_t len; + struct tag *params; + + buf = xmalloc(getpagesize()); + memset(buf, 0xff, getpagesize()); + params = (struct tag *)buf; + + if (saved_tags) { + // Copy tags + saved_tags = (struct tag *) saved_tags; + while(byte_size(saved_tags)) { + switch (saved_tags->hdr.tag) { + case ATAG_INITRD: + case ATAG_INITRD2: + case ATAG_CMDLINE: + case ATAG_NONE: + // skip these tags + break; + default: + // copy all other tags + memcpy(params, saved_tags, byte_size(saved_tags)); + params = tag_next(params); + } + saved_tags = tag_next(saved_tags); + } + } else { + params->hdr.size = 2; + params->hdr.tag = ATAG_CORE; + params = tag_next(params); + + if (!create_mem32_tag(¶ms->u.mem)) { + params->hdr.size = 4; + params->hdr.tag = ATAG_MEM; + params = tag_next(params); + } + } + + if (initrd) { + params->hdr.size = tag_size(tag_initrd); + params->hdr.tag = ATAG_INITRD2; + params->u.initrd.start = initrd_base; + params->u.initrd.size = initrd_size; + params = tag_next(params); + } + + if (command_line) { + params->hdr.size = (sizeof(struct tag_header) + command_line_len + 3) >> 2; + params->hdr.tag = ATAG_CMDLINE; + memcpy(params->u.cmdline.cmdline, command_line, + command_line_len); + params->u.cmdline.cmdline[command_line_len - 1] = '\0'; + params = tag_next(params); + } + + params->hdr.size = 0; + params->hdr.tag = ATAG_NONE; + + len = ((char *)params - buf) + sizeof(struct tag_header); + + add_segment(info, buf, len, base, len); + + return 0; +} + +static int setup_dtb_prop(char **bufp, off_t *sizep, int parentoffset, + const char *node_name, const char *prop_name, + const void *val, int len) +{ + char *dtb_buf; + off_t dtb_size; + int off; + int prop_len = 0; + const struct fdt_property *prop; + + if ((bufp == NULL) || (sizep == NULL) || (*bufp == NULL)) + die("Internal error\n"); + + dtb_buf = *bufp; + dtb_size = *sizep; + + /* check if the subnode has already exist */ + off = fdt_subnode_offset(dtb_buf, parentoffset, node_name); + if (off == -FDT_ERR_NOTFOUND) { + dtb_size += fdt_node_len(node_name); + fdt_set_totalsize(dtb_buf, dtb_size); + dtb_buf = xrealloc(dtb_buf, dtb_size); + off = fdt_add_subnode(dtb_buf, parentoffset, node_name); + } + + if (off < 0) { + fprintf(stderr, "FDT: Error adding %s node.\n", node_name); + return -1; + } + + prop = fdt_get_property(dtb_buf, off, prop_name, &prop_len); + if ((prop == NULL) && (prop_len != -FDT_ERR_NOTFOUND)) { + die("FDT: fdt_get_property"); + } else if (prop == NULL) { + /* prop_len == -FDT_ERR_NOTFOUND */ + /* prop doesn't exist */ + dtb_size += fdt_prop_len(prop_name, len); + } else { + if (prop_len < len) + dtb_size += FDT_TAGALIGN(len - prop_len); + } + + if (fdt_totalsize(dtb_buf) < dtb_size) { + fdt_set_totalsize(dtb_buf, dtb_size); + dtb_buf = xrealloc(dtb_buf, dtb_size); + } + + if (fdt_setprop(dtb_buf, off, prop_name, + val, len) != 0) { + fprintf(stderr, "FDT: Error setting %s/%s property.\n", + node_name, prop_name); + return -1; + } + *bufp = dtb_buf; + *sizep = dtb_size; + return 0; +} + +static const struct zimage_tag *find_extension_tag(const char *buf, off_t len, + uint32_t tag_id) +{ + const struct zimage_header *hdr = (const struct zimage_header *)buf; + const struct zimage_tag *tag; + uint32_t offset, size; + uint32_t max = len - sizeof(struct tag_header); + + if (len < sizeof(*hdr) || + hdr->magic != ZIMAGE_MAGIC || + hdr->magic2 != ZIMAGE_MAGIC2) + return NULL; + + for (offset = hdr->extension_tag_offset; + (tag = (void *)(buf + offset)) != NULL && + offset < max && + (size = le32_to_cpu(byte_size(tag))) != 0 && + offset + size < len; + offset += size) { + dbgprintf(" offset 0x%08x tag 0x%08x size %u\n", + offset, le32_to_cpu(tag->hdr.tag), size); + if (tag->hdr.tag == tag_id) + return tag; + } + + return NULL; +} + +static int get_cells_size(void *fdt, uint32_t *address_cells, + uint32_t *size_cells) +{ + int nodeoffset; + const uint32_t *prop = NULL; + int prop_len; + + /* default values */ + *address_cells = 1; + *size_cells = 1; + + /* under root node */ + nodeoffset = fdt_path_offset(fdt, "/"); + if (nodeoffset < 0) + return -1; + + prop = fdt_getprop(fdt, nodeoffset, "#address-cells", &prop_len); + if (prop) { + if (prop_len != sizeof(*prop)) + return -1; + + *address_cells = fdt32_to_cpu(*prop); + } + + prop = fdt_getprop(fdt, nodeoffset, "#size-cells", &prop_len); + if (prop) { + if (prop_len != sizeof(*prop)) + return -1; + + *size_cells = fdt32_to_cpu(*prop); + } + + dbgprintf("%s: #address-cells:%d #size-cells:%d\n", __func__, + *address_cells, *size_cells); + return 0; +} + +static bool cells_size_fitted(uint32_t address_cells, uint32_t size_cells, + struct memory_range *range) +{ + dbgprintf("%s: %llx-%llx\n", __func__, range->start, range->end); + + /* if *_cells >= 2, cells can hold 64-bit values anyway */ + if ((address_cells == 1) && (range->start >= (1ULL << 32))) + return false; + + if ((size_cells == 1) && + ((range->end - range->start + 1) >= (1ULL << 32))) + return false; + + return true; +} + +static void fill_property(void *buf, uint64_t val, uint32_t cells) +{ + uint32_t val32; + int i; + + if (cells == 1) { + val32 = cpu_to_fdt32((uint32_t)val); + memcpy(buf, &val32, sizeof(uint32_t)); + } else { + for (i = 0; + i < (cells * sizeof(uint32_t) - sizeof(uint64_t)); i++) + *(char *)buf++ = 0; + + val = cpu_to_fdt64(val); + memcpy(buf, &val, sizeof(uint64_t)); + } +} + +static int setup_dtb_prop_range(char **bufp, off_t *sizep, int parentoffset, + const char *node_name, const char *prop_name, + struct memory_range *range, + uint32_t address_cells, uint32_t size_cells) +{ + void *buf, *prop; + size_t buf_size; + int result; + + buf_size = (address_cells + size_cells) * sizeof(uint32_t); + prop = buf = xmalloc(buf_size); + + fill_property(prop, range->start, address_cells); + prop += address_cells * sizeof(uint32_t); + + fill_property(prop, range->end - range->start + 1, size_cells); + prop += size_cells * sizeof(uint32_t); + + result = setup_dtb_prop(bufp, sizep, parentoffset, node_name, + prop_name, buf, buf_size); + + free(buf); + + return result; +} + +int zImage_arm_load(int argc, char **argv, const char *buf, off_t len, + struct kexec_info *info) +{ + unsigned long page_size = getpagesize(); + unsigned long base, kernel_base; + unsigned int atag_offset = 0x1000; /* 4k offset from memory start */ + unsigned int extra_size = 0x8000; /* TEXT_OFFSET */ + uint32_t address_cells, size_cells; + const struct zimage_tag *tag; + size_t kernel_buf_size; + size_t kernel_mem_size; + const char *command_line; + char *modified_cmdline = NULL; + off_t command_line_len; + const char *ramdisk; + const char *ramdisk_buf; + int opt; + int use_atags; + int result; + char *dtb_buf; + off_t dtb_length; + char *dtb_file; + off_t dtb_offset; + char *end; + + /* See options.h -- add any more there, too. */ + static const struct option options[] = { + KEXEC_ARCH_OPTIONS + { "command-line", 1, 0, OPT_APPEND }, + { "append", 1, 0, OPT_APPEND }, + { "initrd", 1, 0, OPT_RAMDISK }, + { "ramdisk", 1, 0, OPT_RAMDISK }, + { "dtb", 1, 0, OPT_DTB }, + { "atags", 0, 0, OPT_ATAGS }, + { "image-size", 1, 0, OPT_IMAGE_SIZE }, + { "page-offset", 1, 0, OPT_PAGE_OFFSET }, + { 0, 0, 0, 0 }, + }; + static const char short_options[] = KEXEC_ARCH_OPT_STR ""; + + /* + * Parse the command line arguments + */ + command_line = 0; + command_line_len = 0; + ramdisk = 0; + ramdisk_buf = 0; + initrd_size = 0; + use_atags = 0; + dtb_file = NULL; + while((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) { + switch(opt) { + default: + /* Ignore core options */ + if (opt < OPT_ARCH_MAX) { + break; + } + case OPT_APPEND: + command_line = optarg; + break; + case OPT_RAMDISK: + ramdisk = optarg; + break; + case OPT_DTB: + dtb_file = optarg; + break; + case OPT_ATAGS: + use_atags = 1; + break; + case OPT_IMAGE_SIZE: + kexec_arm_image_size = strtoul(optarg, &end, 0); + break; + case OPT_PAGE_OFFSET: + user_page_offset = strtoull(optarg, &end, 0); + break; + } + } + + if (use_atags && dtb_file) { + fprintf(stderr, "You can only use ATAGs if you don't specify a " + "dtb file.\n"); + return -1; + } + + if (!use_atags && !dtb_file) { + int f; + + f = have_sysfs_fdt(); + if (f) + dtb_file = SYSFS_FDT; + } + + if (command_line) { + command_line_len = strlen(command_line) + 1; + if (command_line_len > COMMAND_LINE_SIZE) + command_line_len = COMMAND_LINE_SIZE; + } + if (ramdisk) + ramdisk_buf = slurp_file_mmap(ramdisk, &initrd_size); + + if (dtb_file) + dtb_buf = slurp_file(dtb_file, &dtb_length); + + if (len > sizeof(struct zimage_header)) { + const struct zimage_header *hdr; + off_t size; + + hdr = (const struct zimage_header *)buf; + + dbgprintf("zImage header: 0x%08x 0x%08x 0x%08x\n", + hdr->magic, hdr->start, hdr->end); + + if (hdr->magic == ZIMAGE_MAGIC) { + size = le32_to_cpu(hdr->end) - le32_to_cpu(hdr->start); + + dbgprintf("zImage size 0x%llx, file size 0x%llx\n", + (unsigned long long)size, + (unsigned long long)len); + + if (size > len) { + fprintf(stderr, + "zImage is truncated - file 0x%llx vs header 0x%llx\n", + (unsigned long long)len, + (unsigned long long)size); + return -1; + } + if (size < len) + len = size; + } + } + + /* Handle android images, 2048 is the minimum page size */ + if (len > 2048 && !strncmp(buf, "ANDROID!", 8)) { + const struct android_image *aimg = (const void *)buf; + uint32_t page_size = le32_to_cpu(aimg->page_size); + uint32_t kernel_size = le32_to_cpu(aimg->kernel_size); + uint32_t ramdisk_size = le32_to_cpu(aimg->ramdisk_size); + uint32_t stage2_size = le32_to_cpu(aimg->stage2_size); + off_t aimg_size = page_size + _ALIGN(kernel_size, page_size) + + _ALIGN(ramdisk_size, page_size) + stage2_size; + + if (len < aimg_size) { + fprintf(stderr, "Android image size is incorrect\n"); + return -1; + } + + /* Get the kernel */ + buf = buf + page_size; + len = kernel_size; + + /* And the ramdisk if none was given on the command line */ + if (!ramdisk && ramdisk_size) { + initrd_size = ramdisk_size; + ramdisk_buf = buf + _ALIGN(kernel_size, page_size); + } + + /* Likewise for the command line */ + if (!command_line && aimg->command_line[0]) { + command_line = aimg->command_line; + if (command_line[sizeof(aimg->command_line) - 1]) + command_line_len = sizeof(aimg->command_line); + else + command_line_len = strlen(command_line) + 1; + } + } + + /* + * Save the length of the compressed kernel image w/o the appended DTB. + * This will be required later on when the kernel image contained + * in the zImage will be loaded into a kernel memory segment. + * And we want to load ONLY the compressed kernel image from the zImage + * and discard the appended DTB. + */ + kernel_buf_size = len; + + /* + * Always extend the zImage by four bytes to ensure that an appended + * DTB image always sees an initialised value after _edata. + */ + kernel_mem_size = len + 4; + + /* + * Check for a kernel size extension, and set or validate the + * image size. This is the total space needed to avoid the + * boot kernel BSS, so other data (such as initrd) does not get + * overwritten. + */ + tag = find_extension_tag(buf, len, ZIMAGE_TAG_KRNL_SIZE); + + /* + * The zImage length does not include its stack (4k) or its + * malloc space (64k). Include this. + */ + len += 0x11000; + + dbgprintf("zImage requires 0x%08llx bytes\n", (unsigned long long)len); + + if (tag) { + uint32_t *p = (void *)buf + le32_to_cpu(tag->u.krnl_size.size_ptr); + uint32_t edata_size = le32_to_cpu(get_unaligned(p)); + uint32_t bss_size = le32_to_cpu(tag->u.krnl_size.bss_size); + uint32_t kernel_size = edata_size + bss_size; + + dbgprintf("Decompressed kernel sizes:\n"); + dbgprintf(" text+data 0x%08lx bss 0x%08lx total 0x%08lx\n", + (unsigned long)edata_size, + (unsigned long)bss_size, + (unsigned long)kernel_size); + + /* + * While decompressing, the zImage is placed past _edata + * of the decompressed kernel. Ensure we account for that. + */ + if (kernel_size < edata_size + len) + kernel_size = edata_size + len; + + dbgprintf("Resulting kernel space: 0x%08lx\n", + (unsigned long)kernel_size); + + if (kexec_arm_image_size == 0) + kexec_arm_image_size = kernel_size; + else if (kexec_arm_image_size < kernel_size) { + fprintf(stderr, + "Kernel size is too small, increasing to 0x%lx\n", + (unsigned long)kernel_size); + kexec_arm_image_size = kernel_size; + } + } + + /* + * If the user didn't specify the size of the image, and we don't + * have the extension tables, assume the maximum kernel compression + * ratio is 4. Note that we must include space for the compressed + * image here as well. + */ + if (!kexec_arm_image_size) + kexec_arm_image_size = len * 5; + + /* + * If we are loading a dump capture kernel, we need to update kernel + * command line and also add some additional segments. + */ + if (info->kexec_flags & KEXEC_ON_CRASH) { + uint64_t start, end; + + modified_cmdline = xmalloc(COMMAND_LINE_SIZE); + memset(modified_cmdline, '\0', COMMAND_LINE_SIZE); + + if (command_line) { + (void) strncpy(modified_cmdline, command_line, + COMMAND_LINE_SIZE); + modified_cmdline[COMMAND_LINE_SIZE - 1] = '\0'; + } + + if (load_crashdump_segments(info, modified_cmdline) < 0) { + free(modified_cmdline); + return -1; + } + + command_line = modified_cmdline; + command_line_len = strlen(command_line) + 1; + + /* + * We put the dump capture kernel at the start of crashkernel + * reserved memory. + */ + if (parse_iomem_single(CRASH_KERNEL_BOOT, &start, &end) && + parse_iomem_single(CRASH_KERNEL, &start, &end)) { + /* + * No crash kernel memory reserved. We cannot do more + * but just bail out. + */ + return ENOCRASHKERNEL; + } + base = start; + } else { + base = locate_hole(info, len + extra_size, 0, 0, + ULONG_MAX, INT_MAX); + } + + if (base == ULONG_MAX) + return -1; + + kernel_base = base + extra_size; + + /* + * Calculate the minimum address of the initrd, which must be + * above the memory used by the zImage while it runs. This + * needs to be page-size aligned. + */ + initrd_base = kernel_base + _ALIGN(kexec_arm_image_size, page_size); + + dbgprintf("%-6s: address=0x%08lx size=0x%08lx\n", "Kernel", + (unsigned long)kernel_base, + (unsigned long)kexec_arm_image_size); + + if (ramdisk_buf) { + /* + * Find a hole to place the initrd. The crash kernel use + * fixed address, so no check is ok. + */ + if (!(info->kexec_flags & KEXEC_ON_CRASH)) { + initrd_base = locate_hole(info, initrd_size, page_size, + initrd_base, + ULONG_MAX, INT_MAX); + if (initrd_base == ULONG_MAX) + return -1; + } + + dbgprintf("%-6s: address=0x%08lx size=0x%08lx\n", "Initrd", + (unsigned long)initrd_base, + (unsigned long)initrd_size); + + add_segment(info, ramdisk_buf, initrd_size, initrd_base, + initrd_size); + } + + if (use_atags) { + /* + * use ATAGs from /proc/atags + */ + if (atag_arm_load(info, base + atag_offset, + command_line, command_line_len, + ramdisk_buf) == -1) + return -1; + } else { + /* + * Read a user-specified DTB file. + */ + if (dtb_file) { + if (fdt_check_header(dtb_buf) != 0) { + fprintf(stderr, "Invalid FDT buffer.\n"); + return -1; + } + + if (command_line) { + /* + * Error should have been reported so + * directly return -1 + */ + if (setup_dtb_prop(&dtb_buf, &dtb_length, 0, "chosen", + "bootargs", command_line, + strlen(command_line) + 1)) + return -1; + } + } else { + /* + * Extract the DTB from /proc/device-tree. + */ + create_flatten_tree(&dtb_buf, &dtb_length, command_line); + } + + /* + * Add the initrd parameters to the dtb + */ + if (ramdisk_buf) { + unsigned long start, end; + + start = cpu_to_be32((unsigned long)(initrd_base)); + end = cpu_to_be32((unsigned long)(initrd_base + initrd_size)); + + if (setup_dtb_prop(&dtb_buf, &dtb_length, 0, "chosen", + "linux,initrd-start", &start, + sizeof(start))) + return -1; + if (setup_dtb_prop(&dtb_buf, &dtb_length, 0, "chosen", + "linux,initrd-end", &end, + sizeof(end))) + return -1; + } + + if (info->kexec_flags & KEXEC_ON_CRASH) { + /* Determine #address-cells and #size-cells */ + result = get_cells_size(dtb_buf, &address_cells, + &size_cells); + if (result) { + fprintf(stderr, "Cannot determine cells-size.\n"); + return -1; + } + + if (!cells_size_fitted(address_cells, size_cells, + &elfcorehdr_mem)) { + fprintf(stderr, "elfcorehdr doesn't fit cells-size.\n"); + return -1; + } + + if (!cells_size_fitted(address_cells, size_cells, + &crash_kernel_mem)) { + fprintf(stderr, "kexec: usable memory range doesn't fit cells-size.\n"); + return -1; + } + + /* Add linux,elfcorehdr */ + if (setup_dtb_prop_range(&dtb_buf, &dtb_length, 0, + "chosen", "linux,elfcorehdr", + &elfcorehdr_mem, + address_cells, size_cells)) + return -1; + + /* Add linux,usable-memory-range */ + if (setup_dtb_prop_range(&dtb_buf, &dtb_length, 0, + "chosen", + "linux,usable-memory-range", + &crash_kernel_mem, + address_cells, size_cells)) + return -1; + } + + /* + * The dtb must also be placed above the memory used by + * the zImage. We don't care about its position wrt the + * ramdisk, but we might as well place it after the initrd. + * We leave a buffer page between the initrd and the dtb. + */ + dtb_offset = initrd_base + initrd_size + page_size; + dtb_offset = _ALIGN_DOWN(dtb_offset, page_size); + + /* + * Find a hole to place the dtb above the initrd. + * Crash kernel use fixed address, no check is ok. + */ + if (!(info->kexec_flags & KEXEC_ON_CRASH)) { + dtb_offset = locate_hole(info, dtb_length, page_size, + dtb_offset, ULONG_MAX, INT_MAX); + if (dtb_offset == ULONG_MAX) + return -1; + } + + dbgprintf("%-6s: address=0x%08lx size=0x%08lx\n", "DT", + (unsigned long)dtb_offset, (unsigned long)dtb_length); + + add_segment(info, dtb_buf, dtb_length, dtb_offset, dtb_length); + } + + add_segment(info, buf, kernel_buf_size, kernel_base, kernel_mem_size); + + info->entry = (void*)kernel_base; + + return 0; +} diff --git a/kexec/arch/arm/phys_to_virt.c b/kexec/arch/arm/phys_to_virt.c new file mode 100644 index 0000000..46a4f68 --- /dev/null +++ b/kexec/arch/arm/phys_to_virt.c @@ -0,0 +1,22 @@ +#include "../../kexec.h" +#include "../../crashdump.h" +#include "phys_to_virt.h" + +uint64_t phys_offset; + +/** + * phys_to_virt() - translate physical address to virtual address + * @paddr: physical address to translate + * + * For ARM we have following equation to translate from virtual address to + * physical: + * paddr = vaddr - PAGE_OFFSET + PHYS_OFFSET + * + * See also: + * http://lists.arm.linux.org.uk/lurker/message/20010723.185051.94ce743c.en.html + */ +unsigned long +phys_to_virt(struct crash_elf_info *elf_info, unsigned long long paddr) +{ + return paddr + elf_info->page_offset - phys_offset; +} diff --git a/kexec/arch/arm/phys_to_virt.h b/kexec/arch/arm/phys_to_virt.h new file mode 100644 index 0000000..b3147dd --- /dev/null +++ b/kexec/arch/arm/phys_to_virt.h @@ -0,0 +1,8 @@ +#ifndef PHYS_TO_VIRT_H +#define PHYS_TO_VIRT_H + +#include <stdint.h> + +extern uint64_t phys_offset; + +#endif diff --git a/kexec/arch/arm64/Makefile b/kexec/arch/arm64/Makefile new file mode 100644 index 0000000..59212f1 --- /dev/null +++ b/kexec/arch/arm64/Makefile @@ -0,0 +1,50 @@ + +arm64_FS2DT += kexec/fs2dt.c +arm64_FS2DT_INCLUDE += \ + -include $(srcdir)/kexec/arch/arm64/crashdump-arm64.h \ + -include $(srcdir)/kexec/arch/arm64/kexec-arm64.h + +arm64_DT_OPS += kexec/dt-ops.c + +arm64_MEM_REGIONS = kexec/mem_regions.c + +arm64_CPPFLAGS += -I $(srcdir)/kexec/ + +arm64_KEXEC_SRCS += \ + kexec/arch/arm64/crashdump-arm64.c \ + kexec/arch/arm64/kexec-arm64.c \ + kexec/arch/arm64/kexec-elf-arm64.c \ + kexec/arch/arm64/kexec-uImage-arm64.c \ + kexec/arch/arm64/kexec-image-arm64.c \ + kexec/arch/arm64/kexec-vmlinuz-arm64.c + +arm64_UIMAGE = kexec/kexec-uImage.c + +arm64_ARCH_REUSE_INITRD = +arm64_ADD_SEGMENT = +arm64_VIRT_TO_PHYS = +arm64_PHYS_TO_VIRT = + +dist += $(arm64_KEXEC_SRCS) \ + kexec/arch/arm64/include/arch/options.h \ + kexec/arch/arm64/crashdump-arm64.h \ + kexec/arch/arm64/image-header.h \ + kexec/arch/arm64/iomem.h \ + kexec/arch/arm64/kexec-arm64.h \ + kexec/arch/arm64/Makefile + +ifdef HAVE_LIBFDT + +LIBS += -lfdt + +else + +include $(srcdir)/kexec/libfdt/Makefile.libfdt + +libfdt_SRCS += $(LIBFDT_SRCS:%=kexec/libfdt/%) + +arm64_CPPFLAGS += -I$(srcdir)/kexec/libfdt + +arm64_KEXEC_SRCS += $(libfdt_SRCS) + +endif diff --git a/kexec/arch/arm64/crashdump-arm64.c b/kexec/arch/arm64/crashdump-arm64.c new file mode 100644 index 0000000..3098315 --- /dev/null +++ b/kexec/arch/arm64/crashdump-arm64.c @@ -0,0 +1,248 @@ +/* + * ARM64 crashdump. + * partly derived from arm implementation + * + * Copyright (c) 2014-2017 Linaro Limited + * Author: AKASHI Takahiro <takahiro.akashi@linaro.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#define _GNU_SOURCE + +#include <errno.h> +#include <linux/elf.h> + +#include "kexec.h" +#include "crashdump.h" +#include "crashdump-arm64.h" +#include "iomem.h" +#include "kexec-arm64.h" +#include "kexec-elf.h" +#include "mem_regions.h" + +/* memory ranges of crashed kernel */ +static struct memory_ranges system_memory_rgns; + +/* memory range reserved for crashkernel */ +struct memory_range crash_reserved_mem[CRASH_MAX_RESERVED_RANGES]; +struct memory_ranges usablemem_rgns = { + .size = 0, + .max_size = CRASH_MAX_RESERVED_RANGES, + .ranges = crash_reserved_mem, +}; + +struct memory_range elfcorehdr_mem; + +static struct crash_elf_info elf_info = { + .class = ELFCLASS64, +#if (__BYTE_ORDER == __LITTLE_ENDIAN) + .data = ELFDATA2LSB, +#else + .data = ELFDATA2MSB, +#endif + .machine = EM_AARCH64, +}; + +/* + * iomem_range_callback() - callback called for each iomem region + * @data: not used + * @nr: not used + * @str: name of the memory region + * @base: start address of the memory region + * @length: size of the memory region + * + * This function is called once for each memory region found in /proc/iomem. + * It locates system RAM and crashkernel reserved memory and places these to + * variables, respectively, system_memory_rgns and usablemem_rgns. + */ + +static int iomem_range_callback(void *UNUSED(data), int UNUSED(nr), + char *str, unsigned long long base, + unsigned long long length) +{ + if (strncmp(str, CRASH_KERNEL, strlen(CRASH_KERNEL)) == 0) + return mem_regions_alloc_and_add(&usablemem_rgns, + base, length, RANGE_RAM); + else if (strncmp(str, SYSTEM_RAM, strlen(SYSTEM_RAM)) == 0) + return mem_regions_alloc_and_add(&system_memory_rgns, + base, length, RANGE_RAM); + else if (strncmp(str, KERNEL_CODE, strlen(KERNEL_CODE)) == 0) { + + unsigned long long kva_text = get_kernel_sym("_text"); + unsigned long long kva_stext = get_kernel_sym("_stext"); + unsigned long long kva_text_end = get_kernel_sym("__init_begin"); + + /* + * old: kernel_code.start = __pa_symbol(_text); + * new: kernel_code.start = __pa_symbol(_stext); + * + * For compatibility, deduce by comparing the gap "__init_begin - _stext" + * and the res size of "Kernel code" in /proc/iomem + */ + if (kva_text_end - kva_stext == length) + elf_info.kern_paddr_start = base - (kva_stext - kva_text); + else + elf_info.kern_paddr_start = base; + } + else if (strncmp(str, KERNEL_DATA, strlen(KERNEL_DATA)) == 0) + elf_info.kern_size = base + length - elf_info.kern_paddr_start; + + return 0; +} + +int is_crashkernel_mem_reserved(void) +{ + if (!usablemem_rgns.size) + kexec_iomem_for_each_line(NULL, iomem_range_callback, NULL); + + return usablemem_rgns.size; +} + +/* + * crash_get_memory_ranges() - read system physical memory + * + * Function reads through system physical memory and stores found memory + * regions in system_memory_ranges. + * Regions are sorted in ascending order. + * + * Returns 0 in case of success and a negative value otherwise. + */ +static int crash_get_memory_ranges(void) +{ + int i; + + /* + * First read all memory regions that can be considered as + * system memory including the crash area. + */ + if (!usablemem_rgns.size) + kexec_iomem_for_each_line(NULL, iomem_range_callback, NULL); + + /* allow one or two regions for crash dump kernel */ + if (!usablemem_rgns.size) + return -EINVAL; + + dbgprint_mem_range("Reserved memory range", + usablemem_rgns.ranges, usablemem_rgns.size); + + for (i = 0; i < usablemem_rgns.size; i++) { + if (mem_regions_alloc_and_exclude(&system_memory_rgns, + &crash_reserved_mem[i])) { + fprintf(stderr, "Cannot allocate memory for ranges\n"); + return -ENOMEM; + } + } + + /* + * Make sure that the memory regions are sorted. + */ + mem_regions_sort(&system_memory_rgns); + + dbgprint_mem_range("Coredump memory ranges", + system_memory_rgns.ranges, system_memory_rgns.size); + + /* + * For additional kernel code/data segment. + * kern_paddr_start/kern_size are determined in iomem_range_callback + */ + elf_info.kern_vaddr_start = get_kernel_sym("_text"); + if (!elf_info.kern_vaddr_start) + elf_info.kern_vaddr_start = UINT64_MAX; + + return 0; +} + +/* + * load_crashdump_segments() - load the elf core header + * @info: kexec info structure + * + * This function creates and loads an additional segment of elf core header + : which is used to construct /proc/vmcore on crash dump kernel. + * + * Return 0 in case of success and -1 in case of error. + */ + +int load_crashdump_segments(struct kexec_info *info) +{ + unsigned long elfcorehdr; + unsigned long bufsz; + void *buf; + int err; + + /* + * First fetch all the memory (RAM) ranges that we are going to + * pass to the crash dump kernel during panic. + */ + + err = crash_get_memory_ranges(); + + if (err) + return EFAILED; + + get_page_offset((unsigned long *)&elf_info.page_offset); + dbgprintf("%s: page_offset: %016llx\n", __func__, + elf_info.page_offset); + + err = crash_create_elf64_headers(info, &elf_info, + system_memory_rgns.ranges, system_memory_rgns.size, + &buf, &bufsz, ELF_CORE_HEADER_ALIGN); + + if (err) + return EFAILED; + + elfcorehdr = add_buffer_phys_virt(info, buf, bufsz, bufsz, 0, + crash_reserved_mem[usablemem_rgns.size - 1].start, + crash_reserved_mem[usablemem_rgns.size - 1].end, + -1, 0); + + elfcorehdr_mem.start = elfcorehdr; + elfcorehdr_mem.end = elfcorehdr + bufsz - 1; + + dbgprintf("%s: elfcorehdr 0x%llx-0x%llx\n", __func__, + elfcorehdr_mem.start, elfcorehdr_mem.end); + + return 0; +} + +/* + * e_entry and p_paddr are actually in virtual address space. + * Those values will be translated to physcal addresses by using + * virt_to_phys() in add_segment(). + * So let's fix up those values for later use so the memory base + * (arm64_mm.phys_offset) will be correctly replaced with + * crash_reserved_mem[usablemem_rgns.size - 1].start. + */ +void fixup_elf_addrs(struct mem_ehdr *ehdr) +{ + struct mem_phdr *phdr; + int i; + + ehdr->e_entry += -arm64_mem.phys_offset + + crash_reserved_mem[usablemem_rgns.size - 1].start; + + for (i = 0; i < ehdr->e_phnum; i++) { + phdr = &ehdr->e_phdr[i]; + if (phdr->p_type != PT_LOAD) + continue; + phdr->p_paddr += + (-arm64_mem.phys_offset + + crash_reserved_mem[usablemem_rgns.size - 1].start); + } +} + +int get_crash_kernel_load_range(uint64_t *start, uint64_t *end) +{ + if (!usablemem_rgns.size) + kexec_iomem_for_each_line(NULL, iomem_range_callback, NULL); + + if (!usablemem_rgns.size) + return -1; + + *start = crash_reserved_mem[usablemem_rgns.size - 1].start; + *end = crash_reserved_mem[usablemem_rgns.size - 1].end; + + return 0; +} diff --git a/kexec/arch/arm64/crashdump-arm64.h b/kexec/arch/arm64/crashdump-arm64.h new file mode 100644 index 0000000..82fa69b --- /dev/null +++ b/kexec/arch/arm64/crashdump-arm64.h @@ -0,0 +1,29 @@ +/* + * ARM64 crashdump. + * + * Copyright (c) 2014-2017 Linaro Limited + * Author: AKASHI Takahiro <takahiro.akashi@linaro.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef CRASHDUMP_ARM64_H +#define CRASHDUMP_ARM64_H + +#include "kexec.h" + +#define CRASH_MAX_MEMORY_RANGES 32768 + +/* crash dump kernel support at most two regions, low_region and high region. */ +#define CRASH_MAX_RESERVED_RANGES 2 + +extern struct memory_ranges usablemem_rgns; +extern struct memory_range crash_reserved_mem[]; +extern struct memory_range elfcorehdr_mem; + +extern int load_crashdump_segments(struct kexec_info *info); +extern void fixup_elf_addrs(struct mem_ehdr *ehdr); + +#endif /* CRASHDUMP_ARM64_H */ diff --git a/kexec/arch/arm64/image-header.h b/kexec/arch/arm64/image-header.h new file mode 100644 index 0000000..26bb02f --- /dev/null +++ b/kexec/arch/arm64/image-header.h @@ -0,0 +1,147 @@ +/* + * ARM64 binary image header. + */ + +#if !defined(__ARM64_IMAGE_HEADER_H) +#define __ARM64_IMAGE_HEADER_H + +#include <endian.h> +#include <stdint.h> + +/** + * struct arm64_image_header - arm64 kernel image header. + * + * @pe_sig: Optional PE format 'MZ' signature. + * @branch_code: Reserved for instructions to branch to stext. + * @text_offset: The image load offset in LSB byte order. + * @image_size: An estimated size of the memory image size in LSB byte order. + * @flags: Bit flags in LSB byte order: + * Bit 0: Image byte order: 1=MSB. + * Bit 1-2: Kernel page size: 1=4K, 2=16K, 3=64K. + * Bit 3: Image placement: 0=low. + * @reserved_1: Reserved. + * @magic: Magic number, "ARM\x64". + * @pe_header: Optional offset to a PE format header. + **/ + +struct arm64_image_header { + uint8_t pe_sig[2]; + uint16_t branch_code[3]; + uint64_t text_offset; + uint64_t image_size; + uint64_t flags; + uint64_t reserved_1[3]; + uint8_t magic[4]; + uint32_t pe_header; +}; + +static const uint8_t arm64_image_magic[4] = {'A', 'R', 'M', 0x64U}; +static const uint8_t arm64_image_pe_sig[2] = {'M', 'Z'}; +static const uint8_t arm64_pe_machtype[6] = {'P','E', 0x0, 0x0, 0x64, 0xAA}; +static const uint64_t arm64_image_flag_be = (1UL << 0); +static const uint64_t arm64_image_flag_page_size = (3UL << 1); +static const uint64_t arm64_image_flag_placement = (1UL << 3); + +/** + * enum arm64_header_page_size + */ + +enum arm64_header_page_size { + arm64_header_page_size_invalid = 0, + arm64_header_page_size_4k, + arm64_header_page_size_16k, + arm64_header_page_size_64k +}; + +/** + * arm64_header_check_magic - Helper to check the arm64 image header. + * + * Returns non-zero if header is OK. + */ + +static inline int arm64_header_check_magic(const struct arm64_image_header *h) +{ + if (!h) + return 0; + + return (h->magic[0] == arm64_image_magic[0] + && h->magic[1] == arm64_image_magic[1] + && h->magic[2] == arm64_image_magic[2] + && h->magic[3] == arm64_image_magic[3]); +} + +/** + * arm64_header_check_pe_sig - Helper to check the arm64 image header. + * + * Returns non-zero if 'MZ' signature is found. + */ + +static inline int arm64_header_check_pe_sig(const struct arm64_image_header *h) +{ + if (!h) + return 0; + + return (h->pe_sig[0] == arm64_image_pe_sig[0] + && h->pe_sig[1] == arm64_image_pe_sig[1]); +} + +/** + * arm64_header_check_msb - Helper to check the arm64 image header. + * + * Returns non-zero if the image was built as big endian. + */ + +static inline int arm64_header_check_msb(const struct arm64_image_header *h) +{ + if (!h) + return 0; + + return (le64toh(h->flags) & arm64_image_flag_be) >> 0; +} + +/** + * arm64_header_page_size + */ + +static inline enum arm64_header_page_size arm64_header_page_size( + const struct arm64_image_header *h) +{ + if (!h) + return 0; + + return (le64toh(h->flags) & arm64_image_flag_page_size) >> 1; +} + +/** + * arm64_header_placement + * + * Returns non-zero if the image has no physical placement restrictions. + */ + +static inline int arm64_header_placement(const struct arm64_image_header *h) +{ + if (!h) + return 0; + + return (le64toh(h->flags) & arm64_image_flag_placement) >> 3; +} + +static inline uint64_t arm64_header_text_offset( + const struct arm64_image_header *h) +{ + if (!h) + return 0; + + return le64toh(h->text_offset); +} + +static inline uint64_t arm64_header_image_size( + const struct arm64_image_header *h) +{ + if (!h) + return 0; + + return le64toh(h->image_size); +} + +#endif diff --git a/kexec/arch/arm64/include/arch/options.h b/kexec/arch/arm64/include/arch/options.h new file mode 100644 index 0000000..8c695f3 --- /dev/null +++ b/kexec/arch/arm64/include/arch/options.h @@ -0,0 +1,43 @@ +#if !defined(KEXEC_ARCH_ARM64_OPTIONS_H) +#define KEXEC_ARCH_ARM64_OPTIONS_H + +#define OPT_APPEND ((OPT_MAX)+0) +#define OPT_DTB ((OPT_MAX)+1) +#define OPT_INITRD ((OPT_MAX)+2) +#define OPT_REUSE_CMDLINE ((OPT_MAX)+3) +#define OPT_SERIAL ((OPT_MAX)+4) +#define OPT_ARCH_MAX ((OPT_MAX)+5) + +#define KEXEC_ARCH_OPTIONS \ + KEXEC_OPTIONS \ + { "append", 1, NULL, OPT_APPEND }, \ + { "command-line", 1, NULL, OPT_APPEND }, \ + { "dtb", 1, NULL, OPT_DTB }, \ + { "initrd", 1, NULL, OPT_INITRD }, \ + { "serial", 1, NULL, OPT_SERIAL }, \ + { "ramdisk", 1, NULL, OPT_INITRD }, \ + { "reuse-cmdline", 0, NULL, OPT_REUSE_CMDLINE }, \ + +#define KEXEC_ARCH_OPT_STR KEXEC_OPT_STR /* Only accept long arch options. */ +#define KEXEC_ALL_OPTIONS KEXEC_ARCH_OPTIONS +#define KEXEC_ALL_OPT_STR KEXEC_ARCH_OPT_STR + +static const char arm64_opts_usage[] __attribute__ ((unused)) = +" --append=STRING Set the kernel command line to STRING.\n" +" --command-line=STRING Set the kernel command line to STRING.\n" +" --dtb=FILE Use FILE as the device tree blob.\n" +" --initrd=FILE Use FILE as the kernel initial ramdisk.\n" +" --serial=STRING Name of console used for purgatory printing. (e.g. ttyAMA0)\n" +" --ramdisk=FILE Use FILE as the kernel initial ramdisk.\n" +" --reuse-cmdline Use kernel command line from running system.\n"; + +struct arm64_opts { + const char *command_line; + const char *dtb; + const char *initrd; + const char *console; +}; + +extern struct arm64_opts arm64_opts; + +#endif diff --git a/kexec/arch/arm64/iomem.h b/kexec/arch/arm64/iomem.h new file mode 100644 index 0000000..d4864bb --- /dev/null +++ b/kexec/arch/arm64/iomem.h @@ -0,0 +1,10 @@ +#ifndef IOMEM_H +#define IOMEM_H + +#define SYSTEM_RAM "System RAM\n" +#define KERNEL_CODE "Kernel code\n" +#define KERNEL_DATA "Kernel data\n" +#define CRASH_KERNEL "Crash kernel\n" +#define IOMEM_RESERVED "reserved\n" + +#endif diff --git a/kexec/arch/arm64/kexec-arm64.c b/kexec/arch/arm64/kexec-arm64.c new file mode 100644 index 0000000..4a67b0d --- /dev/null +++ b/kexec/arch/arm64/kexec-arm64.c @@ -0,0 +1,1365 @@ +/* + * ARM64 kexec. + */ + +#define _GNU_SOURCE + +#include <assert.h> +#include <errno.h> +#include <getopt.h> +#include <inttypes.h> +#include <libfdt.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <linux/elf-em.h> +#include <elf.h> +#include <elf_info.h> + +#include <unistd.h> +#include <syscall.h> +#include <errno.h> +#include <linux/random.h> + +#include "kexec.h" +#include "kexec-arm64.h" +#include "crashdump.h" +#include "crashdump-arm64.h" +#include "dt-ops.h" +#include "fs2dt.h" +#include "iomem.h" +#include "kexec-syscall.h" +#include "mem_regions.h" +#include "arch/options.h" + +#define ROOT_NODE_ADDR_CELLS_DEFAULT 1 +#define ROOT_NODE_SIZE_CELLS_DEFAULT 1 + +#define PROP_ADDR_CELLS "#address-cells" +#define PROP_SIZE_CELLS "#size-cells" +#define PROP_ELFCOREHDR "linux,elfcorehdr" +#define PROP_USABLE_MEM_RANGE "linux,usable-memory-range" + +#define PAGE_OFFSET_36 ((0xffffffffffffffffUL) << 36) +#define PAGE_OFFSET_39 ((0xffffffffffffffffUL) << 39) +#define PAGE_OFFSET_42 ((0xffffffffffffffffUL) << 42) +#define PAGE_OFFSET_47 ((0xffffffffffffffffUL) << 47) +#define PAGE_OFFSET_48 ((0xffffffffffffffffUL) << 48) + +/* Global flag which indicates that we have tried reading + * PHYS_OFFSET from 'kcore' already. + */ +static bool try_read_phys_offset_from_kcore = false; + +/* Machine specific details. */ +static int va_bits = -1; +static unsigned long page_offset; + +/* Global varables the core kexec routines expect. */ + +unsigned char reuse_initrd; + +off_t initrd_base; +off_t initrd_size; + +const struct arch_map_entry arches[] = { + { "aarch64", KEXEC_ARCH_ARM64 }, + { "aarch64_be", KEXEC_ARCH_ARM64 }, + { NULL, 0 }, +}; + +struct file_type file_type[] = { + {"vmlinux", elf_arm64_probe, elf_arm64_load, elf_arm64_usage}, + {"Image", image_arm64_probe, image_arm64_load, image_arm64_usage}, + {"uImage", uImage_arm64_probe, uImage_arm64_load, uImage_arm64_usage}, + {"vmlinuz", pez_arm64_probe, pez_arm64_load, pez_arm64_usage}, +}; + +int file_types = sizeof(file_type) / sizeof(file_type[0]); + +/* arm64 global varables. */ + +struct arm64_opts arm64_opts; +struct arm64_mem arm64_mem = { + .phys_offset = arm64_mem_ngv, + .vp_offset = arm64_mem_ngv, +}; + +uint64_t get_phys_offset(void) +{ + assert(arm64_mem.phys_offset != arm64_mem_ngv); + return arm64_mem.phys_offset; +} + +uint64_t get_vp_offset(void) +{ + assert(arm64_mem.vp_offset != arm64_mem_ngv); + return arm64_mem.vp_offset; +} + +/** + * arm64_process_image_header - Process the arm64 image header. + * + * Make a guess that KERNEL_IMAGE_SIZE will be enough for older kernels. + */ + +int arm64_process_image_header(const struct arm64_image_header *h) +{ +#if !defined(KERNEL_IMAGE_SIZE) +# define KERNEL_IMAGE_SIZE MiB(16) +#endif + + if (!arm64_header_check_magic(h)) + return EFAILED; + + if (h->image_size) { + arm64_mem.text_offset = arm64_header_text_offset(h); + arm64_mem.image_size = arm64_header_image_size(h); + } else { + /* For 3.16 and older kernels. */ + arm64_mem.text_offset = 0x80000; + arm64_mem.image_size = KERNEL_IMAGE_SIZE; + fprintf(stderr, + "kexec: %s: Warning: Kernel image size set to %lu MiB.\n" + " Please verify compatability with lodaed kernel.\n", + __func__, KERNEL_IMAGE_SIZE / 1024UL / 1024UL); + } + + return 0; +} + +void arch_usage(void) +{ + printf(arm64_opts_usage); +} + +int arch_process_options(int argc, char **argv) +{ + static const char short_options[] = KEXEC_OPT_STR ""; + static const struct option options[] = { + KEXEC_ARCH_OPTIONS + { 0 } + }; + int opt; + char *cmdline = NULL; + const char *append = NULL; + int do_kexec_file_syscall = 0; + + for (opt = 0; opt != -1; ) { + opt = getopt_long(argc, argv, short_options, options, 0); + + switch (opt) { + case OPT_APPEND: + append = optarg; + break; + case OPT_REUSE_CMDLINE: + cmdline = get_command_line(); + break; + case OPT_DTB: + arm64_opts.dtb = optarg; + break; + case OPT_INITRD: + arm64_opts.initrd = optarg; + break; + case OPT_KEXEC_FILE_SYSCALL: + do_kexec_file_syscall = 1; + case OPT_SERIAL: + arm64_opts.console = optarg; + break; + default: + break; /* Ignore core and unknown options. */ + } + } + + arm64_opts.command_line = concat_cmdline(cmdline, append); + + dbgprintf("%s:%d: command_line: %s\n", __func__, __LINE__, + arm64_opts.command_line); + dbgprintf("%s:%d: initrd: %s\n", __func__, __LINE__, + arm64_opts.initrd); + dbgprintf("%s:%d: dtb: %s\n", __func__, __LINE__, + (do_kexec_file_syscall && arm64_opts.dtb ? "(ignored)" : + arm64_opts.dtb)); + dbgprintf("%s:%d: console: %s\n", __func__, __LINE__, + arm64_opts.console); + + if (do_kexec_file_syscall) + arm64_opts.dtb = NULL; + + return 0; +} + +/** + * find_purgatory_sink - Find a sink for purgatory output. + */ + +static uint64_t find_purgatory_sink(const char *console) +{ + int fd, ret; + char device[255], mem[255]; + struct stat sb; + char buffer[10]; + uint64_t iomem = 0x0; + + if (!console) + return 0; + + ret = snprintf(device, sizeof(device), "/sys/class/tty/%s", console); + if (ret < 0 || ret >= sizeof(device)) { + fprintf(stderr, "snprintf failed: %s\n", strerror(errno)); + return 0; + } + + if (stat(device, &sb) || !S_ISDIR(sb.st_mode)) { + fprintf(stderr, "kexec: %s: No valid console found for %s\n", + __func__, device); + return 0; + } + + ret = snprintf(mem, sizeof(mem), "%s%s", device, "/iomem_base"); + if (ret < 0 || ret >= sizeof(mem)) { + fprintf(stderr, "snprintf failed: %s\n", strerror(errno)); + return 0; + } + + printf("console memory read from %s\n", mem); + + fd = open(mem, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "kexec: %s: No able to open %s\n", + __func__, mem); + return 0; + } + + memset(buffer, '\0', sizeof(buffer)); + ret = read(fd, buffer, sizeof(buffer)); + if (ret < 0) { + fprintf(stderr, "kexec: %s: not able to read fd\n", __func__); + close(fd); + return 0; + } + + sscanf(buffer, "%lx", &iomem); + printf("console memory is at %#lx\n", iomem); + + close(fd); + return iomem; +} + +/** + * struct dtb - Info about a binary device tree. + * + * @buf: Device tree data. + * @size: Device tree data size. + * @name: Shorthand name of this dtb for messages. + * @path: Filesystem path. + */ + +struct dtb { + char *buf; + off_t size; + const char *name; + const char *path; +}; + +/** + * dump_reservemap - Dump the dtb's reservemap. + */ + +static void dump_reservemap(const struct dtb *dtb) +{ + int i; + + for (i = 0; ; i++) { + uint64_t address; + uint64_t size; + + fdt_get_mem_rsv(dtb->buf, i, &address, &size); + + if (!size) + break; + + dbgprintf("%s: %s {%" PRIx64 ", %" PRIx64 "}\n", __func__, + dtb->name, address, size); + } +} + +/** + * set_bootargs - Set the dtb's bootargs. + */ + +static int set_bootargs(struct dtb *dtb, const char *command_line) +{ + int result; + + if (!command_line || !command_line[0]) + return 0; + + result = dtb_set_bootargs(&dtb->buf, &dtb->size, command_line); + + if (result) { + fprintf(stderr, + "kexec: Set device tree bootargs failed.\n"); + return EFAILED; + } + + return 0; +} + +/** + * read_proc_dtb - Read /proc/device-tree. + */ + +static int read_proc_dtb(struct dtb *dtb) +{ + int result; + struct stat s; + static const char path[] = "/proc/device-tree"; + + result = stat(path, &s); + + if (result) { + dbgprintf("%s: %s\n", __func__, strerror(errno)); + return EFAILED; + } + + dtb->path = path; + create_flatten_tree((char **)&dtb->buf, &dtb->size, NULL); + + return 0; +} + +/** + * read_sys_dtb - Read /sys/firmware/fdt. + */ + +static int read_sys_dtb(struct dtb *dtb) +{ + int result; + struct stat s; + static const char path[] = "/sys/firmware/fdt"; + + result = stat(path, &s); + + if (result) { + dbgprintf("%s: %s\n", __func__, strerror(errno)); + return EFAILED; + } + + dtb->path = path; + dtb->buf = slurp_file(path, &dtb->size); + + return 0; +} + +/** + * read_1st_dtb - Read the 1st stage kernel's dtb. + */ + +static int read_1st_dtb(struct dtb *dtb) +{ + int result; + + dtb->name = "dtb_sys"; + result = read_sys_dtb(dtb); + + if (!result) + goto on_success; + + dtb->name = "dtb_proc"; + result = read_proc_dtb(dtb); + + if (!result) + goto on_success; + + dbgprintf("%s: not found\n", __func__); + return EFAILED; + +on_success: + dbgprintf("%s: found %s\n", __func__, dtb->path); + return 0; +} + +static int get_cells_size(void *fdt, uint32_t *address_cells, + uint32_t *size_cells) +{ + int nodeoffset; + const uint32_t *prop = NULL; + int prop_len; + + /* default values */ + *address_cells = ROOT_NODE_ADDR_CELLS_DEFAULT; + *size_cells = ROOT_NODE_SIZE_CELLS_DEFAULT; + + /* under root node */ + nodeoffset = fdt_path_offset(fdt, "/"); + if (nodeoffset < 0) + goto on_error; + + prop = fdt_getprop(fdt, nodeoffset, PROP_ADDR_CELLS, &prop_len); + if (prop) { + if (prop_len == sizeof(*prop)) + *address_cells = fdt32_to_cpu(*prop); + else + goto on_error; + } + + prop = fdt_getprop(fdt, nodeoffset, PROP_SIZE_CELLS, &prop_len); + if (prop) { + if (prop_len == sizeof(*prop)) + *size_cells = fdt32_to_cpu(*prop); + else + goto on_error; + } + + dbgprintf("%s: #address-cells:%d #size-cells:%d\n", __func__, + *address_cells, *size_cells); + return 0; + +on_error: + return EFAILED; +} + +static bool cells_size_fitted(uint32_t address_cells, uint32_t size_cells, + struct memory_range *range) +{ + dbgprintf("%s: %llx-%llx\n", __func__, range->start, range->end); + + /* if *_cells >= 2, cells can hold 64-bit values anyway */ + if ((address_cells == 1) && (range->start >= (1ULL << 32))) + return false; + + if ((size_cells == 1) && + ((range->end - range->start + 1) >= (1ULL << 32))) + return false; + + return true; +} + +static void fill_property(void *buf, uint64_t val, uint32_t cells) +{ + uint32_t val32; + int i; + + if (cells == 1) { + val32 = cpu_to_fdt32((uint32_t)val); + memcpy(buf, &val32, sizeof(uint32_t)); + } else { + for (i = 0; + i < (cells * sizeof(uint32_t) - sizeof(uint64_t)); i++) + *(char *)buf++ = 0; + + val = cpu_to_fdt64(val); + memcpy(buf, &val, sizeof(uint64_t)); + } +} + +static int fdt_setprop_ranges(void *fdt, int nodeoffset, const char *name, + struct memory_range *ranges, int nr_ranges, bool reverse, + uint32_t address_cells, uint32_t size_cells) +{ + void *buf, *prop; + size_t buf_size; + int i, result; + struct memory_range *range; + + buf_size = (address_cells + size_cells) * sizeof(uint32_t) * nr_ranges; + prop = buf = xmalloc(buf_size); + if (!buf) + return -ENOMEM; + + for (i = 0; i < nr_ranges; i++) { + if (reverse) + range = ranges + (nr_ranges - 1 - i); + else + range = ranges + i; + + fill_property(prop, range->start, address_cells); + prop += address_cells * sizeof(uint32_t); + + fill_property(prop, range->end - range->start + 1, size_cells); + prop += size_cells * sizeof(uint32_t); + } + + result = fdt_setprop(fdt, nodeoffset, name, buf, buf_size); + + free(buf); + + return result; +} + +/** + * setup_2nd_dtb - Setup the 2nd stage kernel's dtb. + */ + +static int setup_2nd_dtb(struct dtb *dtb, char *command_line, int on_crash) +{ + uint32_t address_cells, size_cells; + uint64_t fdt_val64; + uint64_t *prop; + char *new_buf = NULL; + int len, range_len; + int nodeoffset; + int new_size; + int i, result, kaslr_seed; + + result = fdt_check_header(dtb->buf); + + if (result) { + fprintf(stderr, "kexec: Invalid 2nd device tree.\n"); + return EFAILED; + } + + result = set_bootargs(dtb, command_line); + if (result) { + fprintf(stderr, "kexec: cannot set bootargs.\n"); + result = -EINVAL; + goto on_error; + } + + /* determine #address-cells and #size-cells */ + result = get_cells_size(dtb->buf, &address_cells, &size_cells); + if (result) { + fprintf(stderr, "kexec: cannot determine cells-size.\n"); + result = -EINVAL; + goto on_error; + } + + if (!cells_size_fitted(address_cells, size_cells, + &elfcorehdr_mem)) { + fprintf(stderr, "kexec: elfcorehdr doesn't fit cells-size.\n"); + result = -EINVAL; + goto on_error; + } + + for (i = 0; i < usablemem_rgns.size; i++) { + if (!cells_size_fitted(address_cells, size_cells, + &crash_reserved_mem[i])) { + fprintf(stderr, "kexec: usable memory range doesn't fit cells-size.\n"); + result = -EINVAL; + goto on_error; + } + } + + /* duplicate dt blob */ + range_len = sizeof(uint32_t) * (address_cells + size_cells); + new_size = fdt_totalsize(dtb->buf) + + fdt_prop_len(PROP_ELFCOREHDR, range_len) + + fdt_prop_len(PROP_USABLE_MEM_RANGE, range_len * usablemem_rgns.size); + + new_buf = xmalloc(new_size); + result = fdt_open_into(dtb->buf, new_buf, new_size); + if (result) { + dbgprintf("%s: fdt_open_into failed: %s\n", __func__, + fdt_strerror(result)); + result = -ENOSPC; + goto on_error; + } + + /* fixup 'kaslr-seed' with a random value, if supported */ + nodeoffset = fdt_path_offset(new_buf, "/chosen"); + prop = fdt_getprop_w(new_buf, nodeoffset, + "kaslr-seed", &len); + if (!prop || len != sizeof(uint64_t)) { + dbgprintf("%s: no kaslr-seed found\n", + __func__); + /* for kexec warm reboot case, we don't need to fixup + * other dtb properties + */ + if (!on_crash) { + dump_reservemap(dtb); + if (new_buf) + free(new_buf); + + return result; + } + } else { + kaslr_seed = fdt64_to_cpu(*prop); + + /* kaslr_seed must be wiped clean by primary + * kernel during boot + */ + if (kaslr_seed != 0) { + dbgprintf("%s: kaslr-seed is not wiped to 0.\n", + __func__); + result = -EINVAL; + goto on_error; + } + + /* + * Invoke the getrandom system call with + * GRND_NONBLOCK, to make sure we + * have a valid random seed to pass to the + * secondary kernel. + */ + result = syscall(SYS_getrandom, &fdt_val64, + sizeof(fdt_val64), + GRND_NONBLOCK); + + if(result == -1) { + fprintf(stderr, "%s: Reading random bytes failed.\n", + __func__); + + /* Currently on some arm64 platforms this + * 'getrandom' system call fails while booting + * the platform. + * + * In case, this happens at best we can set + * the 'kaslr_seed' as 0, indicating that the + * 2nd kernel will be booted with a 'nokaslr' + * like behaviour. + */ + fdt_val64 = 0UL; + dbgprintf("%s: Disabling KASLR in secondary kernel.\n", + __func__); + } + + nodeoffset = fdt_path_offset(new_buf, "/chosen"); + result = fdt_setprop_inplace(new_buf, + nodeoffset, "kaslr-seed", + &fdt_val64, sizeof(fdt_val64)); + if (result) { + dbgprintf("%s: fdt_setprop failed: %s\n", + __func__, fdt_strerror(result)); + result = -EINVAL; + goto on_error; + } + } + + if (on_crash) { + /* add linux,elfcorehdr */ + nodeoffset = fdt_path_offset(new_buf, "/chosen"); + result = fdt_setprop_ranges(new_buf, nodeoffset, + PROP_ELFCOREHDR, &elfcorehdr_mem, 1, false, + address_cells, size_cells); + if (result) { + dbgprintf("%s: fdt_setprop failed: %s\n", __func__, + fdt_strerror(result)); + result = -EINVAL; + goto on_error; + } + + /* + * add linux,usable-memory-range + * + * crash dump kernel support one or two regions, to make + * compatibility with existing user-space and older kdump, the + * low region is always the last one. + */ + nodeoffset = fdt_path_offset(new_buf, "/chosen"); + result = fdt_setprop_ranges(new_buf, nodeoffset, + PROP_USABLE_MEM_RANGE, + usablemem_rgns.ranges, usablemem_rgns.size, true, + address_cells, size_cells); + if (result) { + dbgprintf("%s: fdt_setprop failed: %s\n", __func__, + fdt_strerror(result)); + result = -EINVAL; + goto on_error; + } + } + + fdt_pack(new_buf); + dtb->buf = new_buf; + dtb->size = fdt_totalsize(new_buf); + + dump_reservemap(dtb); + + return result; + +on_error: + fprintf(stderr, "kexec: %s failed.\n", __func__); + if (new_buf) + free(new_buf); + + return result; +} + +unsigned long arm64_locate_kernel_segment(struct kexec_info *info) +{ + unsigned long hole; + + if (info->kexec_flags & KEXEC_ON_CRASH) { + unsigned long hole_end; + + hole = (crash_reserved_mem[usablemem_rgns.size - 1].start < mem_min ? + mem_min : crash_reserved_mem[usablemem_rgns.size - 1].start); + hole = _ALIGN_UP(hole, MiB(2)); + hole_end = hole + arm64_mem.text_offset + arm64_mem.image_size; + + if ((hole_end > mem_max) || + (hole_end > crash_reserved_mem[usablemem_rgns.size - 1].end)) { + dbgprintf("%s: Crash kernel out of range\n", __func__); + hole = ULONG_MAX; + } + } else { + hole = locate_hole(info, + arm64_mem.text_offset + arm64_mem.image_size, + MiB(2), 0, ULONG_MAX, 1); + + if (hole == ULONG_MAX) + dbgprintf("%s: locate_hole failed\n", __func__); + } + + return hole; +} + +/** + * arm64_load_other_segments - Prepare the dtb, initrd and purgatory segments. + */ + +int arm64_load_other_segments(struct kexec_info *info, + unsigned long image_base) +{ + int result; + unsigned long dtb_base; + unsigned long hole_min; + unsigned long hole_max; + unsigned long initrd_end; + uint64_t purgatory_sink; + char *initrd_buf = NULL; + struct dtb dtb; + char command_line[COMMAND_LINE_SIZE] = ""; + + if (arm64_opts.command_line) { + if (strlen(arm64_opts.command_line) > + sizeof(command_line) - 1) { + fprintf(stderr, + "Kernel command line too long for kernel!\n"); + return EFAILED; + } + + strncpy(command_line, arm64_opts.command_line, + sizeof(command_line) - 1); + command_line[sizeof(command_line) - 1] = 0; + } + + purgatory_sink = find_purgatory_sink(arm64_opts.console); + + dbgprintf("%s:%d: purgatory sink: 0x%" PRIx64 "\n", __func__, __LINE__, + purgatory_sink); + + if (arm64_opts.dtb) { + dtb.name = "dtb_user"; + dtb.buf = slurp_file(arm64_opts.dtb, &dtb.size); + } else { + result = read_1st_dtb(&dtb); + + if (result) { + fprintf(stderr, + "kexec: Error: No device tree available.\n"); + return EFAILED; + } + } + + result = setup_2nd_dtb(&dtb, command_line, + info->kexec_flags & KEXEC_ON_CRASH); + + if (result) + return EFAILED; + + /* Put the other segments after the image. */ + + hole_min = image_base + arm64_mem.image_size; + if (info->kexec_flags & KEXEC_ON_CRASH) + hole_max = crash_reserved_mem[usablemem_rgns.size - 1].end; + else + hole_max = ULONG_MAX; + + if (arm64_opts.initrd) { + initrd_buf = slurp_file(arm64_opts.initrd, &initrd_size); + + if (!initrd_buf) + fprintf(stderr, "kexec: Empty ramdisk file.\n"); + else { + /* Put the initrd after the kernel. */ + + initrd_base = add_buffer_phys_virt(info, initrd_buf, + initrd_size, initrd_size, 0, + hole_min, hole_max, 1, 0); + + initrd_end = initrd_base + initrd_size; + + /* Check limits as specified in booting.txt. + * The kernel may have as little as 32 GB of address space to map + * system memory and both kernel and initrd must be 1GB aligend. + */ + + if (_ALIGN_UP(initrd_end, GiB(1)) - _ALIGN_DOWN(image_base, GiB(1)) > GiB(32)) { + fprintf(stderr, "kexec: Error: image + initrd too big.\n"); + return EFAILED; + } + + dbgprintf("initrd: base %lx, size %lxh (%ld)\n", + initrd_base, initrd_size, initrd_size); + + result = dtb_set_initrd((char **)&dtb.buf, + &dtb.size, initrd_base, + initrd_base + initrd_size); + + if (result) + return EFAILED; + } + } + + if (!initrd_buf) { + /* Don't reuse the initrd addresses from 1st DTB */ + dtb_clear_initrd((char **)&dtb.buf, &dtb.size); + } + + /* Check size limit as specified in booting.txt. */ + + if (dtb.size > MiB(2)) { + fprintf(stderr, "kexec: Error: dtb too big.\n"); + return EFAILED; + } + + dtb_base = add_buffer_phys_virt(info, dtb.buf, dtb.size, dtb.size, + 0, hole_min, hole_max, 1, 0); + + /* dtb_base is valid if we got here. */ + + dbgprintf("dtb: base %lx, size %lxh (%ld)\n", dtb_base, dtb.size, + dtb.size); + + elf_rel_build_load(info, &info->rhdr, purgatory, purgatory_size, + hole_min, hole_max, 1, 0); + + info->entry = (void *)elf_rel_get_addr(&info->rhdr, "purgatory_start"); + + elf_rel_set_symbol(&info->rhdr, "arm64_sink", &purgatory_sink, + sizeof(purgatory_sink)); + + elf_rel_set_symbol(&info->rhdr, "arm64_kernel_entry", &image_base, + sizeof(image_base)); + + elf_rel_set_symbol(&info->rhdr, "arm64_dtb_addr", &dtb_base, + sizeof(dtb_base)); + + return 0; +} + +/** + * virt_to_phys - For processing elf file values. + */ + +unsigned long virt_to_phys(unsigned long v) +{ + unsigned long p; + + p = v - get_vp_offset() + get_phys_offset(); + + return p; +} + +/** + * phys_to_virt - For crashdump setup. + */ + +unsigned long phys_to_virt(struct crash_elf_info *elf_info, + unsigned long long p) +{ + unsigned long v; + + v = p - get_phys_offset() + elf_info->page_offset; + + return v; +} + +/** + * add_segment - Use virt_to_phys when loading elf files. + */ + +void add_segment(struct kexec_info *info, const void *buf, size_t bufsz, + unsigned long base, size_t memsz) +{ + add_segment_phys_virt(info, buf, bufsz, base, memsz, 1); +} + +static inline void set_phys_offset(int64_t v, char *set_method) +{ + if (arm64_mem.phys_offset == arm64_mem_ngv + || v < arm64_mem.phys_offset) { + arm64_mem.phys_offset = v; + dbgprintf("%s: phys_offset : %016lx (method : %s)\n", + __func__, arm64_mem.phys_offset, + set_method); + } +} + +/** + * get_va_bits - Helper for getting VA_BITS + */ + +static int get_va_bits(void) +{ + unsigned long long stext_sym_addr; + + /* + * if already got from kcore + */ + if (va_bits != -1) + goto out; + + + /* For kernel older than v4.19 */ + fprintf(stderr, "Warning, can't get the VA_BITS from kcore\n"); + stext_sym_addr = get_kernel_sym("_stext"); + + if (stext_sym_addr == 0) { + fprintf(stderr, "Can't get the symbol of _stext.\n"); + return -1; + } + + /* Derive va_bits as per arch/arm64/Kconfig */ + if ((stext_sym_addr & PAGE_OFFSET_36) == PAGE_OFFSET_36) { + va_bits = 36; + } else if ((stext_sym_addr & PAGE_OFFSET_39) == PAGE_OFFSET_39) { + va_bits = 39; + } else if ((stext_sym_addr & PAGE_OFFSET_42) == PAGE_OFFSET_42) { + va_bits = 42; + } else if ((stext_sym_addr & PAGE_OFFSET_47) == PAGE_OFFSET_47) { + va_bits = 47; + } else if ((stext_sym_addr & PAGE_OFFSET_48) == PAGE_OFFSET_48) { + va_bits = 48; + } else { + fprintf(stderr, + "Cannot find a proper _stext for calculating VA_BITS\n"); + return -1; + } + +out: + dbgprintf("va_bits : %d\n", va_bits); + + return 0; +} + +/** + * get_page_offset - Helper for getting PAGE_OFFSET + */ + +int get_page_offset(unsigned long *page_offset) +{ + unsigned long long text_sym_addr, kernel_va_mid; + int ret; + + text_sym_addr = get_kernel_sym("_text"); + if (text_sym_addr == 0) { + fprintf(stderr, "Can't get the symbol of _text to calculate page_offset.\n"); + return -1; + } + + ret = get_va_bits(); + if (ret < 0) + return ret; + + /* Since kernel 5.4, kernel image is put above + * UINT64_MAX << (va_bits - 1) + */ + kernel_va_mid = UINT64_MAX << (va_bits - 1); + /* older kernel */ + if (text_sym_addr < kernel_va_mid) + *page_offset = UINT64_MAX << (va_bits - 1); + else + *page_offset = UINT64_MAX << va_bits; + + dbgprintf("page_offset : %lx\n", *page_offset); + + return 0; +} + +static void arm64_scan_vmcoreinfo(char *pos) +{ + const char *str; + + str = "NUMBER(VA_BITS)="; + if (memcmp(str, pos, strlen(str)) == 0) + va_bits = strtoul(pos + strlen(str), NULL, 10); +} + +/** + * get_phys_offset_from_vmcoreinfo_pt_note - Helper for getting PHYS_OFFSET (and va_bits) + * from VMCOREINFO note inside 'kcore'. + */ + +static int get_phys_offset_from_vmcoreinfo_pt_note(long *phys_offset) +{ + int fd, ret = 0; + + if ((fd = open("/proc/kcore", O_RDONLY)) < 0) { + fprintf(stderr, "Can't open (%s).\n", "/proc/kcore"); + return EFAILED; + } + + arch_scan_vmcoreinfo = arm64_scan_vmcoreinfo; + ret = read_phys_offset_elf_kcore(fd, phys_offset); + + close(fd); + return ret; +} + +/** + * get_phys_base_from_pt_load - Helper for getting PHYS_OFFSET + * from PT_LOADs inside 'kcore'. + */ + +int get_phys_base_from_pt_load(long *phys_offset) +{ + int i, fd, ret; + unsigned long long phys_start; + unsigned long long virt_start; + + ret = get_page_offset(&page_offset); + if (ret < 0) + return ret; + + if ((fd = open("/proc/kcore", O_RDONLY)) < 0) { + fprintf(stderr, "Can't open (%s).\n", "/proc/kcore"); + return EFAILED; + } + + read_elf(fd); + + for (i = 0; get_pt_load(i, + &phys_start, NULL, &virt_start, NULL); + i++) { + if (virt_start != NOT_KV_ADDR + && virt_start >= page_offset + && phys_start != NOT_PADDR) + *phys_offset = phys_start - + (virt_start & ~page_offset); + } + + close(fd); + return 0; +} + +static bool to_be_excluded(char *str, unsigned long long start, unsigned long long end) +{ + if (!strncmp(str, CRASH_KERNEL, strlen(CRASH_KERNEL))) { + uint64_t load_start, load_end; + + if (!get_crash_kernel_load_range(&load_start, &load_end) && + (load_start == start) && (load_end == end)) + return false; + + return true; + } + + if (!strncmp(str, SYSTEM_RAM, strlen(SYSTEM_RAM)) || + !strncmp(str, KERNEL_CODE, strlen(KERNEL_CODE)) || + !strncmp(str, KERNEL_DATA, strlen(KERNEL_DATA))) + return false; + else + return true; +} + +/** + * get_memory_ranges - Try to get the memory ranges from + * /proc/iomem. + */ +int get_memory_ranges(struct memory_range **range, int *ranges, + unsigned long kexec_flags) +{ + long phys_offset = -1; + FILE *fp; + const char *iomem = proc_iomem(); + char line[MAX_LINE], *str; + unsigned long long start, end; + int n, consumed; + struct memory_ranges memranges; + struct memory_range *last, excl_range; + int ret; + + if (!try_read_phys_offset_from_kcore) { + /* Since kernel version 4.19, 'kcore' contains + * a new PT_NOTE which carries the VMCOREINFO + * information. + * If the same is available, one should prefer the + * same to retrieve 'PHYS_OFFSET' value exported by + * the kernel as this is now the standard interface + * exposed by kernel for sharing machine specific + * details with the userland. + */ + ret = get_phys_offset_from_vmcoreinfo_pt_note(&phys_offset); + if (!ret) { + if (phys_offset != -1) + set_phys_offset(phys_offset, + "vmcoreinfo pt_note"); + } else { + /* If we are running on a older kernel, + * try to retrieve the 'PHYS_OFFSET' value + * exported by the kernel in the 'kcore' + * file by reading the PT_LOADs and determining + * the correct combination. + */ + ret = get_phys_base_from_pt_load(&phys_offset); + if (!ret) + if (phys_offset != -1) + set_phys_offset(phys_offset, + "pt_load"); + } + + try_read_phys_offset_from_kcore = true; + } + + fp = fopen(iomem, "r"); + if (!fp) + die("Cannot open %s\n", iomem); + + memranges.ranges = NULL; + memranges.size = memranges.max_size = 0; + + while (fgets(line, sizeof(line), fp) != 0) { + n = sscanf(line, "%llx-%llx : %n", &start, &end, &consumed); + if (n != 2) + continue; + str = line + consumed; + + if (!strncmp(str, SYSTEM_RAM, strlen(SYSTEM_RAM))) { + ret = mem_regions_alloc_and_add(&memranges, + start, end - start + 1, RANGE_RAM); + if (ret) { + fprintf(stderr, + "Cannot allocate memory for ranges\n"); + fclose(fp); + return -ENOMEM; + } + + dbgprintf("%s:+[%d] %016llx - %016llx\n", __func__, + memranges.size - 1, + memranges.ranges[memranges.size - 1].start, + memranges.ranges[memranges.size - 1].end); + } else if (to_be_excluded(str, start, end)) { + if (!memranges.size) + continue; + + /* + * Note: mem_regions_exclude() doesn't guarantee + * that the ranges are sorted out, but as long as + * we cope with /proc/iomem, we only operate on + * the last entry and so it is safe. + */ + + /* The last System RAM range */ + last = &memranges.ranges[memranges.size - 1]; + + if (last->end < start) + /* New resource outside of System RAM */ + continue; + if (end < last->start) + /* Already excluded by parent resource */ + continue; + + excl_range.start = start; + excl_range.end = end; + ret = mem_regions_alloc_and_exclude(&memranges, &excl_range); + if (ret) { + fprintf(stderr, + "Cannot allocate memory for ranges (exclude)\n"); + fclose(fp); + return -ENOMEM; + } + dbgprintf("%s:- %016llx - %016llx\n", + __func__, start, end); + } + } + + fclose(fp); + + *range = memranges.ranges; + *ranges = memranges.size; + + /* As a fallback option, we can try determining the PHYS_OFFSET + * value from the '/proc/iomem' entries as well. + * + * But note that this can be flaky, as on certain arm64 + * platforms, it has been noticed that due to a hole at the + * start of physical ram exposed to kernel + * (i.e. it doesn't start from address 0), the kernel still + * calculates the 'memstart_addr' kernel variable as 0. + * + * Whereas the SYSTEM_RAM or IOMEM_RESERVED range in + * '/proc/iomem' would carry a first entry whose start address + * is non-zero (as the physical ram exposed to the kernel + * starts from a non-zero address). + * + * In such cases, if we rely on '/proc/iomem' entries to + * calculate the phys_offset, then we will have mismatch + * between the user-space and kernel space 'PHYS_OFFSET' + * value. + */ + if (memranges.size) + set_phys_offset(memranges.ranges[0].start, "iomem"); + + dbgprint_mem_range("System RAM ranges;", + memranges.ranges, memranges.size); + + return 0; +} + +int arch_compat_trampoline(struct kexec_info *info) +{ + return 0; +} + +int machine_verify_elf_rel(struct mem_ehdr *ehdr) +{ + return (ehdr->e_machine == EM_AARCH64); +} + +enum aarch64_rel_type { + R_AARCH64_NONE = 0, + R_AARCH64_ABS64 = 257, + R_AARCH64_PREL32 = 261, + R_AARCH64_MOVW_UABS_G0_NC = 264, + R_AARCH64_MOVW_UABS_G1_NC = 266, + R_AARCH64_MOVW_UABS_G2_NC = 268, + R_AARCH64_MOVW_UABS_G3 =269, + R_AARCH64_LD_PREL_LO19 = 273, + R_AARCH64_ADR_PREL_LO21 = 274, + R_AARCH64_ADR_PREL_PG_HI21 = 275, + R_AARCH64_ADD_ABS_LO12_NC = 277, + R_AARCH64_JUMP26 = 282, + R_AARCH64_CALL26 = 283, + R_AARCH64_LDST64_ABS_LO12_NC = 286, + R_AARCH64_LDST128_ABS_LO12_NC = 299 +}; + +static uint32_t get_bits(uint32_t value, int start, int end) +{ + uint32_t mask = ((uint32_t)1 << (end + 1 - start)) - 1; + return (value >> start) & mask; +} + +void machine_apply_elf_rel(struct mem_ehdr *ehdr, struct mem_sym *UNUSED(sym), + unsigned long r_type, void *ptr, unsigned long address, + unsigned long value) +{ + uint64_t *loc64; + uint32_t *loc32; + uint64_t *location = (uint64_t *)ptr; + uint64_t data = *location; + uint64_t imm; + const char *type = NULL; + + switch((enum aarch64_rel_type)r_type) { + case R_AARCH64_ABS64: + type = "ABS64"; + loc64 = ptr; + *loc64 = cpu_to_elf64(ehdr, value); + break; + case R_AARCH64_PREL32: + type = "PREL32"; + loc32 = ptr; + *loc32 = cpu_to_elf32(ehdr, value - address); + break; + + /* Set a MOV[KZ] immediate field to bits [15:0] of X. No overflow check */ + case R_AARCH64_MOVW_UABS_G0_NC: + type = "MOVW_UABS_G0_NC"; + loc32 = ptr; + imm = get_bits(value, 0, 15); + *loc32 = cpu_to_le32(le32_to_cpu(*loc32) + (imm << 5)); + break; + /* Set a MOV[KZ] immediate field to bits [31:16] of X. No overflow check */ + case R_AARCH64_MOVW_UABS_G1_NC: + type = "MOVW_UABS_G1_NC"; + loc32 = ptr; + imm = get_bits(value, 16, 31); + *loc32 = cpu_to_le32(le32_to_cpu(*loc32) + (imm << 5)); + break; + /* Set a MOV[KZ] immediate field to bits [47:32] of X. No overflow check */ + case R_AARCH64_MOVW_UABS_G2_NC: + type = "MOVW_UABS_G2_NC"; + loc32 = ptr; + imm = get_bits(value, 32, 47); + *loc32 = cpu_to_le32(le32_to_cpu(*loc32) + (imm << 5)); + break; + /* Set a MOV[KZ] immediate field to bits [63:48] of X */ + case R_AARCH64_MOVW_UABS_G3: + type = "MOVW_UABS_G3"; + loc32 = ptr; + imm = get_bits(value, 48, 63); + *loc32 = cpu_to_le32(le32_to_cpu(*loc32) + (imm << 5)); + break; + + case R_AARCH64_LD_PREL_LO19: + type = "LD_PREL_LO19"; + loc32 = ptr; + *loc32 = cpu_to_le32(le32_to_cpu(*loc32) + + (((value - address) << 3) & 0xffffe0)); + break; + case R_AARCH64_ADR_PREL_LO21: + if (value & 3) + die("%s: ERROR Unaligned value: %lx\n", __func__, + value); + type = "ADR_PREL_LO21"; + loc32 = ptr; + *loc32 = cpu_to_le32(le32_to_cpu(*loc32) + + (((value - address) << 3) & 0xffffe0)); + break; + case R_AARCH64_ADR_PREL_PG_HI21: + type = "ADR_PREL_PG_HI21"; + imm = ((value & ~0xfff) - (address & ~0xfff)) >> 12; + loc32 = ptr; + *loc32 = cpu_to_le32(le32_to_cpu(*loc32) + + ((imm & 3) << 29) + ((imm & 0x1ffffc) << (5 - 2))); + break; + case R_AARCH64_ADD_ABS_LO12_NC: + type = "ADD_ABS_LO12_NC"; + loc32 = ptr; + *loc32 = cpu_to_le32(le32_to_cpu(*loc32) + + ((value & 0xfff) << 10)); + break; + case R_AARCH64_JUMP26: + type = "JUMP26"; + loc32 = ptr; + *loc32 = cpu_to_le32(le32_to_cpu(*loc32) + + (((value - address) >> 2) & 0x3ffffff)); + break; + case R_AARCH64_CALL26: + type = "CALL26"; + loc32 = ptr; + *loc32 = cpu_to_le32(le32_to_cpu(*loc32) + + (((value - address) >> 2) & 0x3ffffff)); + break; + /* encode imm field with bits [11:3] of value */ + case R_AARCH64_LDST64_ABS_LO12_NC: + if (value & 7) + die("%s: ERROR Unaligned value: %lx\n", __func__, + value); + type = "LDST64_ABS_LO12_NC"; + loc32 = ptr; + *loc32 = cpu_to_le32(le32_to_cpu(*loc32) + + ((value & 0xff8) << (10 - 3))); + break; + + /* encode imm field with bits [11:4] of value */ + case R_AARCH64_LDST128_ABS_LO12_NC: + if (value & 15) + die("%s: ERROR Unaligned value: %lx\n", __func__, + value); + type = "LDST128_ABS_LO12_NC"; + loc32 = ptr; + imm = value & 0xff0; + *loc32 = cpu_to_le32(le32_to_cpu(*loc32) + (imm << (10 - 4))); + break; + default: + die("%s: ERROR Unknown type: %lu\n", __func__, r_type); + break; + } + + dbgprintf("%s: %s %016lx->%016lx\n", __func__, type, data, *location); +} + +void arch_reuse_initrd(void) +{ + reuse_initrd = 1; +} + +void arch_update_purgatory(struct kexec_info *UNUSED(info)) +{ +} diff --git a/kexec/arch/arm64/kexec-arm64.h b/kexec/arch/arm64/kexec-arm64.h new file mode 100644 index 0000000..95fb5c2 --- /dev/null +++ b/kexec/arch/arm64/kexec-arm64.h @@ -0,0 +1,84 @@ +/* + * ARM64 kexec. + */ + +#if !defined(KEXEC_ARM64_H) +#define KEXEC_ARM64_H + +#include <stdbool.h> +#include <sys/types.h> + +#include "image-header.h" +#include "kexec.h" + +#define KEXEC_SEGMENT_MAX 64 + +#define BOOT_BLOCK_VERSION 17 +#define BOOT_BLOCK_LAST_COMP_VERSION 16 +#define COMMAND_LINE_SIZE 2048 /* from kernel */ + +#define KiB(x) ((x) * 1024UL) +#define MiB(x) (KiB(x) * 1024UL) +#define GiB(x) (MiB(x) * 1024UL) + +#define ULONGLONG_MAX (~0ULL) + +/* + * Incorrect address + */ +#define NOT_KV_ADDR (0x0) +#define NOT_PADDR (ULONGLONG_MAX) + +int elf_arm64_probe(const char *kernel_buf, off_t kernel_size); +int elf_arm64_load(int argc, char **argv, const char *kernel_buf, + off_t kernel_size, struct kexec_info *info); +void elf_arm64_usage(void); + +int image_arm64_probe(const char *kernel_buf, off_t kernel_size); +int image_arm64_load(int argc, char **argv, const char *kernel_buf, + off_t kernel_size, struct kexec_info *info); +void image_arm64_usage(void); + +int uImage_arm64_probe(const char *buf, off_t len); +int uImage_arm64_load(int argc, char **argv, const char *buf, off_t len, + struct kexec_info *info); +void uImage_arm64_usage(void); + +int pez_arm64_probe(const char *kernel_buf, off_t kernel_size); +int pez_arm64_load(int argc, char **argv, const char *buf, off_t len, + struct kexec_info *info); +void pez_arm64_usage(void); + + +extern off_t initrd_base; +extern off_t initrd_size; + +/** + * struct arm64_mem - Memory layout info. + */ + +struct arm64_mem { + int64_t phys_offset; + uint64_t text_offset; + uint64_t image_size; + uint64_t vp_offset; +}; + +#define arm64_mem_ngv UINT64_MAX +extern struct arm64_mem arm64_mem; + +uint64_t get_phys_offset(void); +uint64_t get_vp_offset(void); +int get_page_offset(unsigned long *offset); + +static inline void reset_vp_offset(void) +{ + arm64_mem.vp_offset = arm64_mem_ngv; +} + +int arm64_process_image_header(const struct arm64_image_header *h); +unsigned long arm64_locate_kernel_segment(struct kexec_info *info); +int arm64_load_other_segments(struct kexec_info *info, + unsigned long image_base); + +#endif diff --git a/kexec/arch/arm64/kexec-elf-arm64.c b/kexec/arch/arm64/kexec-elf-arm64.c new file mode 100644 index 0000000..e14f8e9 --- /dev/null +++ b/kexec/arch/arm64/kexec-elf-arm64.c @@ -0,0 +1,170 @@ +/* + * ARM64 kexec elf support. + */ + +#define _GNU_SOURCE + +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <stdlib.h> +#include <linux/elf.h> + +#include "arch/options.h" +#include "crashdump-arm64.h" +#include "kexec-arm64.h" +#include "kexec-elf.h" +#include "kexec-syscall.h" + +int elf_arm64_probe(const char *kernel_buf, off_t kernel_size) +{ + struct mem_ehdr ehdr; + int result; + + result = build_elf_exec_info(kernel_buf, kernel_size, &ehdr, 0); + + if (result < 0) { + dbgprintf("%s: Not an ELF executable.\n", __func__); + goto on_exit; + } + + if (ehdr.e_machine != EM_AARCH64) { + dbgprintf("%s: Not an AARCH64 ELF executable.\n", __func__); + result = -1; + goto on_exit; + } + + result = 0; +on_exit: + free_elf_info(&ehdr); + return result; +} + +int elf_arm64_load(int argc, char **argv, const char *kernel_buf, + off_t kernel_size, struct kexec_info *info) +{ + const struct arm64_image_header *header = NULL; + unsigned long kernel_segment; + struct mem_ehdr ehdr; + int result; + int i; + + if (info->file_mode) { + fprintf(stderr, + "ELF executable is not supported in kexec_file\n"); + + return EFAILED; + } + + result = build_elf_exec_info(kernel_buf, kernel_size, &ehdr, 0); + + if (result < 0) { + dbgprintf("%s: build_elf_exec_info failed\n", __func__); + goto exit; + } + + /* Find and process the arm64 image header. */ + + for (i = 0; i < ehdr.e_phnum; i++) { + struct mem_phdr *phdr = &ehdr.e_phdr[i]; + unsigned long header_offset; + + if (phdr->p_type != PT_LOAD) + continue; + + /* + * When CONFIG_ARM64_RANDOMIZE_TEXT_OFFSET=y the image header + * could be offset in the elf segment. The linker script sets + * ehdr.e_entry to the start of text. + */ + + header_offset = ehdr.e_entry - phdr->p_vaddr; + + header = (const struct arm64_image_header *)( + kernel_buf + phdr->p_offset + header_offset); + + if (!arm64_process_image_header(header)) { + dbgprintf("%s: e_entry: %016llx\n", __func__, + ehdr.e_entry); + dbgprintf("%s: p_vaddr: %016llx\n", __func__, + phdr->p_vaddr); + dbgprintf("%s: header_offset: %016lx\n", __func__, + header_offset); + + break; + } + } + + if (i == ehdr.e_phnum) { + dbgprintf("%s: Valid arm64 header not found\n", __func__); + result = EFAILED; + goto exit; + } + + kernel_segment = arm64_locate_kernel_segment(info); + + if (kernel_segment == ULONG_MAX) { + dbgprintf("%s: Kernel segment is not allocated\n", __func__); + result = EFAILED; + goto exit; + } + + arm64_mem.vp_offset = _ALIGN_DOWN(ehdr.e_entry, MiB(2)); + if (!(info->kexec_flags & KEXEC_ON_CRASH)) + arm64_mem.vp_offset -= kernel_segment - get_phys_offset(); + + dbgprintf("%s: kernel_segment: %016lx\n", __func__, kernel_segment); + dbgprintf("%s: text_offset: %016lx\n", __func__, + arm64_mem.text_offset); + dbgprintf("%s: image_size: %016lx\n", __func__, + arm64_mem.image_size); + dbgprintf("%s: phys_offset: %016lx\n", __func__, + arm64_mem.phys_offset); + dbgprintf("%s: vp_offset: %016lx\n", __func__, + arm64_mem.vp_offset); + dbgprintf("%s: PE format: %s\n", __func__, + (arm64_header_check_pe_sig(header) ? "yes" : "no")); + + /* create and initialize elf core header segment */ + if (info->kexec_flags & KEXEC_ON_CRASH) { + result = load_crashdump_segments(info); + if (result) { + dbgprintf("%s: Creating eflcorehdr failed.\n", + __func__); + goto exit; + } + } + + /* load the kernel */ + if (info->kexec_flags & KEXEC_ON_CRASH) + /* + * offset addresses in elf header in order to load + * vmlinux (elf_exec) into crash kernel's memory + */ + fixup_elf_addrs(&ehdr); + + result = elf_exec_load(&ehdr, info); + + if (result) { + dbgprintf("%s: elf_exec_load failed\n", __func__); + goto exit; + } + + /* load additional data */ + result = arm64_load_other_segments(info, kernel_segment + + arm64_mem.text_offset); + +exit: + reset_vp_offset(); + free_elf_info(&ehdr); + if (result) + fprintf(stderr, "kexec: Bad elf image file, load failed.\n"); + return result; +} + +void elf_arm64_usage(void) +{ + printf( +" An ARM64 ELF image, big or little endian.\n" +" Typically vmlinux or a stripped version of vmlinux.\n\n"); +} diff --git a/kexec/arch/arm64/kexec-image-arm64.c b/kexec/arch/arm64/kexec-image-arm64.c new file mode 100644 index 0000000..a196747 --- /dev/null +++ b/kexec/arch/arm64/kexec-image-arm64.c @@ -0,0 +1,119 @@ +/* + * ARM64 kexec binary image support. + */ + +#define _GNU_SOURCE + +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include "crashdump-arm64.h" +#include "image-header.h" +#include "kexec.h" +#include "kexec-arm64.h" +#include "kexec-syscall.h" +#include "arch/options.h" + +int image_arm64_probe(const char *kernel_buf, off_t kernel_size) +{ + const struct arm64_image_header *h; + + if (kernel_size < sizeof(struct arm64_image_header)) { + dbgprintf("%s: No arm64 image header.\n", __func__); + return -1; + } + + h = (const struct arm64_image_header *)(kernel_buf); + + if (!arm64_header_check_magic(h)) { + dbgprintf("%s: Bad arm64 image header.\n", __func__); + return -1; + } + + return 0; +} + +int image_arm64_load(int argc, char **argv, const char *kernel_buf, + off_t kernel_size, struct kexec_info *info) +{ + const struct arm64_image_header *header; + unsigned long kernel_segment; + int result; + + if (info->file_mode) { + if (arm64_opts.initrd) { + info->initrd_fd = open(arm64_opts.initrd, O_RDONLY); + if (info->initrd_fd == -1) { + fprintf(stderr, + "Could not open initrd file %s:%s\n", + arm64_opts.initrd, strerror(errno)); + result = EFAILED; + goto exit; + } + } + + if (arm64_opts.command_line) { + info->command_line = (char *)arm64_opts.command_line; + info->command_line_len = + strlen(arm64_opts.command_line) + 1; + } + + return 0; + } + + header = (const struct arm64_image_header *)(kernel_buf); + + if (arm64_process_image_header(header)) + return EFAILED; + + kernel_segment = arm64_locate_kernel_segment(info); + + if (kernel_segment == ULONG_MAX) { + dbgprintf("%s: Kernel segment is not allocated\n", __func__); + result = EFAILED; + goto exit; + } + + dbgprintf("%s: kernel_segment: %016lx\n", __func__, kernel_segment); + dbgprintf("%s: text_offset: %016lx\n", __func__, + arm64_mem.text_offset); + dbgprintf("%s: image_size: %016lx\n", __func__, + arm64_mem.image_size); + dbgprintf("%s: phys_offset: %016lx\n", __func__, + arm64_mem.phys_offset); + dbgprintf("%s: vp_offset: %016lx\n", __func__, + arm64_mem.vp_offset); + dbgprintf("%s: PE format: %s\n", __func__, + (arm64_header_check_pe_sig(header) ? "yes" : "no")); + + /* create and initialize elf core header segment */ + if (info->kexec_flags & KEXEC_ON_CRASH) { + result = load_crashdump_segments(info); + if (result) { + dbgprintf("%s: Creating eflcorehdr failed.\n", + __func__); + goto exit; + } + } + + /* load the kernel */ + add_segment_phys_virt(info, kernel_buf, kernel_size, + kernel_segment + arm64_mem.text_offset, + arm64_mem.image_size, 0); + + /* load additional data */ + result = arm64_load_other_segments(info, kernel_segment + + arm64_mem.text_offset); + +exit: + if (result) + fprintf(stderr, "kexec: load failed.\n"); + return result; +} + +void image_arm64_usage(void) +{ + printf( +" An ARM64 binary image, compressed or not, big or little endian.\n" +" Typically an Image file.\n\n"); +} diff --git a/kexec/arch/arm64/kexec-uImage-arm64.c b/kexec/arch/arm64/kexec-uImage-arm64.c new file mode 100644 index 0000000..c466913 --- /dev/null +++ b/kexec/arch/arm64/kexec-uImage-arm64.c @@ -0,0 +1,52 @@ +/* + * uImage support added by David Woodhouse <dwmw2@infradead.org> + */ +#include <stdint.h> +#include <string.h> +#include <sys/types.h> +#include <image.h> +#include <kexec-uImage.h> +#include "../../kexec.h" +#include "kexec-arm64.h" + +int uImage_arm64_probe(const char *buf, off_t len) +{ + int ret; + + ret = uImage_probe_kernel(buf, len, IH_ARCH_ARM64); + + /* 0 - valid uImage. + * -1 - uImage is corrupted. + * 1 - image is not a uImage. + */ + if (!ret) + return 0; + else + return -1; +} + +int uImage_arm64_load(int argc, char **argv, const char *buf, off_t len, + struct kexec_info *info) +{ + struct Image_info img; + int ret; + + if (info->file_mode) { + fprintf(stderr, + "uImage is not supported in kexec_file\n"); + + return EFAILED; + } + + ret = uImage_load(buf, len, &img); + if (ret) + return ret; + + return image_arm64_load(argc, argv, img.buf, img.len, info); +} + +void uImage_arm64_usage(void) +{ + printf( +" An ARM64 U-boot uImage file, compressed or not, big or little endian.\n\n"); +} diff --git a/kexec/arch/arm64/kexec-vmlinuz-arm64.c b/kexec/arch/arm64/kexec-vmlinuz-arm64.c new file mode 100644 index 0000000..c0ee47c --- /dev/null +++ b/kexec/arch/arm64/kexec-vmlinuz-arm64.c @@ -0,0 +1,110 @@ +/* + * ARM64 PE compressed Image (vmlinuz, ZBOOT) support. + * + * Several distros use 'make zinstall' rule inside + * 'arch/arm64/boot/Makefile' to install the arm64 + * ZBOOT compressed file inside the boot destination + * directory (for e.g. /boot). + * + * Currently we cannot use kexec_file_load() to load vmlinuz + * PE images that self decompress. + * + * To support ZBOOT, we should: + * a). Copy the compressed contents of vmlinuz to a temporary file. + * b). Decompress (gunzip-decompress) the contents inside the + * temporary file. + * c). Validate the resulting image and write it back to the + * temporary file. + * d). Pass the 'fd' of the temporary file to the kernel space. + * + * Note this, module doesn't provide a _load() function instead + * relying on image_arm64_load() to load the resulting decompressed + * image. + * + * So basically the kernel space still gets a decompressed + * kernel image to load via kexec-tools. + */ + +#define _GNU_SOURCE +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include "kexec-arm64.h" +#include <kexec-pe-zboot.h> +#include "arch/options.h" + +static int kernel_fd = -1; + +/* Returns: + * -1 : in case of error/invalid format (not a valid PE+compressed ZBOOT format. + */ +int pez_arm64_probe(const char *kernel_buf, off_t kernel_size) +{ + int ret = -1; + const struct arm64_image_header *h; + char *buf; + off_t buf_sz; + + buf = (char *)kernel_buf; + buf_sz = kernel_size; + if (!buf) + return -1; + h = (const struct arm64_image_header *)buf; + + dbgprintf("%s: PROBE.\n", __func__); + if (buf_sz < sizeof(struct arm64_image_header)) { + dbgprintf("%s: Not large enough to be a PE image.\n", __func__); + return -1; + } + if (!arm64_header_check_pe_sig(h)) { + dbgprintf("%s: Not an PE image.\n", __func__); + return -1; + } + + if (buf_sz < sizeof(struct arm64_image_header) + h->pe_header) { + dbgprintf("%s: PE image offset larger than image.\n", __func__); + return -1; + } + + if (memcmp(&buf[h->pe_header], + arm64_pe_machtype, sizeof(arm64_pe_machtype))) { + dbgprintf("%s: PE header doesn't match machine type.\n", __func__); + return -1; + } + + ret = pez_prepare(buf, buf_sz, &kernel_fd); + + if (!ret) { + /* validate the arm64 specific header */ + struct arm64_image_header hdr_check; + if (read(kernel_fd, &hdr_check, sizeof(hdr_check)) != sizeof(hdr_check)) + goto bad_header; + + lseek(kernel_fd, 0, SEEK_SET); + + if (!arm64_header_check_magic(&hdr_check)) { + dbgprintf("%s: Bad arm64 image header.\n", __func__); + goto bad_header; + } + } + + return ret; +bad_header: + close(kernel_fd); + free(buf); + return -1; +} + +int pez_arm64_load(int argc, char **argv, const char *buf, off_t len, + struct kexec_info *info) +{ + info->kernel_fd = kernel_fd; + return image_arm64_load(argc, argv, buf, len, info); +} + +void pez_arm64_usage(void) +{ + printf( +" An ARM64 vmlinuz, PE image of a compressed, little endian.\n" +" kernel, built with ZBOOT enabled.\n\n"); +} diff --git a/kexec/arch/cris/Makefile b/kexec/arch/cris/Makefile new file mode 100644 index 0000000..4982f3e --- /dev/null +++ b/kexec/arch/cris/Makefile @@ -0,0 +1,13 @@ +cris_KEXEC_SRCS = kexec/arch/cris/kexec-cris.c +cris_KEXEC_SRCS += kexec/arch/cris/kexec-elf-cris.c +cris_KEXEC_SRCS += kexec/arch/cris/cris-setup-simple.S +cris_KEXEC_SRCS += kexec/arch/cris/kexec-elf-rel-cris.c + +cris_ADD_BUFFER = +cris_ADD_SEGMENT = +cris_VIRT_TO_PHYS = + +dist += kexec/arch/cris/Makefile $(cris_KEXEC_SRCS) \ + kexec/arch/cris/kexec-cris.h \ + kexec/arch/cris/include/arch/options.h + diff --git a/kexec/arch/cris/cris-setup-simple.S b/kexec/arch/cris/cris-setup-simple.S new file mode 100644 index 0000000..764f188 --- /dev/null +++ b/kexec/arch/cris/cris-setup-simple.S @@ -0,0 +1,31 @@ +/* + * cris-setup-simple.S - code to execute before stepping into the new kernel. + * Copyright (C) 2008 AXIS Communications AB + * Written by Edgar E. Iglesias + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + + .data + .globl cris_trampoline +cris_trampoline: + .balign 4 + lapc cris_regframe, $sp + moveq 0, $r0 + move $r0, $pid + + movem [$sp+], $r14 + jump $r0 + nop + + .globl cris_regframe +cris_regframe: + .balign 4 + .fill 16, 4, 0 +cris_trampoline_end: + + .globl cris_trampoline_size +cris_trampoline_size: + .long cris_trampoline_end - cris_trampoline + diff --git a/kexec/arch/cris/include/arch/options.h b/kexec/arch/cris/include/arch/options.h new file mode 100644 index 0000000..1c1a029 --- /dev/null +++ b/kexec/arch/cris/include/arch/options.h @@ -0,0 +1,35 @@ +#ifndef KEXEC_ARCH_CRIS_OPTIONS_H +#define KEXEC_ARCH_CRIS_OPTIONS_H + +#define OPT_ARCH_MAX (OPT_MAX+0) +#define OPT_APPEND (OPT_ARCH_MAX+0) + +/* Options relevant to the architecture (excluding loader-specific ones), + * in this case none: + */ +#define KEXEC_ARCH_OPTIONS \ + KEXEC_OPTIONS \ + +#define KEXEC_ARCH_OPT_STR KEXEC_OPT_STR "" + +/* The following two #defines list ALL of the options added by all of the + * architecture's loaders. + * o main() uses this complete list to scan for its options, ignoring + * arch-specific/loader-specific ones. + * o Then, arch_process_options() uses this complete list to scan for its + * options, ignoring general/loader-specific ones. + * o Then, the file_type[n].load re-scans for options, using + * KEXEC_ARCH_OPTIONS plus its loader-specific options subset. + * Any unrecognised options cause an error here. + * + * This is done so that main()'s/arch_process_options()'s getopt_long() calls + * don't choose a kernel filename from random arguments to options they don't + * recognise -- as they now recognise (if not act upon) all possible options. + */ +#define KEXEC_ALL_OPTIONS \ + KEXEC_ARCH_OPTIONS \ + {"append", 1, 0, OPT_APPEND}, + +#define KEXEC_ALL_OPT_STR KEXEC_ARCH_OPT_STR + +#endif /* KEXEC_ARCH_CRIS_OPTIONS_H */ diff --git a/kexec/arch/cris/kexec-cris.c b/kexec/arch/cris/kexec-cris.c new file mode 100644 index 0000000..3b69709 --- /dev/null +++ b/kexec/arch/cris/kexec-cris.c @@ -0,0 +1,111 @@ +/* + * kexec-cris.c + * Copyright (C) 2008 AXIS Communications AB + * Written by Edgar E. Iglesias + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#include <stddef.h> +#include <stdio.h> +#include <errno.h> +#include <stdint.h> +#include <string.h> +#include <getopt.h> +#include "../../kexec.h" +#include "../../kexec-syscall.h" +#include "kexec-cris.h" +#include <arch/options.h> + +#define MAX_MEMORY_RANGES 64 +#define MAX_LINE 160 +static struct memory_range memory_range[MAX_MEMORY_RANGES]; + +/* Return a sorted list of memory ranges. */ +int get_memory_ranges(struct memory_range **range, int *ranges, + unsigned long UNUSED(kexec_flags)) +{ + int memory_ranges = 0; + + memory_range[memory_ranges].start = 0x40000000; + memory_range[memory_ranges].end = 0x41000000; + memory_range[memory_ranges].type = RANGE_RAM; + memory_ranges++; + + memory_range[memory_ranges].start = 0xc0000000; + memory_range[memory_ranges].end = 0xc1000000; + memory_range[memory_ranges].type = RANGE_RAM; + memory_ranges++; + + *range = memory_range; + *ranges = memory_ranges; + return 0; +} + +struct file_type file_type[] = { + {"elf-cris", elf_cris_probe, elf_cris_load, elf_cris_usage}, +}; +int file_types = sizeof(file_type) / sizeof(file_type[0]); + +void arch_usage(void) +{ +} + +int arch_process_options(int argc, char **argv) +{ + return 0; +} + +const struct arch_map_entry arches[] = { + { "cris", KEXEC_ARCH_CRIS }, + { "crisv32", KEXEC_ARCH_CRIS }, + { 0 }, +}; + +int arch_compat_trampoline(struct kexec_info *UNUSED(info)) +{ + return 0; +} + +void arch_update_purgatory(struct kexec_info *UNUSED(info)) +{ +} + +int is_crashkernel_mem_reserved(void) +{ + return 0; +} + +int get_crash_kernel_load_range(uint64_t *start, uint64_t *end) +{ + /* Crash kernel region size is not exposed by the system */ + return -1; +} + +unsigned long virt_to_phys(unsigned long addr) +{ + return (addr) & 0x7fffffff; +} + +/* + * add_segment() should convert base to a physical address on cris, + * while the default is just to work with base as is */ +void add_segment(struct kexec_info *info, const void *buf, size_t bufsz, + unsigned long base, size_t memsz) +{ + add_segment_phys_virt(info, buf, bufsz, base, memsz, 1); +} + +/* + * add_buffer() should convert base to a physical address on cris, + * while the default is just to work with base as is */ +unsigned long add_buffer(struct kexec_info *info, const void *buf, + unsigned long bufsz, unsigned long memsz, + unsigned long buf_align, unsigned long buf_min, + unsigned long buf_max, int buf_end) +{ + return add_buffer_phys_virt(info, buf, bufsz, memsz, buf_align, + buf_min, buf_max, buf_end, 1); +} + diff --git a/kexec/arch/cris/kexec-cris.h b/kexec/arch/cris/kexec-cris.h new file mode 100644 index 0000000..7ee9945 --- /dev/null +++ b/kexec/arch/cris/kexec-cris.h @@ -0,0 +1,9 @@ +#ifndef KEXEC_CRIS_H +#define KEXEC_CRIS_H + +int elf_cris_probe(const char *buf, off_t len); +int elf_cris_load(int argc, char **argv, const char *buf, off_t len, + struct kexec_info *info); +void elf_cris_usage(void); + +#endif /* KEXEC_CRIS_H */ diff --git a/kexec/arch/cris/kexec-elf-cris.c b/kexec/arch/cris/kexec-elf-cris.c new file mode 100644 index 0000000..7e251e6 --- /dev/null +++ b/kexec/arch/cris/kexec-elf-cris.c @@ -0,0 +1,133 @@ +/* + * kexec: Linux boots Linux + * + * Copyright (C) 2008 AXIS Communications AB + * Written by Edgar E. Iglesias + * + * Based on x86 implementation, + * Copyright (C) 2003-2005 Eric Biederman (ebiederm@xmission.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation (version 2 of the License). + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#define _GNU_SOURCE +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <limits.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <getopt.h> +#include <elf.h> +#include "../../kexec.h" +#include "../../kexec-syscall.h" +#include "../../kexec-elf.h" +#include "../../kexec-elf-boot.h" +#include <arch/options.h> +#include "kexec-cris.h" + +int elf_cris_probe(const char *buf, off_t len) +{ + struct mem_ehdr ehdr; + int result; + result = build_elf_exec_info(buf, len, &ehdr, 0); + if (result < 0) + goto out; + + /* Verify the architecuture specific bits */ + if (ehdr.e_machine != EM_CRIS) { + result = -1; + goto out; + } + + result = 0; + out: + free_elf_info(&ehdr); + return result; +} + +void elf_cris_usage(void) +{ + printf(" --append=STRING Set the kernel command line to STRING\n" + ); +} + +#define CRAMFS_MAGIC 0x28cd3d45 +#define JHEAD_MAGIC 0x1FF528A6 +#define JHEAD_SIZE 8 +#define RAM_INIT_MAGIC 0x56902387 +#define COMMAND_LINE_MAGIC 0x87109563 +#define NAND_BOOT_MAGIC 0x9a9db001 + +int elf_cris_load(int argc, char **argv, const char *buf, off_t len, + struct kexec_info *info) +{ + struct mem_ehdr ehdr; + char *command_line; + unsigned int *trampoline_buf; + unsigned long trampoline_base; + int opt; + extern void cris_trampoline(void); + extern unsigned long cris_trampoline_size; + extern struct regframe_t { + unsigned int regs[16]; + } cris_regframe; + + /* See options.h -- add any more there, too. */ + static const struct option options[] = { + KEXEC_ARCH_OPTIONS + {"append", 1, 0, OPT_APPEND}, + { 0, 0, 0, 0 }, + }; + + static const char short_options[] = KEXEC_OPT_STR ""; + + /* + * Parse the command line arguments + */ + command_line = 0; + while((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) { + switch(opt) { + default: + /* Ignore core options */ + if (opt < OPT_ARCH_MAX) { + break; + } + case OPT_APPEND: + command_line = optarg; + break; + } + } + + /* Load the ELF executable */ + elf_exec_build_load(info, &ehdr, buf, len, 0); + + cris_regframe.regs[0] = virt_to_phys(ehdr.e_entry); + cris_regframe.regs[8] = RAM_INIT_MAGIC; + cris_regframe.regs[12] = NAND_BOOT_MAGIC; + + trampoline_buf = xmalloc(cris_trampoline_size); + trampoline_base = add_buffer_virt(info, + trampoline_buf, + cris_trampoline_size, + cris_trampoline_size, + 4, 0, elf_max_addr(&ehdr), 1); + memcpy(trampoline_buf, + cris_trampoline, cris_trampoline_size); + info->entry = (void *)trampoline_base; + return 0; +} diff --git a/kexec/arch/cris/kexec-elf-rel-cris.c b/kexec/arch/cris/kexec-elf-rel-cris.c new file mode 100644 index 0000000..255cc2c --- /dev/null +++ b/kexec/arch/cris/kexec-elf-rel-cris.c @@ -0,0 +1,43 @@ +/* + * kexec-elf-rel-cris.c - kexec Elf relocation routines + * Copyright (C) 2008 AXIS Communications AB + * Written by Edgar E. Iglesias + * + * derived from ../ppc/kexec-elf-rel-ppc.c + * Copyright (C) 2004 Albert Herranz + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. +*/ + +#include <stdio.h> +#include <elf.h> +#include "../../kexec.h" +#include "../../kexec-elf.h" + +int machine_verify_elf_rel(struct mem_ehdr *ehdr) +{ + if (ehdr->ei_data != ELFDATA2MSB) { + return 0; + } + if (ehdr->ei_class != ELFCLASS32) { + return 0; + } + if (ehdr->e_machine != EM_CRIS) { + return 0; + } + return 1; +} + +void machine_apply_elf_rel(struct mem_ehdr *UNUSED(ehdr), + struct mem_sym *UNUSED(sym), unsigned long r_type, void *location, + unsigned long address, unsigned long value) +{ + switch(r_type) { + + default: + die("Unknown rela relocation: %lu\n", r_type); + break; + } + return; +} diff --git a/kexec/arch/hppa/Makefile b/kexec/arch/hppa/Makefile new file mode 100644 index 0000000..e98f6da --- /dev/null +++ b/kexec/arch/hppa/Makefile @@ -0,0 +1,13 @@ +# +# kexec hppa (linux booting linux) +# +hppa_KEXEC_SRCS = kexec/arch/hppa/kexec-hppa.c +hppa_KEXEC_SRCS += kexec/arch/hppa/kexec-elf-hppa.c +hppa_KEXEC_SRCS += kexec/arch/hppa/kexec-elf-rel-hppa.c + +hppa_ADD_SEGMENT = +hppa_VIRT_TO_PHYS = + +dist += kexec/arch/hppa/Makefile $(hppa_KEXEC_SRCS) \ + kexec/arch/hppa/kexec-hppa.h \ + kexec/arch/hppa/include/arch/options.h diff --git a/kexec/arch/hppa/include/arch/options.h b/kexec/arch/hppa/include/arch/options.h new file mode 100644 index 0000000..e9deb51 --- /dev/null +++ b/kexec/arch/hppa/include/arch/options.h @@ -0,0 +1,32 @@ +#ifndef KEXEC_ARCH_HPPA_OPTIONS_H +#define KEXEC_ARCH_HPPA_OPTIONS_H + +#define OPT_ARCH_MAX (OPT_MAX+0) + +#define KEXEC_ARCH_OPTIONS \ + KEXEC_OPTIONS + +#define KEXEC_ARCH_OPT_STR KEXEC_OPT_STR "" + +#define KEXEC_ALL_OPTIONS \ + KEXEC_ARCH_OPTIONS \ + { "command-line", 1, 0, OPT_APPEND }, \ + { "reuse-cmdline", 0, 0, OPT_REUSE_CMDLINE }, \ + { "append", 1, 0, OPT_APPEND }, \ + { "initrd", 1, 0, OPT_RAMDISK }, \ + { "ramdisk", 1, 0, OPT_RAMDISK }, + + +#define KEXEC_ALL_OPT_STR KEXEC_ARCH_OPT_STR "" + +/* See the other architectures for details of these; HPPA has no + * loader-specific options yet. + */ +#define OPT_ARCH_MAX (OPT_MAX+0) + +#define OPT_APPEND (OPT_ARCH_MAX+0) +#define OPT_REUSE_CMDLINE (OPT_ARCH_MAX+1) +#define OPT_RAMDISK (OPT_ARCH_MAX+2) + +#define MAX_MEMORY_RANGES 16 +#endif /* KEXEC_ARCH_HPPA_OPTIONS_H */ diff --git a/kexec/arch/hppa/kexec-elf-hppa.c b/kexec/arch/hppa/kexec-elf-hppa.c new file mode 100644 index 0000000..474a919 --- /dev/null +++ b/kexec/arch/hppa/kexec-elf-hppa.c @@ -0,0 +1,159 @@ +/* + * kexec-elf-hppa.c - kexec Elf loader for hppa + * + * Copyright (c) 2019 Sven Schnelle <svens@stackframe.org> + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. +*/ + +#define _GNU_SOURCE +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <getopt.h> +#include <elf.h> +#include <boot/elf_boot.h> +#include <ip_checksum.h> +#include "../../kexec.h" +#include "../../kexec-elf.h" +#include "../../kexec-syscall.h" +#include "kexec-hppa.h" +#include <arch/options.h> + +#define PAGE_SIZE 4096 + +extern unsigned long phys_offset; + +int elf_hppa_probe(const char *buf, off_t len) +{ + struct mem_ehdr ehdr; + int result; + result = build_elf_exec_info(buf, len, &ehdr, 0); + if (result < 0) + goto out; + + phys_offset = ehdr.e_entry & 0xf0000000; + /* Verify the architecuture specific bits */ + if (ehdr.e_machine != EM_PARISC) { + /* for a different architecture */ + fprintf(stderr, "Not for this architecture.\n"); + result = -1; + goto out; + } + result = 0; + out: + free_elf_info(&ehdr); + return result; +} + +void elf_hppa_usage(void) +{ + printf(" --command-line=STRING Set the kernel command line to STRING\n" + " --append=STRING Set the kernel command line to STRING\n" + " --reuse-cmdline Use kernel command line from running system.\n" + " --ramdisk=FILE Use FILE as the kernel's initial ramdisk.\n" + " --initrd=FILE Use FILE as the kernel's initial ramdisk.\n" + ); +} + +int elf_hppa_load(int argc, char **argv, const char *buf, off_t len, + struct kexec_info *info) +{ + struct mem_ehdr ehdr; + char *cmdline = NULL, *ramdisk = NULL; + int opt, result, i; + unsigned long ramdisk_addr = 0; + off_t ramdisk_size = 0; + + static const struct option options[] = { + KEXEC_ALL_OPTIONS + { 0, 0, NULL, 0 }, + }; + + static const char short_options[] = KEXEC_ALL_OPT_STR; + + while ((opt = getopt_long(argc, argv, short_options, options, 0)) != + -1) { + switch (opt) { + default: + /* Ignore core options */ + if (opt < OPT_ARCH_MAX) + break; + case OPT_APPEND: + cmdline = strdup(optarg); + break; + case OPT_REUSE_CMDLINE: + cmdline = get_command_line(); + break; + case OPT_RAMDISK: + ramdisk = optarg; + break; + } + } + + if (info->file_mode) { + if (cmdline) { + info->command_line = cmdline; + info->command_line_len = strlen(cmdline) + 1; + } + + if (ramdisk) { + info->initrd_fd = open(ramdisk, O_RDONLY); + if (info->initrd_fd == -1) { + fprintf(stderr, "Could not open initrd file " + "%s:%s\n", ramdisk, strerror(errno)); + return -1; + } + } + return 0; + } + + result = build_elf_exec_info(buf, len, &ehdr, 0); + if (result < 0) + die("ELF exec parse failed\n"); + + /* Fixup PT_LOAD segments that include the ELF header (offset zero) */ + for (i = 0; i < ehdr.e_phnum; i++) { + struct mem_phdr *phdr; + phdr = &ehdr.e_phdr[i]; + if (phdr->p_type != PT_LOAD || phdr->p_offset) + continue; + + dbgprintf("Removing ELF header from segment %d\n", i); + phdr->p_paddr += PAGE_SIZE; + phdr->p_vaddr += PAGE_SIZE; + phdr->p_filesz -= PAGE_SIZE; + phdr->p_memsz -= PAGE_SIZE; + phdr->p_offset += PAGE_SIZE; + phdr->p_data += PAGE_SIZE; + } + + /* Load the ELF data */ + result = elf_exec_load(&ehdr, info); + if (result < 0) + die("ELF exec load failed\n"); + + info->entry = (void *)virt_to_phys(ehdr.e_entry); + + + /* Load ramdisk */ + if (ramdisk) { + void *initrd = slurp_decompress_file(ramdisk, &ramdisk_size); + /* Store ramdisk at top of first memory chunk */ + ramdisk_addr = _ALIGN_DOWN(info->memory_range[0].end - + ramdisk_size + 1, PAGE_SIZE); + if (!buf) + die("Ramdisk load failed\n"); + add_buffer(info, initrd, ramdisk_size, ramdisk_size, + PAGE_SIZE, ramdisk_addr, info->memory_range[0].end, + 1); + } + + return 0; +} diff --git a/kexec/arch/hppa/kexec-elf-rel-hppa.c b/kexec/arch/hppa/kexec-elf-rel-hppa.c new file mode 100644 index 0000000..661b67b --- /dev/null +++ b/kexec/arch/hppa/kexec-elf-rel-hppa.c @@ -0,0 +1,37 @@ +/* + * kexec-elf-rel-hppa.c - kexec Elf relocation routines + * + * Copyright (C) 2019 Sven Schnelle <svens@stackframe.org> + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. +*/ + +#include <stdio.h> +#include <elf.h> +#include "../../kexec.h" +#include "../../kexec-elf.h" + +int machine_verify_elf_rel(struct mem_ehdr *ehdr) +{ + if (ehdr->ei_data != ELFDATA2MSB) + return 0; + if (ehdr->e_machine != EM_PARISC) + return 0; + return 1; +} + +void machine_apply_elf_rel(struct mem_ehdr *UNUSED(ehdr), + struct mem_sym *UNUSED(sym), + unsigned long r_type, + void *UNUSED(location), + unsigned long UNUSED(address), + unsigned long UNUSED(value)) +{ + switch (r_type) { + default: + die("Unknown rela relocation: %lu\n", r_type); + break; + } + return; +} diff --git a/kexec/arch/hppa/kexec-hppa.c b/kexec/arch/hppa/kexec-hppa.c new file mode 100644 index 0000000..77c9739 --- /dev/null +++ b/kexec/arch/hppa/kexec-hppa.c @@ -0,0 +1,148 @@ +/* + * kexec-hppa.c - kexec for hppa + * + * Copyright (C) 2019 Sven Schnelle <svens@stackframe.org> + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#include <stddef.h> +#include <stdio.h> +#include <errno.h> +#include <stdint.h> +#include <string.h> +#include <getopt.h> +#include "../../kexec.h" +#include "../../kexec-syscall.h" +#include "kexec-hppa.h" +#include <arch/options.h> + +#define SYSTEM_RAM "System RAM\n" +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + +static struct memory_range memory_range[MAX_MEMORY_RANGES]; +unsigned long phys_offset; + +/* Return a sorted list of available memory ranges. */ +int get_memory_ranges(struct memory_range **range, int *ranges, + unsigned long UNUSED(kexec_flags)) +{ + const char *iomem = proc_iomem(); + int memory_ranges = 0; + char line[512]; + FILE *fp; + + fp = fopen(iomem, "r"); + + if (!fp) { + fprintf(stderr, "Cannot open %s: %s\n", + iomem, strerror(errno)); + return -1; + } + + while(fgets(line, sizeof(line), fp) != 0) { + unsigned long long start, end; + char *str; + int type; + int consumed; + int count; + + + count = sscanf(line, "%llx-%llx : %n", &start, &end, &consumed); + + if (count != 2) + continue; + + str = line + consumed; + + if (memcmp(str, SYSTEM_RAM, strlen(SYSTEM_RAM)) == 0) { + type = RANGE_RAM; + } else if (memcmp(str, "reserved\n", 9) == 0) { + type = RANGE_RESERVED; + } else { + continue; + } + + memory_range[memory_ranges].start = start; + memory_range[memory_ranges].end = end; + memory_range[memory_ranges].type = type; + if (++memory_ranges >= MAX_MEMORY_RANGES) + break; + } + fclose(fp); + *range = memory_range; + *ranges = memory_ranges; + + dbgprint_mem_range("MEMORY RANGES", *range, *ranges); + return 0; +} + +struct file_type file_type[] = { + {"elf-hppa", elf_hppa_probe, elf_hppa_load, elf_hppa_usage}, +}; +int file_types = ARRAY_SIZE(file_type); + +void arch_usage(void) +{ +} + +int arch_process_options(int argc, char **argv) +{ + static const struct option options[] = { + KEXEC_ALL_OPTIONS + { 0, 0, NULL, 0 }, + }; + static const char short_options[] = KEXEC_ALL_OPT_STR; + int opt; + + opterr = 0; /* Don't complain about unrecognized options here */ + while ((opt = getopt_long(argc, argv, short_options, options, 0)) != + -1) { + switch (opt) { + default: + break; + } + } + /* Reset getopt for the next pass; called in other source modules */ + opterr = 1; + optind = 1; + return 0; +} + +const struct arch_map_entry arches[] = { + { "parisc64", KEXEC_ARCH_HPPA }, + { "parisc", KEXEC_ARCH_HPPA }, + { NULL, 0 }, +}; + +int arch_compat_trampoline(struct kexec_info *UNUSED(info)) +{ + return 0; +} + +void arch_update_purgatory(struct kexec_info *UNUSED(info)) +{ +} + +int is_crashkernel_mem_reserved(void) +{ + return 0; +} + +int get_crash_kernel_load_range(uint64_t *start, uint64_t *end) +{ + /* Crash kernel region size is not exposed by the system */ + return -1; +} + +void add_segment(struct kexec_info *info, const void *buf, size_t bufsz, + unsigned long base, size_t memsz) +{ + add_segment_phys_virt(info, buf, bufsz, base, memsz, 1); +} + +unsigned long virt_to_phys(unsigned long addr) +{ + return addr - phys_offset; +} diff --git a/kexec/arch/hppa/kexec-hppa.h b/kexec/arch/hppa/kexec-hppa.h new file mode 100644 index 0000000..485e5b6 --- /dev/null +++ b/kexec/arch/hppa/kexec-hppa.h @@ -0,0 +1,9 @@ +#ifndef KEXEC_HPPA_H +#define KEXEC_HPPA_H + +int elf_hppa_probe(const char *buf, off_t len); +int elf_hppa_load(int argc, char **argv, const char *buf, off_t len, + struct kexec_info *info); +void elf_hppa_usage(void); + +#endif /* KEXEC_HPPA_H */ diff --git a/kexec/arch/i386/Makefile b/kexec/arch/i386/Makefile new file mode 100644 index 0000000..f486103 --- /dev/null +++ b/kexec/arch/i386/Makefile @@ -0,0 +1,20 @@ +# +# kexec i386 (linux booting linux) +# +i386_KEXEC_SRCS = kexec/arch/i386/kexec-x86.c +i386_KEXEC_SRCS += kexec/arch/i386/kexec-x86-common.c +i386_KEXEC_SRCS += kexec/arch/i386/kexec-elf-x86.c +i386_KEXEC_SRCS += kexec/arch/i386/kexec-elf-rel-x86.c +i386_KEXEC_SRCS += kexec/arch/i386/kexec-bzImage.c +i386_KEXEC_SRCS += kexec/arch/i386/kexec-multiboot-x86.c +i386_KEXEC_SRCS += kexec/arch/i386/kexec-mb2-x86.c +i386_KEXEC_SRCS += kexec/arch/i386/kexec-beoboot-x86.c +i386_KEXEC_SRCS += kexec/arch/i386/kexec-nbi.c +i386_KEXEC_SRCS += kexec/arch/i386/x86-linux-setup.c +i386_KEXEC_SRCS += kexec/arch/i386/crashdump-x86.c + +dist += kexec/arch/i386/Makefile $(i386_KEXEC_SRCS) \ + kexec/arch/i386/crashdump-x86.h \ + kexec/arch/i386/kexec-x86.h \ + kexec/arch/i386/x86-linux-setup.h \ + kexec/arch/i386/include/arch/options.h diff --git a/kexec/arch/i386/crashdump-x86.c b/kexec/arch/i386/crashdump-x86.c new file mode 100644 index 0000000..df1f24c --- /dev/null +++ b/kexec/arch/i386/crashdump-x86.c @@ -0,0 +1,1049 @@ +/* + * kexec: Linux boots Linux + * + * Created by: Vivek Goyal (vgoyal@in.ibm.com) + * old x86_64 version Created by: Murali M Chakravarthy (muralim@in.ibm.com) + * Copyright (C) IBM Corporation, 2005. All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation (version 2 of the License). + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#define _XOPEN_SOURCE 600 +#define _BSD_SOURCE +#define _DEFAULT_SOURCE + +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <limits.h> +#include <elf.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <dirent.h> +#include "../../kexec.h" +#include "../../kexec-elf.h" +#include "../../kexec-syscall.h" +#include "../../firmware_memmap.h" +#include "../../crashdump.h" +#include "kexec-x86.h" +#include "crashdump-x86.h" +#include "../../kexec-xen.h" +#include "x86-linux-setup.h" +#include <x86/x86-linux.h> + +extern struct arch_options_t arch_options; + +static int get_kernel_page_offset(struct kexec_info *UNUSED(info), + struct crash_elf_info *elf_info) +{ + + if (elf_info->machine == EM_X86_64) { + /* get_kernel_vaddr_and_size will override this */ + elf_info->page_offset = X86_64_PAGE_OFFSET; + } + else if (elf_info->machine == EM_386) { + elf_info->page_offset = X86_PAGE_OFFSET; + } + + return 0; +} + +#define X86_64_KERN_VADDR_ALIGN 0x100000 /* 1MB */ + +/* Read kernel physical load addr from the file returned by proc_iomem() + * (Kernel Code) and store in kexec_info */ +static int get_kernel_paddr(struct kexec_info *UNUSED(info), + struct crash_elf_info *elf_info) +{ + uint64_t start; + + if (elf_info->machine != EM_X86_64) + return 0; + + if (xen_present()) /* Kernel not entity mapped under Xen */ + return 0; + + if (parse_iomem_single("Kernel code\n", &start, NULL) == 0) { + elf_info->kern_paddr_start = start; + dbgprintf("kernel load physical addr start = 0x%016Lx\n", + (unsigned long long)start); + return 0; + } + + fprintf(stderr, "Cannot determine kernel physical load addr\n"); + return -1; +} + +/* Retrieve info regarding virtual address kernel has been compiled for and + * size of the kernel from /proc/kcore. Current /proc/kcore parsing from + * from kexec-tools fails because of malformed elf notes. A kernel patch has + * been submitted. For the folks using older kernels, this function + * hard codes the values to remain backward compatible. Once things stablize + * we should get rid of backward compatible code. */ + +static int get_kernel_vaddr_and_size(struct kexec_info *UNUSED(info), + struct crash_elf_info *elf_info) +{ + int result; + const char kcore[] = "/proc/kcore"; + char *buf; + struct mem_ehdr ehdr; + struct mem_phdr *phdr, *end_phdr; + int align; + off_t size; + uint32_t elf_flags = 0; + uint64_t stext_sym; + const unsigned long long pud_mask = ~((1 << 30) - 1); + unsigned long long vaddr, lowest_vaddr = 0; + + if (elf_info->machine != EM_X86_64) + return 0; + + if (xen_present()) /* Kernel not entity mapped under Xen */ + return 0; + + align = getpagesize(); + buf = slurp_file_len(kcore, KCORE_ELF_HEADERS_SIZE, &size); + if (!buf) { + fprintf(stderr, "Cannot read %s: %s\n", kcore, strerror(errno)); + return -1; + } + + /* Don't perform checks to make sure stated phdrs and shdrs are + * actually present in the core file. It is not practical + * to read the GB size file into a user space buffer, Given the + * fact that we don't use any info from that. + */ + elf_flags |= ELF_SKIP_FILESZ_CHECK; + result = build_elf_core_info(buf, size, &ehdr, elf_flags); + if (result < 0) { + /* Perhaps KCORE_ELF_HEADERS_SIZE is too small? */ + fprintf(stderr, "ELF core (kcore) parse failed\n"); + return -1; + } + + end_phdr = &ehdr.e_phdr[ehdr.e_phnum]; + + /* Search for the real PAGE_OFFSET when KASLR memory randomization + * is enabled */ + for(phdr = ehdr.e_phdr; phdr != end_phdr; phdr++) { + if (phdr->p_type == PT_LOAD) { + vaddr = phdr->p_vaddr & pud_mask; + if (lowest_vaddr == 0 || lowest_vaddr > vaddr) + lowest_vaddr = vaddr; + } + } + if (lowest_vaddr != 0) + elf_info->page_offset = lowest_vaddr; + + /* Traverse through the Elf headers and find the region where + * _stext symbol is located in. That's where kernel is mapped */ + stext_sym = get_kernel_sym("_stext"); + for(phdr = ehdr.e_phdr; stext_sym && phdr != end_phdr; phdr++) { + if (phdr->p_type == PT_LOAD) { + unsigned long long saddr = phdr->p_vaddr; + unsigned long long eaddr = phdr->p_vaddr + phdr->p_memsz; + unsigned long long size; + + /* Look for kernel text mapping header. */ + if (saddr <= stext_sym && eaddr > stext_sym) { + saddr = _ALIGN_DOWN(saddr, X86_64_KERN_VADDR_ALIGN); + elf_info->kern_vaddr_start = saddr; + size = eaddr - saddr; + /* Align size to page size boundary. */ + size = _ALIGN(size, align); + elf_info->kern_size = size; + dbgprintf("kernel vaddr = 0x%llx size = 0x%llx\n", + saddr, size); + return 0; + } + } + } + + /* If failed to retrieve kernel text mapping through + * /proc/kallsyms, Traverse through the Elf headers again and + * find the region where kernel is mapped using hard-coded + * kernel mapping boundries */ + for(phdr = ehdr.e_phdr; phdr != end_phdr; phdr++) { + if (phdr->p_type == PT_LOAD) { + unsigned long long saddr = phdr->p_vaddr; + unsigned long long eaddr = phdr->p_vaddr + phdr->p_memsz; + unsigned long long size; + + /* Look for kernel text mapping header. */ + if ((saddr >= X86_64__START_KERNEL_map) && + (eaddr <= X86_64__START_KERNEL_map + X86_64_KERNEL_TEXT_SIZE)) { + saddr = _ALIGN_DOWN(saddr, X86_64_KERN_VADDR_ALIGN); + elf_info->kern_vaddr_start = saddr; + size = eaddr - saddr; + /* Align size to page size boundary. */ + size = _ALIGN(size, align); + elf_info->kern_size = size; + dbgprintf("kernel vaddr = 0x%llx size = 0x%llx\n", + saddr, size); + return 0; + } + } + } + + fprintf(stderr, "Can't find kernel text map area from kcore\n"); + return -1; +} + +/* Forward Declaration. */ +static void segregate_lowmem_region(int *nr_ranges, unsigned long lowmem_limit); +static int exclude_region(int *nr_ranges, uint64_t start, uint64_t end); + +/* Stores a sorted list of RAM memory ranges for which to create elf headers. + * A separate program header is created for backup region */ +static struct memory_range crash_memory_range[CRASH_MAX_MEMORY_RANGES]; + +/* Memory region reserved for storing panic kernel and other data. */ +#define CRASH_RESERVED_MEM_NR 8 +static struct memory_range crash_reserved_mem[CRASH_RESERVED_MEM_NR]; +static int crash_reserved_mem_nr; + +/* Reads the appropriate file and retrieves the SYSTEM RAM regions for whom to + * create Elf headers. Keeping it separate from get_memory_ranges() as + * requirements are different in the case of normal kexec and crashdumps. + * + * Normal kexec needs to look at all of available physical memory irrespective + * of the fact how much of it is being used by currently running kernel. + * Crashdumps need to have access to memory regions actually being used by + * running kernel. Expecting a different file/data structure than /proc/iomem + * to look into down the line. May be something like /proc/kernelmem or may + * be zone data structures exported from kernel. + */ +static int get_crash_memory_ranges(struct memory_range **range, int *ranges, + int kexec_flags, unsigned long lowmem_limit) +{ + const char *iomem = proc_iomem(); + int memory_ranges = 0, gart = 0, i; + char line[MAX_LINE]; + FILE *fp; + unsigned long long start, end; + uint64_t gart_start = 0, gart_end = 0; + + fp = fopen(iomem, "r"); + if (!fp) { + fprintf(stderr, "Cannot open %s: %s\n", + iomem, strerror(errno)); + return -1; + } + + while(fgets(line, sizeof(line), fp) != 0) { + char *str; + int type, consumed, count; + + if (memory_ranges >= CRASH_MAX_MEMORY_RANGES) + break; + count = sscanf(line, "%llx-%llx : %n", + &start, &end, &consumed); + if (count != 2) + continue; + str = line + consumed; + dbgprintf("%016llx-%016llx : %s", + start, end, str); + /* + * We want to dump any System RAM -- memory regions currently + * used by the kernel. In the usual case, this is "System RAM" + * on the top level. However, we can also have "System RAM + * (virtio_mem)" below virtio devices or "System RAM (kmem)" + * below "Persistent Memory". + */ + if (strstr(str, "System RAM")) { + type = RANGE_RAM; + } else if (memcmp(str, "ACPI Tables\n", 12) == 0) { + /* + * ACPI Tables area need to be passed to new + * kernel with appropriate memmap= option. This + * is needed so that x86_64 kernel creates linear + * mapping for this region which is required for + * initializing acpi tables in second kernel. + */ + type = RANGE_ACPI; + } else if(memcmp(str,"ACPI Non-volatile Storage\n",26) == 0 ) { + type = RANGE_ACPI_NVS; + } else if(memcmp(str,"Persistent Memory (legacy)\n",27) == 0 ) { + type = RANGE_PRAM; + } else if(memcmp(str,"Persistent Memory\n",18) == 0 ) { + type = RANGE_PMEM; + } else if(memcmp(str,"reserved\n",9) == 0 ) { + type = RANGE_RESERVED; + } else if (memcmp(str, "Reserved\n", 9) == 0) { + type = RANGE_RESERVED; + } else if (memcmp(str, "GART\n", 5) == 0) { + gart_start = start; + gart_end = end; + gart = 1; + continue; + } else { + continue; + } + + crash_memory_range[memory_ranges].start = start; + crash_memory_range[memory_ranges].end = end; + crash_memory_range[memory_ranges].type = type; + + segregate_lowmem_region(&memory_ranges, lowmem_limit); + + memory_ranges++; + } + fclose(fp); + if (kexec_flags & KEXEC_PRESERVE_CONTEXT) { + for (i = 0; i < memory_ranges; i++) { + if (crash_memory_range[i].end > 0x0009ffff) { + crash_reserved_mem[0].start = \ + crash_memory_range[i].start; + break; + } + } + if (crash_reserved_mem[0].start >= mem_max) { + fprintf(stderr, "Too small mem_max: 0x%llx.\n", + mem_max); + return -1; + } + crash_reserved_mem[0].end = mem_max; + crash_reserved_mem[0].type = RANGE_RAM; + crash_reserved_mem_nr = 1; + } + + for (i = 0; i < crash_reserved_mem_nr; i++) + if (exclude_region(&memory_ranges, crash_reserved_mem[i].start, + crash_reserved_mem[i].end) < 0) + return -1; + + if (gart) { + /* exclude GART region if the system has one */ + if (exclude_region(&memory_ranges, gart_start, gart_end) < 0) + return -1; + } + *range = crash_memory_range; + *ranges = memory_ranges; + + return 0; +} + +#ifdef HAVE_LIBXENCTRL +static int get_crash_memory_ranges_xen(struct memory_range **range, + int *ranges, unsigned long lowmem_limit) +{ + struct e820entry *e820entries; + int j, rc, ret = -1; + unsigned int i; + xc_interface *xc; + + xc = xc_interface_open(NULL, NULL, 0); + + if (!xc) { + fprintf(stderr, "%s: Failed to open Xen control interface\n", __func__); + return -1; + } + + e820entries = xmalloc(sizeof(*e820entries) * CRASH_MAX_MEMORY_RANGES); + + rc = xc_get_machine_memory_map(xc, e820entries, CRASH_MAX_MEMORY_RANGES); + + if (rc < 0) { + fprintf(stderr, "%s: xc_get_machine_memory_map: %s\n", __func__, strerror(-rc)); + goto err; + } + + for (i = 0, j = 0; i < rc && j < CRASH_MAX_MEMORY_RANGES; ++i, ++j) { + crash_memory_range[j].start = e820entries[i].addr; + crash_memory_range[j].end = e820entries[i].addr + e820entries[i].size - 1; + crash_memory_range[j].type = xen_e820_to_kexec_type(e820entries[i].type); + segregate_lowmem_region(&j, lowmem_limit); + } + + *range = crash_memory_range; + *ranges = j; + + qsort(*range, *ranges, sizeof(struct memory_range), compare_ranges); + + for (i = 0; i < crash_reserved_mem_nr; i++) + if (exclude_region(ranges, crash_reserved_mem[i].start, + crash_reserved_mem[i].end) < 0) + goto err; + + ret = 0; + +err: + xc_interface_close(xc); + free(e820entries); + return ret; +} +#else +static int get_crash_memory_ranges_xen(struct memory_range **range, + int *ranges, unsigned long lowmem_limit) +{ + return 0; +} +#endif /* HAVE_LIBXENCTRL */ + +static void segregate_lowmem_region(int *nr_ranges, unsigned long lowmem_limit) +{ + unsigned long long end, start; + unsigned type; + + start = crash_memory_range[*nr_ranges].start; + end = crash_memory_range[*nr_ranges].end; + type = crash_memory_range[*nr_ranges].type; + + if (!(lowmem_limit && lowmem_limit > start && lowmem_limit < end)) + return; + + crash_memory_range[*nr_ranges].end = lowmem_limit - 1; + + if (*nr_ranges >= CRASH_MAX_MEMORY_RANGES - 1) + return; + + ++*nr_ranges; + + crash_memory_range[*nr_ranges].start = lowmem_limit; + crash_memory_range[*nr_ranges].end = end; + crash_memory_range[*nr_ranges].type = type; +} + +/* Removes crash reserve region from list of memory chunks for whom elf program + * headers have to be created. Assuming crash reserve region to be a single + * continuous area fully contained inside one of the memory chunks */ +static int exclude_region(int *nr_ranges, uint64_t start, uint64_t end) +{ + int i, j, tidx = -1; + struct memory_range temp_region = {0, 0, 0}; + + + for (i = 0; i < (*nr_ranges); i++) { + unsigned long long mstart, mend; + mstart = crash_memory_range[i].start; + mend = crash_memory_range[i].end; + if (start < mend && end > mstart) { + if (start != mstart && end != mend) { + /* Split memory region */ + crash_memory_range[i].end = start - 1; + temp_region.start = end + 1; + temp_region.end = mend; + temp_region.type = RANGE_RAM; + tidx = i+1; + } else if (start != mstart) + crash_memory_range[i].end = start - 1; + else + crash_memory_range[i].start = end + 1; + } + } + /* Insert split memory region, if any. */ + if (tidx >= 0) { + if (*nr_ranges == CRASH_MAX_MEMORY_RANGES) { + /* No space to insert another element. */ + fprintf(stderr, "Error: Number of crash memory ranges" + " excedeed the max limit\n"); + return -1; + } + for (j = (*nr_ranges - 1); j >= tidx; j--) + crash_memory_range[j+1] = crash_memory_range[j]; + crash_memory_range[tidx] = temp_region; + (*nr_ranges)++; + } + return 0; +} + +/* Adds a segment from list of memory regions which new kernel can use to + * boot. Segment start and end should be aligned to 1K boundary. */ +static int add_memmap(struct memory_range *memmap_p, int *nr_memmap, + unsigned long long addr, size_t size, int type) +{ + int i, j, nr_entries = 0, tidx = 0, align = 1024; + unsigned long long mstart, mend; + + /* Shrink to 1KiB alignment if needed. */ + if (type == RANGE_RAM && ((addr%align) || (size%align))) { + unsigned long long end = addr + size; + + printf("%s: RAM chunk %#llx - %#llx unaligned\n", __func__, addr, end); + addr = _ALIGN_UP(addr, align); + end = _ALIGN_DOWN(end, align); + if (addr >= end) + return -1; + size = end - addr; + printf("%s: RAM chunk shrunk to %#llx - %#llx\n", __func__, addr, end); + } + + /* Make sure at least one entry in list is free. */ + for (i = 0; i < CRASH_MAX_MEMMAP_NR; i++) { + mstart = memmap_p[i].start; + mend = memmap_p[i].end; + if (!mstart && !mend) + break; + else + nr_entries++; + } + if (nr_entries == CRASH_MAX_MEMMAP_NR) + return -1; + + for (i = 0; i < CRASH_MAX_MEMMAP_NR; i++) { + mstart = memmap_p[i].start; + mend = memmap_p[i].end; + if (mstart == 0 && mend == 0) + break; + if (mstart <= (addr+size-1) && mend >=addr) + /* Overlapping region. */ + return -1; + else if (addr > mend) + tidx = i+1; + } + /* Insert the memory region. */ + for (j = nr_entries-1; j >= tidx; j--) + memmap_p[j+1] = memmap_p[j]; + memmap_p[tidx].start = addr; + memmap_p[tidx].end = addr + size - 1; + memmap_p[tidx].type = type; + *nr_memmap = nr_entries + 1; + + dbgprint_mem_range("Memmap after adding segment", memmap_p, *nr_memmap); + + return 0; +} + +/* Removes a segment from list of memory regions which new kernel can use to + * boot. Segment start and end should be aligned to 1K boundary. */ +static int delete_memmap(struct memory_range *memmap_p, int *nr_memmap, + unsigned long long addr, size_t size) +{ + int i, j, nr_entries = 0, tidx = -1, operation = 0, align = 1024; + unsigned long long mstart, mend; + struct memory_range temp_region; + + /* Do alignment check. */ + if ((addr%align) || (size%align)) + return -1; + + /* Make sure at least one entry in list is free. */ + for (i = 0; i < CRASH_MAX_MEMMAP_NR; i++) { + mstart = memmap_p[i].start; + mend = memmap_p[i].end; + if (!mstart && !mend) + break; + else + nr_entries++; + } + if (nr_entries == CRASH_MAX_MEMMAP_NR) + /* List if full */ + return -1; + + for (i = 0; i < CRASH_MAX_MEMMAP_NR; i++) { + mstart = memmap_p[i].start; + mend = memmap_p[i].end; + if (mstart == 0 && mend == 0) + /* Did not find the segment in the list. */ + return -1; + if (mstart <= addr && mend >= (addr + size - 1)) { + if (mstart == addr && mend == (addr + size - 1)) { + /* Exact match. Delete region */ + operation = -1; + tidx = i; + break; + } + if (mstart != addr && mend != (addr + size - 1)) { + /* Split in two */ + memmap_p[i].end = addr - 1; + temp_region.start = addr + size; + temp_region.end = mend; + temp_region.type = memmap_p[i].type; + operation = 1; + tidx = i; + break; + } + + /* No addition/deletion required. Adjust the existing.*/ + if (mstart != addr) { + memmap_p[i].end = addr - 1; + break; + } else { + memmap_p[i].start = addr + size; + break; + } + } + } + if ((operation == 1) && tidx >=0) { + /* Insert the split memory region. */ + for (j = nr_entries-1; j > tidx; j--) + memmap_p[j+1] = memmap_p[j]; + memmap_p[tidx+1] = temp_region; + *nr_memmap = nr_entries + 1; + } + if ((operation == -1) && tidx >=0) { + /* Delete the exact match memory region. */ + for (j = i+1; j < CRASH_MAX_MEMMAP_NR; j++) + memmap_p[j-1] = memmap_p[j]; + memmap_p[j-1].start = memmap_p[j-1].end = 0; + *nr_memmap = nr_entries - 1; + } + + dbgprint_mem_range("Memmap after deleting segment", memmap_p, *nr_memmap); + + return 0; +} + +static void cmdline_add_memmap_internal(char *cmdline, unsigned long startk, + unsigned long endk, int type) +{ + int cmdlen, len; + char str_mmap[256], str_tmp[20]; + + strcpy (str_mmap, " memmap="); + ultoa((endk-startk), str_tmp); + strcat (str_mmap, str_tmp); + + if (type == RANGE_RAM) + strcat (str_mmap, "K@"); + else if (type == RANGE_RESERVED) + strcat (str_mmap, "K$"); + else if (type == RANGE_ACPI || type == RANGE_ACPI_NVS) + strcat (str_mmap, "K#"); + else if (type == RANGE_PRAM) + strcat (str_mmap, "K!"); + + ultoa(startk, str_tmp); + strcat (str_mmap, str_tmp); + strcat (str_mmap, "K"); + len = strlen(str_mmap); + cmdlen = strlen(cmdline) + len; + if (cmdlen > (COMMAND_LINE_SIZE - 1)) + die("Command line overflow\n"); + strcat(cmdline, str_mmap); +} + +/* Adds the appropriate memmap= options to command line, indicating the + * memory regions the new kernel can use to boot into. */ +static int cmdline_add_memmap(char *cmdline, struct memory_range *memmap_p) +{ + int i, cmdlen, len; + unsigned long min_sizek = 100; + char str_mmap[256]; + + /* Exact map */ + strcpy(str_mmap, " memmap=exactmap"); + len = strlen(str_mmap); + cmdlen = strlen(cmdline) + len; + if (cmdlen > (COMMAND_LINE_SIZE - 1)) + die("Command line overflow\n"); + strcat(cmdline, str_mmap); + + for (i = 0; i < CRASH_MAX_MEMMAP_NR; i++) { + unsigned long startk, endk, type; + + startk = memmap_p[i].start/1024; + endk = (memmap_p[i].end + 1)/1024; + type = memmap_p[i].type; + + /* Only adding memory regions of RAM and ACPI and Persistent Mem */ + if (type != RANGE_RAM && + type != RANGE_ACPI && + type != RANGE_ACPI_NVS && + type != RANGE_PRAM) + continue; + + if (type == RANGE_ACPI || type == RANGE_ACPI_NVS) + endk = _ALIGN_UP(memmap_p[i].end + 1, 1024)/1024; + + if (!startk && !endk) + /* All regions traversed. */ + break; + + /* A RAM region is not worth adding if region size < 100K. + * It eats up precious command line length. */ + if (type == RANGE_RAM && (endk - startk) < min_sizek) + continue; + /* And do not add e820 reserved region either */ + cmdline_add_memmap_internal(cmdline, startk, endk, type); + } + + dbgprintf("Command line after adding memmap\n"); + dbgprintf("%s\n", cmdline); + + return 0; +} + +/* Adds the elfcorehdr= command line parameter to command line. */ +static int cmdline_add_elfcorehdr(char *cmdline, unsigned long addr) +{ + int cmdlen, len, align = 1024; + char str[30], *ptr; + + /* Passing in elfcorehdr=xxxK format. Saves space required in cmdline. + * Ensure 1K alignment*/ + if (addr%align) + return -1; + addr = addr/align; + ptr = str; + strcpy(str, " elfcorehdr="); + ptr += strlen(str); + ultoa(addr, ptr); + strcat(str, "K"); + len = strlen(str); + cmdlen = strlen(cmdline) + len; + if (cmdlen > (COMMAND_LINE_SIZE - 1)) + die("Command line overflow\n"); + strcat(cmdline, str); + + dbgprintf("Command line after adding elfcorehdr\n"); + dbgprintf("%s\n", cmdline); + + return 0; +} + + +/* + * This routine is specific to i386 architecture to maintain the + * backward compatibility, other architectures can use the per + * cpu version get_crash_notes_per_cpu() directly. + */ +static int get_crash_notes(int cpu, uint64_t *addr, uint64_t *len) +{ + const char *crash_notes = "/sys/kernel/crash_notes"; + char line[MAX_LINE]; + FILE *fp; + unsigned long vaddr; + int count; + + fp = fopen(crash_notes, "r"); + if (fp) { + if (fgets(line, sizeof(line), fp) != 0) { + count = sscanf(line, "%lx", &vaddr); + if (count != 1) + die("Cannot parse %s: %s\n", crash_notes, + strerror(errno)); + } + + *addr = x86__pa(vaddr + (cpu * MAX_NOTE_BYTES)); + *len = MAX_NOTE_BYTES; + + dbgprintf("crash_notes addr = %llx\n", + (unsigned long long)*addr); + + fclose(fp); + return 0; + } else + return get_crash_notes_per_cpu(cpu, addr, len); +} + +static enum coretype get_core_type(struct crash_elf_info *elf_info, + struct memory_range *range, int ranges) +{ + if ((elf_info->machine) == EM_X86_64) + return CORE_TYPE_ELF64; + else { + /* fall back to default */ + if (ranges == 0) + return CORE_TYPE_ELF64; + + if (range[ranges - 1].end > 0xFFFFFFFFUL) + return CORE_TYPE_ELF64; + else + return CORE_TYPE_ELF32; + } +} + +static int sysfs_efi_runtime_map_exist(void) +{ + DIR *dir; + + dir = opendir("/sys/firmware/efi/runtime-map"); + if (!dir) + return 0; + + closedir(dir); + return 1; +} + +/* Appends 'acpi_rsdp=' commandline for efi boot crash dump */ +static void cmdline_add_efi(char *cmdline) +{ + uint64_t acpi_rsdp; + char acpi_rsdp_buf[MAX_LINE]; + + acpi_rsdp = get_acpi_rsdp(); + + if (!acpi_rsdp) + return; + + sprintf(acpi_rsdp_buf, " acpi_rsdp=0x%lx", acpi_rsdp); + if (strlen(cmdline) + strlen(acpi_rsdp_buf) > (COMMAND_LINE_SIZE - 1)) + die("Command line overflow\n"); + + strcat(cmdline, acpi_rsdp_buf); +} + +static void get_backup_area(struct kexec_info *info, + struct memory_range *range, int ranges) +{ + int i; + + /* Look for first 640 KiB RAM region. */ + for (i = 0; i < ranges; ++i) { + if (range[i].type != RANGE_RAM || range[i].end > 0xa0000) + continue; + + info->backup_src_start = range[i].start; + info->backup_src_size = range[i].end - range[i].start + 1; + + dbgprintf("%s: %016llx-%016llx : System RAM\n", __func__, + range[i].start, range[i].end); + + return; + } + + /* First 640 KiB RAM region not found. Assume defaults. */ + info->backup_src_start = BACKUP_SRC_START; + info->backup_src_size = BACKUP_SRC_END - BACKUP_SRC_START + 1; +} + +/* Loads additional segments in case of a panic kernel is being loaded. + * One segment for backup region, another segment for storing elf headers + * for crash memory image. + */ +int load_crashdump_segments(struct kexec_info *info, char* mod_cmdline, + unsigned long max_addr, unsigned long min_base) +{ + void *tmp; + unsigned long sz, bufsz, memsz, elfcorehdr; + int nr_ranges = 0, nr_memmap = 0, align = 1024, i; + struct memory_range *mem_range, *memmap_p; + struct crash_elf_info elf_info; + unsigned kexec_arch; + + memset(&elf_info, 0x0, sizeof(elf_info)); + + /* Constant parts of the elf_info */ + memset(&elf_info, 0, sizeof(elf_info)); + elf_info.data = ELFDATA2LSB; + + /* Get the architecture of the running kernel */ + kexec_arch = info->kexec_flags & KEXEC_ARCH_MASK; + if (kexec_arch == KEXEC_ARCH_DEFAULT) + kexec_arch = KEXEC_ARCH_NATIVE; + + /* Get the elf architecture of the running kernel */ + switch(kexec_arch) { + case KEXEC_ARCH_X86_64: + elf_info.machine = EM_X86_64; + break; + case KEXEC_ARCH_386: + elf_info.machine = EM_386; + elf_info.lowmem_limit = X86_MAXMEM; + elf_info.get_note_info = get_crash_notes; + break; + default: + fprintf(stderr, "unsupported crashdump architecture: %04x\n", + kexec_arch); + return -1; + } + + if (xen_present()) { + if (get_crash_memory_ranges_xen(&mem_range, &nr_ranges, + elf_info.lowmem_limit) < 0) + return -1; + } else + if (get_crash_memory_ranges(&mem_range, &nr_ranges, + info->kexec_flags, + elf_info.lowmem_limit) < 0) + return -1; + + get_backup_area(info, mem_range, nr_ranges); + + dbgprint_mem_range("CRASH MEMORY RANGES", mem_range, nr_ranges); + + /* + * if the core type has not been set on command line, set it here + * automatically + */ + if (arch_options.core_header_type == CORE_TYPE_UNDEF) { + arch_options.core_header_type = + get_core_type(&elf_info, mem_range, nr_ranges); + } + /* Get the elf class... */ + elf_info.class = ELFCLASS32; + if (arch_options.core_header_type == CORE_TYPE_ELF64) { + elf_info.class = ELFCLASS64; + } + + if (get_kernel_page_offset(info, &elf_info)) + return -1; + + if (get_kernel_paddr(info, &elf_info)) + return -1; + + if (get_kernel_vaddr_and_size(info, &elf_info)) + return -1; + + /* Memory regions which panic kernel can safely use to boot into */ + sz = (sizeof(struct memory_range) * CRASH_MAX_MEMMAP_NR); + memmap_p = xmalloc(sz); + memset(memmap_p, 0, sz); + add_memmap(memmap_p, &nr_memmap, info->backup_src_start, info->backup_src_size, RANGE_RAM); + for (i = 0; i < crash_reserved_mem_nr; i++) { + sz = crash_reserved_mem[i].end - crash_reserved_mem[i].start +1; + if (add_memmap(memmap_p, &nr_memmap, crash_reserved_mem[i].start, + sz, RANGE_RAM) < 0) { + free(memmap_p); + return ENOCRASHKERNEL; + } + } + + /* Create a backup region segment to store backup data*/ + if (!(info->kexec_flags & KEXEC_PRESERVE_CONTEXT)) { + sz = _ALIGN(info->backup_src_size, align); + tmp = xmalloc(sz); + memset(tmp, 0, sz); + info->backup_start = add_buffer(info, tmp, sz, sz, align, + 0, max_addr, -1); + dbgprintf("Created backup segment at 0x%lx\n", + info->backup_start); + if (delete_memmap(memmap_p, &nr_memmap, info->backup_start, sz) < 0) { + free(tmp); + free(memmap_p); + return EFAILED; + } + } + + /* Create elf header segment and store crash image data. */ + if (arch_options.core_header_type == CORE_TYPE_ELF64) { + if (crash_create_elf64_headers(info, &elf_info, mem_range, + nr_ranges, &tmp, &bufsz, + ELF_CORE_HEADER_ALIGN) < 0) { + free(memmap_p); + return EFAILED; + } + } + else { + if (crash_create_elf32_headers(info, &elf_info, mem_range, + nr_ranges, &tmp, &bufsz, + ELF_CORE_HEADER_ALIGN) < 0) { + free(memmap_p); + return EFAILED; + } + } + /* the size of the elf headers allocated is returned in 'bufsz' */ + + /* Hack: With some ld versions (GNU ld version 2.14.90.0.4 20030523), + * vmlinux program headers show a gap of two pages between bss segment + * and data segment but effectively kernel considers it as bss segment + * and overwrites the any data placed there. Hence bloat the memsz of + * elf core header segment to 16K to avoid being placed in such gaps. + * This is a makeshift solution until it is fixed in kernel. + */ + if (bufsz < (16*1024)) { + /* bufsize is big enough for all the PT_NOTE's and PT_LOAD's */ + memsz = 16*1024; + /* memsz will be the size of the memory hole we look for */ + } else { + memsz = bufsz; + } + elfcorehdr = add_buffer(info, tmp, bufsz, memsz, align, min_base, + max_addr, -1); + dbgprintf("Created elf header segment at 0x%lx\n", elfcorehdr); + if (delete_memmap(memmap_p, &nr_memmap, elfcorehdr, memsz) < 0) { + free(memmap_p); + return -1; + } + if (!bzImage_support_efi_boot || arch_options.noefi || + !sysfs_efi_runtime_map_exist()) + cmdline_add_efi(mod_cmdline); + cmdline_add_elfcorehdr(mod_cmdline, elfcorehdr); + + /* Inform second kernel about the presence of ACPI tables. */ + for (i = 0; i < nr_ranges; i++) { + unsigned long start, end, size, type; + if ( !( mem_range[i].type == RANGE_ACPI + || mem_range[i].type == RANGE_ACPI_NVS + || mem_range[i].type == RANGE_RESERVED + || mem_range[i].type == RANGE_PMEM + || mem_range[i].type == RANGE_PRAM)) + continue; + start = mem_range[i].start; + end = mem_range[i].end; + type = mem_range[i].type; + size = end - start + 1; + add_memmap(memmap_p, &nr_memmap, start, size, type); + } + + if (arch_options.pass_memmap_cmdline) + cmdline_add_memmap(mod_cmdline, memmap_p); + + /* Store 2nd kernel boot memory ranges for later reference in + * x86-setup-linux.c: setup_linux_system_parameters() */ + info->crash_range = memmap_p; + info->nr_crash_ranges = nr_memmap; + + return 0; +} + +/* On x86, the kernel may make a low reservation in addition to the + * normal reservation. However, the kernel refuses to load the panic + * kernel to low memory, so always choose the highest range. + */ +int get_crash_kernel_load_range(uint64_t *start, uint64_t *end) +{ + if (!crash_reserved_mem_nr) + return -1; + + *start = crash_reserved_mem[crash_reserved_mem_nr - 1].start; + *end = crash_reserved_mem[crash_reserved_mem_nr - 1].end; + + return 0; +} + +static int crashkernel_mem_callback(void *UNUSED(data), int nr, + char *UNUSED(str), + unsigned long long base, + unsigned long long length) +{ + if (nr >= CRASH_RESERVED_MEM_NR) + return 1; + + crash_reserved_mem[nr].start = base; + crash_reserved_mem[nr].end = base + length - 1; + crash_reserved_mem[nr].type = RANGE_RAM; + return 0; +} + +int is_crashkernel_mem_reserved(void) +{ + int ret; + + if (xen_present()) { + uint64_t start, end; + + ret = xen_get_crashkernel_region(&start, &end); + if (ret < 0) + return 0; + + crash_reserved_mem[0].start = start; + crash_reserved_mem[0].end = end; + crash_reserved_mem[0].type = RANGE_RAM; + crash_reserved_mem_nr = 1; + } else { + ret = kexec_iomem_for_each_line("Crash kernel\n", + crashkernel_mem_callback, NULL); + crash_reserved_mem_nr = ret; + } + + return !!crash_reserved_mem_nr; +} diff --git a/kexec/arch/i386/crashdump-x86.h b/kexec/arch/i386/crashdump-x86.h new file mode 100644 index 0000000..479a549 --- /dev/null +++ b/kexec/arch/i386/crashdump-x86.h @@ -0,0 +1,33 @@ +#ifndef CRASHDUMP_X86_H +#define CRASHDUMP_X86_H + +struct kexec_info; +int load_crashdump_segments(struct kexec_info *info, char *mod_cmdline, + unsigned long max_addr, unsigned long min_base); + +#define X86_PAGE_OFFSET 0xc0000000 +#define x86__pa(x) ((unsigned long)(x)-X86_PAGE_OFFSET) + +#define X86__VMALLOC_RESERVE (128 << 20) +#define X86_MAXMEM (-X86_PAGE_OFFSET-X86__VMALLOC_RESERVE) + +#define X86_64__START_KERNEL_map 0xffffffff80000000ULL +#define X86_64_PAGE_OFFSET_PRE_2_6_27 0xffff810000000000ULL +#define X86_64_PAGE_OFFSET_PRE_4_20_0 0xffff880000000000ULL +#define X86_64_PAGE_OFFSET 0xffff888000000000ULL + +#define X86_64_MAXMEM 0x3fffffffffffUL + +/* Kernel text size */ +#define X86_64_KERNEL_TEXT_SIZE (512UL*1024*1024) + +#define CRASH_MAX_MEMMAP_NR 1024 + +#define CRASH_MAX_MEMORY_RANGES 32768 + +/* Backup Region, First 640K of System RAM. */ +#define BACKUP_SRC_START 0x00000000 +#define BACKUP_SRC_END 0x0009ffff +#define BACKUP_SRC_SIZE (BACKUP_SRC_END - BACKUP_SRC_START + 1) + +#endif /* CRASHDUMP_X86_H */ diff --git a/kexec/arch/i386/include/arch/options.h b/kexec/arch/i386/include/arch/options.h new file mode 100644 index 0000000..89e0a95 --- /dev/null +++ b/kexec/arch/i386/include/arch/options.h @@ -0,0 +1,86 @@ +#ifndef KEXEC_ARCH_I386_OPTIONS_H +#define KEXEC_ARCH_I386_OPTIONS_H + +/* + ************************************************************************* + * NOTE NOTE NOTE + * This file is included for i386 builds *and* x86_64 builds (which build + * both x86_64 and i386 loaders). + * It contains the combined set of options used by i386 and x86_64. + ************************************************************************* + */ + +#define OPT_RESET_VGA (OPT_MAX+0) +#define OPT_SERIAL (OPT_MAX+1) +#define OPT_SERIAL_BAUD (OPT_MAX+2) +#define OPT_CONSOLE_VGA (OPT_MAX+3) +#define OPT_CONSOLE_SERIAL (OPT_MAX+4) +#define OPT_ELF32_CORE (OPT_MAX+5) +#define OPT_ELF64_CORE (OPT_MAX+6) +#define OPT_ARCH_MAX (OPT_MAX+7) + +#define OPT_APPEND (OPT_ARCH_MAX+0) +#define OPT_REUSE_CMDLINE (OPT_ARCH_MAX+1) +#define OPT_RAMDISK (OPT_ARCH_MAX+2) +#define OPT_ARGS_ELF (OPT_ARCH_MAX+3) +#define OPT_ARGS_LINUX (OPT_ARCH_MAX+4) +#define OPT_ARGS_NONE (OPT_ARCH_MAX+5) +#define OPT_CL (OPT_ARCH_MAX+6) +#define OPT_MOD (OPT_ARCH_MAX+7) +#define OPT_VGA (OPT_ARCH_MAX+8) +#define OPT_REAL_MODE (OPT_ARCH_MAX+9) +#define OPT_ENTRY_32BIT (OPT_ARCH_MAX+10) +#define OPT_PASS_MEMMAP_CMDLINE (OPT_ARCH_MAX+11) +#define OPT_NOEFI (OPT_ARCH_MAX+12) +#define OPT_REUSE_VIDEO_TYPE (OPT_ARCH_MAX+13) +#define OPT_DTB (OPT_ARCH_MAX+14) + +/* Options relevant to the architecture (excluding loader-specific ones): */ +#define KEXEC_ARCH_OPTIONS \ + KEXEC_OPTIONS \ + { "reset-vga", 0, 0, OPT_RESET_VGA }, \ + { "serial", 1, 0, OPT_SERIAL }, \ + { "serial-baud", 1, 0, OPT_SERIAL_BAUD }, \ + { "console-vga", 0, 0, OPT_CONSOLE_VGA }, \ + { "console-serial", 0, 0, OPT_CONSOLE_SERIAL }, \ + { "elf32-core-headers", 0, 0, OPT_ELF32_CORE }, \ + { "elf64-core-headers", 0, 0, OPT_ELF64_CORE }, \ + { "pass-memmap-cmdline", 0, 0, OPT_PASS_MEMMAP_CMDLINE }, \ + { "noefi", 0, 0, OPT_NOEFI}, \ + { "reuse-video-type", 0, 0, OPT_REUSE_VIDEO_TYPE }, \ + +#define KEXEC_ARCH_OPT_STR KEXEC_OPT_STR "" + +/* The following two #defines list ALL of the options added by all of the + * architecture's loaders. + * o main() uses this complete list to scan for its options, ignoring + * arch-specific/loader-specific ones. + * o Then, arch_process_options() uses this complete list to scan for its + * options, ignoring general/loader-specific ones. + * o Then, the file_type[n].load re-scans for options, using + * KEXEC_ARCH_OPTIONS plus its loader-specific options subset. + * Any unrecognised options cause an error here. + * + * This is done so that main()'s/arch_process_options()'s getopt_long() calls + * don't choose a kernel filename from random arguments to options they don't + * recognise -- as they now recognise (if not act upon) all possible options. + */ +#define KEXEC_ALL_OPTIONS \ + KEXEC_ARCH_OPTIONS \ + { "command-line", 1, NULL, OPT_APPEND }, \ + { "append", 1, NULL, OPT_APPEND }, \ + { "reuse-cmdline", 0, NULL, OPT_REUSE_CMDLINE }, \ + { "initrd", 1, NULL, OPT_RAMDISK }, \ + { "ramdisk", 1, NULL, OPT_RAMDISK }, \ + { "args-elf", 0, NULL, OPT_ARGS_ELF }, \ + { "args-linux", 0, NULL, OPT_ARGS_LINUX }, \ + { "args-none", 0, NULL, OPT_ARGS_NONE }, \ + { "module", 1, 0, OPT_MOD }, \ + { "real-mode", 0, NULL, OPT_REAL_MODE }, \ + { "entry-32bit", 0, NULL, OPT_ENTRY_32BIT }, \ + { "dtb", 1, NULL, OPT_DTB }, + +#define KEXEC_ALL_OPT_STR KEXEC_ARCH_OPT_STR + +#endif /* KEXEC_ARCH_I386_OPTIONS_H */ + diff --git a/kexec/arch/i386/kexec-beoboot-x86.c b/kexec/arch/i386/kexec-beoboot-x86.c new file mode 100644 index 0000000..d949ab8 --- /dev/null +++ b/kexec/arch/i386/kexec-beoboot-x86.c @@ -0,0 +1,132 @@ +/*------------------------------------------------------------ -*- C -*- + * Eric Biederman <ebiederman@xmission.com> + * Erik Arjan Hendriks <hendriks@lanl.gov> + * + * 14 December 2004 + * This file is a derivative of the beoboot image loader, modified + * to work with kexec. + * + * This version is derivative from the orignal mkbootimg.c which is + * Copyright (C) 2000 Scyld Computing Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + + * + *--------------------------------------------------------------------*/ + +#define _GNU_SOURCE +#include <stdio.h> +#include <stdint.h> +#include <string.h> +#include <getopt.h> +#include <x86/x86-linux.h> +#include <boot/beoboot.h> +#include "../../kexec.h" +#include "kexec-x86.h" +#include <arch/options.h> + +int beoboot_probe(const char *buf, off_t len) +{ + struct beoboot_header bb_header; + const char *cmdline, *kernel; + int result; + if ((uintmax_t)len < (uintmax_t)sizeof(bb_header)) { + return -1; + } + memcpy(&bb_header, buf, sizeof(bb_header)); + if (memcmp(bb_header.magic, BEOBOOT_MAGIC, 4) != 0) { + return -1; + } + if (bb_header.arch != BEOBOOT_ARCH) { + return -1; + } + /* Make certain a bzImage is packed into there. + */ + cmdline = buf + sizeof(bb_header); + kernel = cmdline + bb_header.cmdline_size; + result = bzImage_probe(kernel, bb_header.kernel_size); + + return result; +} + +void beoboot_usage(void) +{ + printf( " --real-mode Use the kernels real mode entry point.\n" + ); + + /* No parameters are parsed */ +} + +#define SETUP_BASE 0x90000 +#define KERN32_BASE 0x100000 /* 1MB */ +#define INITRD_BASE 0x1000000 /* 16MB */ + +int beoboot_load(int argc, char **argv, const char *buf, off_t UNUSED(len), + struct kexec_info *info) +{ + struct beoboot_header bb_header; + const char *command_line, *kernel, *initrd; + + int real_mode_entry; + int opt; + int result; + + /* See options.h -- add any more there, too. */ + static const struct option options[] = { + KEXEC_ARCH_OPTIONS + { "real-mode", 0, 0, OPT_REAL_MODE }, + { 0, 0, 0, 0 }, + }; + static const char short_options[] = KEXEC_ARCH_OPT_STR ""; + + /* + * Parse the command line arguments + */ + real_mode_entry = 0; + while((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) { + switch(opt) { + default: + /* Ignore core options */ + if (opt < OPT_ARCH_MAX) { + break; + } + case OPT_REAL_MODE: + real_mode_entry = 1; + break; + } + } + + + /* + * Parse the file + */ + memcpy(&bb_header, buf, sizeof(bb_header)); + command_line = buf + sizeof(bb_header); + kernel = command_line + bb_header.cmdline_size; + initrd = NULL; + if (bb_header.flags & BEOBOOT_INITRD_PRESENT) { + initrd = kernel + bb_header.kernel_size; + } + + result = do_bzImage_load(info, + kernel, bb_header.kernel_size, + command_line, bb_header.cmdline_size, + initrd, bb_header.initrd_size, + 0, 0, real_mode_entry); + + return result; +} + diff --git a/kexec/arch/i386/kexec-bzImage.c b/kexec/arch/i386/kexec-bzImage.c new file mode 100644 index 0000000..1b8f20c --- /dev/null +++ b/kexec/arch/i386/kexec-bzImage.c @@ -0,0 +1,471 @@ +/* + * kexec: Linux boots Linux + * + * Copyright (C) 2003-2010 Eric Biederman (ebiederm@xmission.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation (version 2 of the License). + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#define _GNU_SOURCE +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <getopt.h> +#include <elf.h> +#include <boot/elf_boot.h> +#include <ip_checksum.h> +#include <x86/x86-linux.h> +#include "../../kexec.h" +#include "../../kexec-elf.h" +#include "../../kexec-syscall.h" +#include "kexec-x86.h" +#include "x86-linux-setup.h" +#include "crashdump-x86.h" +#include <arch/options.h> + +static const int probe_debug = 0; +int bzImage_support_efi_boot = 0; + +int bzImage_probe(const char *buf, off_t len) +{ + const struct x86_linux_header *header; + if ((uintmax_t)len < (uintmax_t)(2 * 512)) { + if (probe_debug) { + fprintf(stderr, "File is too short to be a bzImage!\n"); + } + return -1; + } + header = (const struct x86_linux_header *)buf; + if (memcmp(header->header_magic, "HdrS", 4) != 0) { + if (probe_debug) { + fprintf(stderr, "Not a bzImage\n"); + } + return -1; + } + if (header->boot_sector_magic != 0xAA55) { + if (probe_debug) { + fprintf(stderr, "No x86 boot sector present\n"); + } + /* No x86 boot sector present */ + return -1; + } + if (header->protocol_version < 0x0200) { + if (probe_debug) { + fprintf(stderr, "Must be at least protocol version 2.00\n"); + } + /* Must be at least protocol version 2.00 */ + return -1; + } + if ((header->loadflags & 1) == 0) { + if (probe_debug) { + fprintf(stderr, "zImage not a bzImage\n"); + } + /* Not a bzImage */ + return -1; + } + /* I've got a bzImage */ + if (probe_debug) { + fprintf(stderr, "It's a bzImage\n"); + } + return 0; +} + + +void bzImage_usage(void) +{ + printf( " --real-mode Use the kernels real mode entry point.\n" + " --command-line=STRING Set the kernel command line to STRING.\n" + " --append=STRING Set the kernel command line to STRING.\n" + " --reuse-cmdline Use kernel command line from running system.\n" + " --initrd=FILE Use FILE as the kernel's initial ramdisk.\n" + " --ramdisk=FILE Use FILE as the kernel's initial ramdisk.\n" + " --dtb=FILE Use FILE as devicetree.\n" + ); + +} + +int do_bzImage_load(struct kexec_info *info, + const char *kernel, off_t kernel_len, + const char *command_line, off_t command_line_len, + const char *initrd, off_t initrd_len, + const char *dtb, off_t dtb_len, + int real_mode_entry) +{ + struct x86_linux_header setup_header; + struct x86_linux_param_header *real_mode; + int setup_sects; + size_t size; + int kern16_size; + unsigned long setup_base, setup_size; + struct entry32_regs regs32; + struct entry16_regs regs16; + unsigned int relocatable_kernel = 0; + unsigned long kernel32_load_addr; + char *modified_cmdline; + unsigned long cmdline_end; + unsigned long kern16_size_needed; + unsigned long heap_size = 0; + + /* + * Find out about the file I am about to load. + */ + if ((uintmax_t)kernel_len < (uintmax_t)(2 * 512)) { + return -1; + } + memcpy(&setup_header, kernel, sizeof(setup_header)); + setup_sects = setup_header.setup_sects; + if (setup_sects == 0) { + setup_sects = 4; + } + + kern16_size = (setup_sects +1) *512; + if (kernel_len < kern16_size) { + fprintf(stderr, "BzImage truncated?\n"); + return -1; + } + + if (setup_header.protocol_version >= 0x0206) { + if ((uintmax_t)command_line_len > + (uintmax_t)setup_header.cmdline_size) { + dbgprintf("Kernel command line too long for kernel!\n"); + return -1; + } + } else { + if (command_line_len > 255) { + dbgprintf("WARNING: This kernel may only support 255 byte command lines\n"); + } + } + + if (setup_header.protocol_version >= 0x0205) { + relocatable_kernel = setup_header.relocatable_kernel; + dbgprintf("bzImage is relocatable\n"); + } + + /* Can't use bzImage for crash dump purposes with real mode entry */ + if((info->kexec_flags & KEXEC_ON_CRASH) && real_mode_entry) { + fprintf(stderr, "Can't use bzImage for crash dump purposes" + " with real mode entry\n"); + return -1; + } + + if((info->kexec_flags & KEXEC_ON_CRASH) && !relocatable_kernel) { + fprintf(stderr, "BzImage is not relocatable. Can't be used" + " as capture kernel.\n"); + return -1; + } + + /* Need to append some command line parameters internally in case of + * taking crash dumps. + */ + if (info->kexec_flags & (KEXEC_ON_CRASH | KEXEC_PRESERVE_CONTEXT)) { + modified_cmdline = xmalloc(COMMAND_LINE_SIZE); + memset((void *)modified_cmdline, 0, COMMAND_LINE_SIZE); + if (command_line) { + strncpy(modified_cmdline, command_line, + COMMAND_LINE_SIZE); + modified_cmdline[COMMAND_LINE_SIZE - 1] = '\0'; + } + + /* If panic kernel is being loaded, additional segments need + * to be created. load_crashdump_segments will take care of + * loading the segments as high in memory as possible, hence + * in turn as away as possible from kernel to avoid being + * stomped by the kernel. + */ + if (load_crashdump_segments(info, modified_cmdline, -1, 0) < 0) + return -1; + + /* Use new command line buffer */ + command_line = modified_cmdline; + command_line_len = strlen(command_line) +1; + } + + /* Load the trampoline. This must load at a higher address + * than the argument/parameter segment or the kernel will stomp + * it's gdt. + * + * x86_64 purgatory code has got relocations type R_X86_64_32S + * that means purgatory got to be loaded within first 2G otherwise + * overflow takes place while applying relocations. + */ + if (!real_mode_entry && relocatable_kernel) + elf_rel_build_load(info, &info->rhdr, purgatory, purgatory_size, + 0x3000, 0x7fffffff, -1, 0); + else + elf_rel_build_load(info, &info->rhdr, purgatory, purgatory_size, + 0x3000, 640*1024, -1, 0); + dbgprintf("Loaded purgatory at addr 0x%lx\n", info->rhdr.rel_addr); + + /* The argument/parameter segment */ + if (real_mode_entry) { + /* need to include size for bss and heap etc */ + if (setup_header.protocol_version >= 0x0201) + kern16_size_needed = setup_header.heap_end_ptr; + else + kern16_size_needed = kern16_size + 8192; /* bss */ + if (kern16_size_needed < kern16_size) + kern16_size_needed = kern16_size; + if (kern16_size_needed > 0xfffc) + die("kern16_size_needed is more then 64k\n"); + heap_size = 0xfffc - kern16_size_needed; /* less 64k */ + heap_size = _ALIGN_DOWN(heap_size, 0x200); + kern16_size_needed += heap_size; + } else { + kern16_size_needed = kern16_size; + /* need to bigger than size of struct bootparams */ + if (kern16_size_needed < 4096) + kern16_size_needed = 4096; + } + setup_size = kern16_size_needed + command_line_len + + PURGATORY_CMDLINE_SIZE; + real_mode = xmalloc(setup_size); + memset(real_mode, 0, setup_size); + if (!real_mode_entry) { + unsigned long setup_header_size = kernel[0x201] + 0x202 - 0x1f1; + + /* only copy setup_header */ + if (setup_header_size > 0x7f) + setup_header_size = 0x7f; + memcpy((unsigned char *)real_mode + 0x1f1, kernel + 0x1f1, + setup_header_size); + } else { + /* copy setup code and setup_header */ + memcpy(real_mode, kernel, kern16_size); + } + + if (info->kexec_flags & (KEXEC_ON_CRASH | KEXEC_PRESERVE_CONTEXT)) { + /* If using bzImage for capture kernel, then we will not be + * executing real mode code. setup segment can be loaded + * anywhere as we will be just reading command line. + */ + setup_base = add_buffer(info, real_mode, setup_size, setup_size, + 16, 0x3000, -1, -1); + } + else if (real_mode->protocol_version >= 0x0200) { + /* Careful setup_base must be greater than 8K */ + setup_base = add_buffer(info, real_mode, setup_size, setup_size, + 16, 0x3000, 640*1024, 1); + } else { + add_segment(info, real_mode, setup_size, SETUP_BASE, setup_size); + setup_base = SETUP_BASE; + } + dbgprintf("Loaded real-mode code and command line at 0x%lx\n", + setup_base); + + /* Verify purgatory loads higher than the parameters */ + if (info->rhdr.rel_addr < setup_base) { + die("Could not put setup code above the kernel parameters\n"); + } + + /* The main kernel segment */ + size = kernel_len - kern16_size; + + if (real_mode->protocol_version >=0x0205 && relocatable_kernel) { + /* Relocatable bzImage */ + unsigned long kern_align = real_mode->kernel_alignment; + unsigned long kernel32_max_addr = DEFAULT_BZIMAGE_ADDR_MAX; + + if (kernel32_max_addr > real_mode->initrd_addr_max) + kernel32_max_addr = real_mode->initrd_addr_max; + + kernel32_load_addr = add_buffer(info, kernel + kern16_size, + size, size, kern_align, + 0x100000, kernel32_max_addr, + 1); + } + else { + kernel32_load_addr = KERN32_BASE; + add_segment(info, kernel + kern16_size, size, + kernel32_load_addr, size); + } + + dbgprintf("Loaded 32bit kernel at 0x%lx\n", kernel32_load_addr); + + /* Tell the kernel what is going on */ + setup_linux_bootloader_parameters(info, real_mode, setup_base, + kern16_size_needed, command_line, command_line_len, + initrd, initrd_len); + + if (real_mode_entry && real_mode->protocol_version >= 0x0201) { + real_mode->loader_flags |= 0x80; /* CAN_USE_HEAP */ + real_mode->heap_end_ptr += heap_size - 0x200; /*stack*/ + } + + /* Get the initial register values */ + if (real_mode_entry) + elf_rel_get_symbol(&info->rhdr, "entry16_regs", + ®s16, sizeof(regs16)); + + /* + * Initialize the 32bit start information. + */ + regs32.eax = 0; /* unused */ + regs32.ebx = 0; /* 0 == boot not AP processor start */ + regs32.ecx = 0; /* unused */ + regs32.edx = 0; /* unused */ + regs32.esi = setup_base; /* kernel parameters */ + regs32.edi = 0; /* unused */ + regs32.esp = elf_rel_get_addr(&info->rhdr, "stack_end"); /* stack, unused */ + regs32.ebp = 0; /* unused */ + regs32.eip = kernel32_load_addr; /* kernel entry point */ + + /* + * Initialize the 16bit start information. + */ + if (real_mode_entry) { + regs16.ds = regs16.es = regs16.fs = regs16.gs = setup_base >> 4; + regs16.cs = regs16.ds + 0x20; + regs16.ip = 0; + /* XXX: Documentation/i386/boot.txt says 'ss' must equal 'ds' */ + regs16.ss = (elf_rel_get_addr(&info->rhdr, "stack_end") - 64*1024) >> 4; + /* XXX: Documentation/i386/boot.txt says 'sp' must equal heap_end */ + regs16.esp = 0xFFFC; + + printf("Starting the kernel in real mode\n"); + regs32.eip = elf_rel_get_addr(&info->rhdr, "entry16"); + real_mode->kernel_start = kernel32_load_addr; + } + if (real_mode_entry && kexec_debug) { + unsigned long entry16_debug, pre32, first32; + uint32_t old_first32; + /* Find the location of the symbols */ + entry16_debug = elf_rel_get_addr(&info->rhdr, "entry16_debug"); + pre32 = elf_rel_get_addr(&info->rhdr, "entry16_debug_pre32"); + first32 = elf_rel_get_addr(&info->rhdr, "entry16_debug_first32"); + + /* Hook all of the linux kernel hooks */ + real_mode->rmode_switch_cs = entry16_debug >> 4; + real_mode->rmode_switch_ip = pre32 - entry16_debug; + old_first32 = real_mode->kernel_start; + real_mode->kernel_start = first32; + elf_rel_set_symbol(&info->rhdr, "entry16_debug_old_first32", + &old_first32, sizeof(old_first32)); + + regs32.eip = entry16_debug; + } + if (real_mode_entry) { + elf_rel_set_symbol(&info->rhdr, "entry16_regs", + ®s16, sizeof(regs16)); + elf_rel_set_symbol(&info->rhdr, "entry16_debug_regs", + ®s16, sizeof(regs16)); + } + elf_rel_set_symbol(&info->rhdr, "entry32_regs", ®s32, sizeof(regs32)); + cmdline_end = setup_base + kern16_size_needed + command_line_len - 1; + elf_rel_set_symbol(&info->rhdr, "cmdline_end", &cmdline_end, + sizeof(unsigned long)); + + /* Fill in the information BIOS calls would normally provide. */ + if (!real_mode_entry) { + setup_linux_system_parameters(info, real_mode); + } + + if (dtb) { + setup_linux_dtb(info, real_mode, dtb, dtb_len); + } + + return 0; +} + +int bzImage_load(int argc, char **argv, const char *buf, off_t len, + struct kexec_info *info) +{ + char *command_line = NULL; + char *tmp_cmdline = NULL; + const char *ramdisk, *append = NULL, *dtb; + char *ramdisk_buf; + off_t ramdisk_length; + int command_line_len; + int real_mode_entry; + int opt; + int result; + char *dtb_buf; + off_t dtb_length; + + /* See options.h -- add any more there, too. */ + static const struct option options[] = { + KEXEC_ARCH_OPTIONS + { "command-line", 1, 0, OPT_APPEND }, + { "append", 1, 0, OPT_APPEND }, + { "reuse-cmdline", 0, 0, OPT_REUSE_CMDLINE }, + { "initrd", 1, 0, OPT_RAMDISK }, + { "ramdisk", 1, 0, OPT_RAMDISK }, + { "real-mode", 0, 0, OPT_REAL_MODE }, + { "dtb", 1, 0, OPT_DTB }, + { 0, 0, 0, 0 }, + }; + static const char short_options[] = KEXEC_ARCH_OPT_STR "d"; + + real_mode_entry = 0; + ramdisk = 0; + ramdisk_length = 0; + dtb = 0; + dtb_length = 0; + while((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) { + switch(opt) { + default: + /* Ignore core options */ + if (opt < OPT_ARCH_MAX) { + break; + } + case OPT_APPEND: + append = optarg; + break; + case OPT_REUSE_CMDLINE: + tmp_cmdline = get_command_line(); + break; + case OPT_RAMDISK: + ramdisk = optarg; + break; + case OPT_REAL_MODE: + real_mode_entry = 1; + break; + case OPT_DTB: + dtb = optarg; + break; + } + } + command_line = concat_cmdline(tmp_cmdline, append); + if (tmp_cmdline) { + free(tmp_cmdline); + } + command_line_len = 0; + if (command_line) { + command_line_len = strlen(command_line) +1; + } else { + command_line = strdup("\0"); + command_line_len = 1; + } + ramdisk_buf = 0; + if (ramdisk) { + ramdisk_buf = slurp_file(ramdisk, &ramdisk_length); + } + dtb_buf = 0; + if (dtb) { + dtb_buf = slurp_file(dtb, &dtb_length); + } + result = do_bzImage_load(info, + buf, len, + command_line, command_line_len, + ramdisk_buf, ramdisk_length, + dtb_buf, dtb_length, + real_mode_entry); + + free(command_line); + return result; +} diff --git a/kexec/arch/i386/kexec-elf-rel-x86.c b/kexec/arch/i386/kexec-elf-rel-x86.c new file mode 100644 index 0000000..55a214e --- /dev/null +++ b/kexec/arch/i386/kexec-elf-rel-x86.c @@ -0,0 +1,36 @@ +#include <stdio.h> +#include <elf.h> +#include "../../kexec.h" +#include "../../kexec-elf.h" + +int machine_verify_elf_rel(struct mem_ehdr *ehdr) +{ + if (ehdr->ei_data != ELFDATA2LSB) { + return 0; + } + if (ehdr->ei_class != ELFCLASS32) { + return 0; + } + if ((ehdr->e_machine != EM_386) && (ehdr->e_machine != EM_486)) + { + return 0; + } + return 1; +} + +void machine_apply_elf_rel(struct mem_ehdr *UNUSED(ehdr), + struct mem_sym *UNUSED(sym), unsigned long r_type, void *location, + unsigned long address, unsigned long value) +{ + switch(r_type) { + case R_386_32: + *((uint32_t *)location) += value; + break; + case R_386_PC32: + *((uint32_t *)location) += value - address; + break; + default: + die("Unknown rel relocation: %lu\n", r_type); + break; + } +} diff --git a/kexec/arch/i386/kexec-elf-x86.c b/kexec/arch/i386/kexec-elf-x86.c new file mode 100644 index 0000000..8eba242 --- /dev/null +++ b/kexec/arch/i386/kexec-elf-x86.c @@ -0,0 +1,331 @@ +/* + * kexec: Linux boots Linux + * + * Copyright (C) 2003-2005 Eric Biederman (ebiederm@xmission.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation (version 2 of the License). + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#define _GNU_SOURCE +#include <stddef.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <limits.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <getopt.h> +#include <elf.h> +#include <x86/x86-linux.h> +#include "../../kexec.h" +#include "../../kexec-syscall.h" +#include "../../kexec-elf.h" +#include "../../kexec-elf-boot.h" +#include "x86-linux-setup.h" +#include "kexec-x86.h" +#include "crashdump-x86.h" +#include <arch/options.h> + +static const int probe_debug = 0; + +int elf_x86_any_probe(const char *buf, off_t len, enum coretype arch) +{ + + struct mem_ehdr ehdr; + int result; + result = build_elf_exec_info(buf, len, &ehdr, 0); + if (result < 0) { + if (probe_debug) { + fprintf(stderr, "Not an ELF executable\n"); + } + goto out; + } + + /* Verify the architecuture specific bits */ + switch (arch) { + case CORE_TYPE_ELF32: + if ((ehdr.e_machine != EM_386) && (ehdr.e_machine != EM_486)) { + if (probe_debug) + fprintf(stderr, "Not i386 ELF executable\n"); + result = -1; + goto out; + } + break; + + case CORE_TYPE_ELF64: + if (ehdr.e_machine != EM_X86_64) { + if (probe_debug) + fprintf(stderr, "Not x86_64 ELF executable\n"); + result = -1; + goto out; + } + break; + + case CORE_TYPE_UNDEF: + default: + if ( + (ehdr.e_machine != EM_386) && + (ehdr.e_machine != EM_486) && + (ehdr.e_machine != EM_X86_64) + ) { + if (probe_debug) + fprintf(stderr, "Not i386 or x86_64 ELF executable\n"); + result = -1; + goto out; + } + break; + } + + result = 0; + out: + free_elf_info(&ehdr); + return result; +} + +int elf_x86_probe(const char *buf, off_t len) { + return elf_x86_any_probe(buf, len, CORE_TYPE_ELF32); +} + +void elf_x86_usage(void) +{ + printf( " --command-line=STRING Set the kernel command line to STRING\n" + " --append=STRING Set the kernel command line to STRING\n" + " --reuse-cmdline Use kernel command line from running system.\n" + " --initrd=FILE Use FILE as the kernel's initial ramdisk.\n" + " --ramdisk=FILE Use FILE as the kernel's initial ramdisk.\n" + " --args-linux Pass linux kernel style options\n" + " --args-elf Pass elf boot notes\n" + ); + + +} + +int elf_x86_load(int argc, char **argv, const char *buf, off_t len, + struct kexec_info *info) +{ + struct mem_ehdr ehdr; + char *command_line = NULL, *modified_cmdline = NULL; + const char *append = NULL; + char *tmp_cmdline = NULL; + const char *error_msg = NULL; + int result; + int command_line_len; + const char *ramdisk; + unsigned long entry, max_addr; + int arg_style; +#define ARG_STYLE_ELF 0 +#define ARG_STYLE_LINUX 1 +#define ARG_STYLE_NONE 2 + int opt; + + /* See options.h -- add any more there, too. */ + static const struct option options[] = { + KEXEC_ARCH_OPTIONS + { "command-line", 1, NULL, OPT_APPEND }, + { "append", 1, NULL, OPT_APPEND }, + { "reuse-cmdline", 0, NULL, OPT_REUSE_CMDLINE }, + { "initrd", 1, NULL, OPT_RAMDISK }, + { "ramdisk", 1, NULL, OPT_RAMDISK }, + { "args-elf", 0, NULL, OPT_ARGS_ELF }, + { "args-linux", 0, NULL, OPT_ARGS_LINUX }, + { "args-none", 0, NULL, OPT_ARGS_NONE }, + { 0, 0, NULL, 0 }, + }; + + static const char short_options[] = KEXEC_OPT_STR ""; + + /* + * Parse the command line arguments + */ + arg_style = ARG_STYLE_ELF; + ramdisk = 0; + result = 0; + while((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) { + switch(opt) { + default: + /* Ignore core options */ + if (opt < OPT_ARCH_MAX) { + break; + } + case OPT_APPEND: + append = optarg; + break; + case OPT_REUSE_CMDLINE: + tmp_cmdline = get_command_line(); + break; + case OPT_RAMDISK: + ramdisk = optarg; + break; + case OPT_ARGS_ELF: + arg_style = ARG_STYLE_ELF; + break; + case OPT_ARGS_LINUX: + arg_style = ARG_STYLE_LINUX; + break; + case OPT_ARGS_NONE: +#ifdef __i386__ + arg_style = ARG_STYLE_NONE; +#else + die("--args-none only works on arch i386\n"); +#endif + break; + } + } + command_line = concat_cmdline(tmp_cmdline, append); + if (tmp_cmdline) { + free(tmp_cmdline); + } + command_line_len = 0; + if (command_line) { + command_line_len = strlen(command_line) +1; + } else { + command_line = strdup("\0"); + command_line_len = 1; + } + + /* Need to append some command line parameters internally in case of + * taking crash dumps. + */ + if (info->kexec_flags & (KEXEC_ON_CRASH|KEXEC_PRESERVE_CONTEXT)) { + modified_cmdline = xmalloc(COMMAND_LINE_SIZE); + memset((void *)modified_cmdline, 0, COMMAND_LINE_SIZE); + if (command_line) { + strncpy(modified_cmdline, command_line, + COMMAND_LINE_SIZE); + modified_cmdline[COMMAND_LINE_SIZE - 1] = '\0'; + } + } + + /* Load the ELF executable */ + elf_exec_build_load(info, &ehdr, buf, len, 0); + + entry = ehdr.e_entry; + max_addr = elf_max_addr(&ehdr); + + /* Do we want arguments? */ + if (arg_style != ARG_STYLE_NONE) { + /* Load the setup code */ + elf_rel_build_load(info, &info->rhdr, purgatory, purgatory_size, + 0, ULONG_MAX, 1, 0); + } + if (arg_style == ARG_STYLE_NONE) { + info->entry = (void *)entry; + + } + else if (arg_style == ARG_STYLE_ELF) { + unsigned long note_base; + struct entry32_regs regs; + uint32_t arg1, arg2; + + /* Setup the ELF boot notes */ + note_base = elf_boot_notes(info, max_addr, + command_line, command_line_len); + + /* Initialize the stack arguments */ + arg2 = 0; /* No return address */ + arg1 = note_base; + elf_rel_set_symbol(&info->rhdr, "stack_arg32_1", &arg1, sizeof(arg1)); + elf_rel_set_symbol(&info->rhdr, "stack_arg32_2", &arg2, sizeof(arg2)); + + /* Initialize the registers */ + elf_rel_get_symbol(&info->rhdr, "entry32_regs", ®s, sizeof(regs)); + regs.eip = entry; /* The entry point */ + regs.esp = elf_rel_get_addr(&info->rhdr, "stack_arg32_2"); + elf_rel_set_symbol(&info->rhdr, "entry32_regs", ®s, sizeof(regs)); + + if (ramdisk) { + error_msg = "Ramdisks not supported with generic elf arguments"; + goto out; + } + } + else if (arg_style == ARG_STYLE_LINUX) { + struct x86_linux_faked_param_header *hdr; + unsigned long param_base; + const char *ramdisk_buf; + off_t ramdisk_length; + struct entry32_regs regs; + int rc = 0; + + /* Get the linux parameter header */ + hdr = xmalloc(sizeof(*hdr)); + + /* Hack: With some ld versions, vmlinux program headers show + * a gap of two pages between bss segment and data segment + * but effectively kernel considers it as bss segment and + * overwrites the any data placed there. Hence bloat the + * memsz of parameter segment to 16K to avoid being placed + * in such gaps. + * This is a makeshift solution until it is fixed in kernel + */ + param_base = add_buffer(info, hdr, sizeof(*hdr), 16*1024, + 16, 0, max_addr, 1); + + /* Initialize the parameter header */ + memset(hdr, 0, sizeof(*hdr)); + init_linux_parameters(&hdr->hdr); + + /* Add a ramdisk to the current image */ + ramdisk_buf = NULL; + ramdisk_length = 0; + if (ramdisk) { + ramdisk_buf = slurp_file(ramdisk, &ramdisk_length); + } + + /* If panic kernel is being loaded, additional segments need + * to be created. */ + if (info->kexec_flags & (KEXEC_ON_CRASH|KEXEC_PRESERVE_CONTEXT)) { + rc = load_crashdump_segments(info, modified_cmdline, + max_addr, 0); + if (rc < 0) { + result = -1; + goto out; + } + /* Use new command line. */ + free(command_line); + command_line = modified_cmdline; + command_line_len = strlen(modified_cmdline) + 1; + modified_cmdline = NULL; + } + + /* Tell the kernel what is going on */ + setup_linux_bootloader_parameters(info, &hdr->hdr, param_base, + offsetof(struct x86_linux_faked_param_header, command_line), + command_line, command_line_len, + ramdisk_buf, ramdisk_length); + + /* Fill in the information bios calls would usually provide */ + setup_linux_system_parameters(info, &hdr->hdr); + + /* Initialize the registers */ + elf_rel_get_symbol(&info->rhdr, "entry32_regs", ®s, sizeof(regs)); + regs.ebx = 0; /* Bootstrap processor */ + regs.esi = param_base; /* Pointer to the parameters */ + regs.eip = entry; /* The entry point */ + regs.esp = elf_rel_get_addr(&info->rhdr, "stack_end"); /* Stack, unused */ + elf_rel_set_symbol(&info->rhdr, "entry32_regs", ®s, sizeof(regs)); + } + else { + error_msg = "Unknown argument style\n"; + } + +out: + free(command_line); + free(modified_cmdline); + if (error_msg) + die("%s", error_msg); + return result; +} diff --git a/kexec/arch/i386/kexec-mb2-x86.c b/kexec/arch/i386/kexec-mb2-x86.c new file mode 100644 index 0000000..0d2e93b --- /dev/null +++ b/kexec/arch/i386/kexec-mb2-x86.c @@ -0,0 +1,616 @@ +/* + * kexec-mb2-x86.c + * + * multiboot2 support for kexec to boot xen. + * + * Copyright (C) 2019 Varad Gautam (vrd at amazon.de), Amazon.com, Inc. or its affiliates. + * + * Parts based on GNU GRUB, Copyright (C) 2000 Free Software Foundation, Inc + * Parts taken from kexec-multiboot-x86.c, Eric Biederman (ebiederm@xmission.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + */ + +#define _GNU_SOURCE +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <limits.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <getopt.h> +#include <elf.h> +#include <boot/elf_boot.h> +#include <ip_checksum.h> +#include "../../kexec.h" +#include "../../kexec-elf.h" +#include "kexec-x86.h" +#include "../../kexec-syscall.h" +#include "../../kexec-xen.h" +#include <arch/options.h> + +/* From GNU GRUB */ +#include <x86/multiboot2.h> +#include <x86/mb_info.h> + +/* Framebuffer */ +#include <sys/ioctl.h> +#include <linux/fb.h> + +extern struct arch_options_t arch_options; + +/* Static storage */ +static char headerbuf[MULTIBOOT_SEARCH]; +static struct multiboot_header *mbh = NULL; +struct multiboot2_header_info { + struct multiboot_header_tag_information_request *request_tag; + struct multiboot_header_tag_address *addr_tag; + struct multiboot_header_tag_entry_address *entry_addr_tag; + struct multiboot_header_tag_console_flags *console_tag; + struct multiboot_header_tag_framebuffer *fb_tag; + struct multiboot_header_tag_module_align *mod_align_tag; + struct multiboot_header_tag_relocatable *rel_tag; +} mhi; + +#define ALIGN_UP(addr, align) \ + ((addr + (typeof (addr)) align - 1) & ~((typeof (addr)) align - 1)) + +int multiboot2_x86_probe(const char *buf, off_t buf_len) +/* Is it a good idea to try booting this file? */ +{ + int i, len; + + /* First of all, check that this is an ELF file for either x86 or x86-64 */ + i = elf_x86_any_probe(buf, buf_len, CORE_TYPE_UNDEF); + if (i < 0) + return i; + + /* Now look for a multiboot header. */ + len = MULTIBOOT_SEARCH; + if (len > buf_len) + len = buf_len; + + memcpy(headerbuf, buf, len); + if (len < sizeof(struct multiboot_header)) { + /* Short file */ + return -1; + } + for (mbh = (struct multiboot_header *) headerbuf; + ((char *) mbh <= (char *) headerbuf + len - sizeof(struct multiboot_header)); + mbh = (struct multiboot_header *) ((char *) mbh + MULTIBOOT_HEADER_ALIGN)) { + if (mbh->magic == MULTIBOOT2_HEADER_MAGIC + && !((mbh->magic+mbh->architecture+mbh->header_length+mbh->checksum) & 0xffffffff)) { + /* Found multiboot header. */ + return 0; + } + } + /* Not multiboot */ + return -1; +} + +void multiboot2_x86_usage(void) +/* Multiboot-specific options */ +{ + printf(" --command-line=STRING Set the kernel command line to STRING.\n"); + printf(" --reuse-cmdline Use kernel command line from running system.\n"); + printf(" --module=\"MOD arg1 arg2...\" Load module MOD with command-line \"arg1...\"\n"); + printf(" (can be used multiple times).\n"); +} + +static size_t +multiboot2_get_mbi_size(int ranges, int cmdline_size, int modcount, int modcmd_size) +{ + size_t mbi_size; + + mbi_size = (2 * sizeof (uint32_t) /* u32 total_size, u32 reserved */ + + ALIGN_UP (sizeof (struct multiboot_tag_basic_meminfo), MULTIBOOT_TAG_ALIGN) + + ALIGN_UP ((sizeof (struct multiboot_tag_mmap) + + ranges * sizeof (struct multiboot_mmap_entry)), MULTIBOOT_TAG_ALIGN) + + (sizeof (struct multiboot_tag_string) + + ALIGN_UP (cmdline_size, MULTIBOOT_TAG_ALIGN)) + + (sizeof (struct multiboot_tag_string) + + ALIGN_UP (strlen(BOOTLOADER " " BOOTLOADER_VERSION) + 1, MULTIBOOT_TAG_ALIGN)) + + (modcount * sizeof (struct multiboot_tag_module) + modcmd_size)) + + sizeof (struct multiboot_tag); /* end tag */ + + if (mhi.rel_tag) + mbi_size += ALIGN_UP (sizeof (struct multiboot_tag_load_base_addr), MULTIBOOT_TAG_ALIGN); + + if (mhi.fb_tag) + mbi_size += ALIGN_UP (sizeof (struct multiboot_tag_framebuffer), MULTIBOOT_TAG_ALIGN); + + return mbi_size; +} + +static void multiboot2_read_header_tags(void) +{ + struct multiboot_header_tag *tag; + + for (tag = (struct multiboot_header_tag *) (mbh + 1); + tag->type != MULTIBOOT_TAG_TYPE_END; + tag = (struct multiboot_header_tag *) ((char *) tag + ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN))) + { + switch (tag->type) + { + case MULTIBOOT_HEADER_TAG_INFORMATION_REQUEST: + { + mhi.request_tag = (struct multiboot_header_tag_information_request *) tag; + break; + } + case MULTIBOOT_HEADER_TAG_RELOCATABLE: + { + mhi.rel_tag = (struct multiboot_header_tag_relocatable *) tag; + break; + } + case MULTIBOOT_HEADER_TAG_ADDRESS: + { + mhi.addr_tag = (struct multiboot_header_tag_address *) tag; + break; + } + case MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS: + { + mhi.entry_addr_tag = (struct multiboot_header_tag_entry_address *) tag; + break; + } + case MULTIBOOT_HEADER_TAG_CONSOLE_FLAGS: + { + mhi.console_tag = (struct multiboot_header_tag_console_flags *) tag; + break; + } + case MULTIBOOT_HEADER_TAG_FRAMEBUFFER: + { + mhi.fb_tag = (struct multiboot_header_tag_framebuffer *) tag; + break; + } + case MULTIBOOT_HEADER_TAG_MODULE_ALIGN: + { + mhi.mod_align_tag = (struct multiboot_header_tag_module_align *) tag; + break; + } + case MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS_EFI64: + case MULTIBOOT_HEADER_TAG_EFI_BS: + /* Ignoring EFI. */ + break; + default: + { + if (!(tag->flags & MULTIBOOT_HEADER_TAG_OPTIONAL)) + fprintf(stderr, "unsupported tag: 0x%x", tag->type); + break; + } + } + } +} + +struct multiboot_mmap_entry *multiboot_construct_memory_map(struct memory_range *range, + int ranges, + unsigned long long *mem_lower, + unsigned long long *mem_upper) +{ + struct multiboot_mmap_entry *entries; + int i; + + *mem_lower = *mem_upper = 0; + entries = xmalloc(ranges * sizeof(*entries)); + for (i = 0; i < ranges; i++) { + entries[i].addr = range[i].start; + entries[i].len = range[i].end - range[i].start + 1; + + if (range[i].type == RANGE_RAM) { + entries[i].type = MULTIBOOT_MEMORY_AVAILABLE; + /* + * Is this the "low" memory? Can't just test + * against zero, because Linux protects (and + * hides) the first few pages of physical + * memory. + */ + + if ((range[i].start <= 64*1024) + && (range[i].end > *mem_lower)) { + range[i].start = 0; + *mem_lower = range[i].end; + } + /* Is this the "high" memory? */ + if ((range[i].start <= 0x100000) + && (range[i].end > *mem_upper + 0x100000)) + *mem_upper = range[i].end - 0x100000; + } else if (range[i].type == RANGE_ACPI) + entries[i].type = MULTIBOOT_MEMORY_ACPI_RECLAIMABLE; + else if (range[i].type == RANGE_ACPI_NVS) + entries[i].type = MULTIBOOT_MEMORY_NVS; + else if (range[i].type == RANGE_RESERVED) + entries[i].type = MULTIBOOT_MEMORY_RESERVED; + } + return entries; +} + +static uint64_t multiboot2_make_mbi(struct kexec_info *info, char *cmdline, int cmdline_len, + unsigned long load_base_addr, void *mbi_buf, size_t mbi_bytes) +{ + uint64_t *ptrorig = mbi_buf; + struct multiboot_mmap_entry *mmap_entries; + unsigned long long mem_lower = 0, mem_upper = 0; + + *ptrorig = mbi_bytes; /* u32 total_size, u32 reserved */ + ptrorig++; + + mmap_entries = multiboot_construct_memory_map(info->memory_range, info->memory_ranges, &mem_lower, &mem_upper); + { + struct multiboot_tag_basic_meminfo *tag = (struct multiboot_tag_basic_meminfo *) ptrorig; + + tag->type = MULTIBOOT_TAG_TYPE_BASIC_MEMINFO; + tag->size = sizeof (struct multiboot_tag_basic_meminfo); + tag->mem_lower = mem_lower >> 10; + tag->mem_upper = mem_upper >> 10; + ptrorig += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN) / sizeof (*ptrorig); + } + + { + struct multiboot_tag_mmap *tag = (struct multiboot_tag_mmap *) ptrorig; + + tag->type = MULTIBOOT_TAG_TYPE_MMAP; + tag->size = sizeof(struct multiboot_tag_mmap) + sizeof(struct multiboot_mmap_entry) * info->memory_ranges; + tag->entry_size = sizeof(struct multiboot_mmap_entry); + tag->entry_version = 0; + memcpy(tag->entries, mmap_entries, tag->entry_size * info->memory_ranges); + ptrorig += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN) / sizeof (*ptrorig); + } + + if (mhi.rel_tag) { + struct multiboot_tag_load_base_addr *tag = (struct multiboot_tag_load_base_addr *) ptrorig; + + tag->type = MULTIBOOT_TAG_TYPE_LOAD_BASE_ADDR; + tag->size = sizeof (struct multiboot_tag_load_base_addr); + tag->load_base_addr = load_base_addr; + ptrorig += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN) / sizeof (*ptrorig); + } + + { + struct multiboot_tag_string *tag = (struct multiboot_tag_string *) ptrorig; + + tag->type = MULTIBOOT_TAG_TYPE_CMDLINE; + tag->size = sizeof (struct multiboot_tag_string) + cmdline_len; + memcpy(tag->string, cmdline, cmdline_len); + ptrorig += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN) / sizeof (*ptrorig); + } + + { + struct multiboot_tag_string *tag = (struct multiboot_tag_string *) ptrorig; + + tag->type = MULTIBOOT_TAG_TYPE_BOOT_LOADER_NAME; + tag->size = sizeof(struct multiboot_tag_string) + strlen(BOOTLOADER " " BOOTLOADER_VERSION) + 1; + sprintf(tag->string, "%s", BOOTLOADER " " BOOTLOADER_VERSION); + ptrorig += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN) / sizeof (*ptrorig); + } + + if (mhi.fb_tag) { + struct multiboot_tag_framebuffer *tag = (struct multiboot_tag_framebuffer *) ptrorig; + struct fb_fix_screeninfo info; + struct fb_var_screeninfo mode; + int fd; + + tag->common.type = MULTIBOOT_TAG_TYPE_FRAMEBUFFER; + tag->common.size = sizeof(struct multiboot_tag_framebuffer); + /* check if purgatory will reset to standard ega text mode */ + if (arch_options.reset_vga || arch_options.console_vga) { + tag->common.framebuffer_type = MB_FRAMEBUFFER_TYPE_EGA_TEXT; + tag->common.framebuffer_addr = 0xb8000; + tag->common.framebuffer_pitch = 80*2; + tag->common.framebuffer_width = 80; + tag->common.framebuffer_height = 25; + tag->common.framebuffer_bpp = 16; + + ptrorig += ALIGN_UP (tag->common.size, MULTIBOOT_TAG_ALIGN) / sizeof (*ptrorig); + goto out; + } + + /* use current graphics framebuffer settings */ + fd = open("/dev/fb0", O_RDONLY); + if (fd < 0) { + fprintf(stderr, "can't open /dev/fb0: %s\n", strerror(errno)); + goto out; + } + if (ioctl(fd, FBIOGET_FSCREENINFO, &info) < 0){ + fprintf(stderr, "can't get screeninfo: %s\n", strerror(errno)); + close(fd); + goto out; + } + if (ioctl(fd, FBIOGET_VSCREENINFO, &mode) < 0){ + fprintf(stderr, "can't get modeinfo: %s\n", strerror(errno)); + close(fd); + goto out; + } + close(fd); + + if (info.smem_start == 0 || info.smem_len == 0) { + fprintf(stderr, "can't get linerar framebuffer address\n"); + goto out; + } + + if (info.type != FB_TYPE_PACKED_PIXELS) { + fprintf(stderr, "unsupported framebuffer type\n"); + goto out; + } + + if (info.visual != FB_VISUAL_TRUECOLOR) { + fprintf(stderr, "unsupported framebuffer visual\n"); + goto out; + } + + tag->common.framebuffer_type = MB_FRAMEBUFFER_TYPE_RGB; + tag->common.framebuffer_addr = info.smem_start; + tag->common.framebuffer_pitch = info.line_length; + tag->common.framebuffer_width = mode.xres; + tag->common.framebuffer_height = mode.yres; + tag->common.framebuffer_bpp = mode.bits_per_pixel; + + tag->framebuffer_red_field_position = mode.red.offset; + tag->framebuffer_red_mask_size = mode.red.length; + tag->framebuffer_green_field_position = mode.green.offset; + tag->framebuffer_green_mask_size = mode.green.length; + tag->framebuffer_blue_field_position = mode.blue.offset; + tag->framebuffer_blue_mask_size = mode.blue.length; + + ptrorig += ALIGN_UP (tag->common.size, MULTIBOOT_TAG_ALIGN) / sizeof (*ptrorig); + } + +out: + return (uint64_t) (uintptr_t) ptrorig; +} + +static uint64_t multiboot2_mbi_add_module(void *mbi_buf, uint64_t mbi_ptr, uint32_t mod_start, + uint32_t mod_end, char *mod_clp) +{ + struct multiboot_tag_module *tag = (struct multiboot_tag_module *) (uintptr_t) mbi_ptr; + + tag->type = MULTIBOOT_TAG_TYPE_MODULE; + tag->size = sizeof(struct multiboot_tag_module) + strlen((char *)(long) mod_clp) + 1; + tag->mod_start = mod_start; + tag->mod_end = mod_end; + + memcpy(tag->cmdline, (char *)(long) mod_clp, strlen((char *)(long) mod_clp) + 1); + mbi_ptr += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN); + + return mbi_ptr; +} + +static uint64_t multiboot2_mbi_end(void *mbi_buf, uint64_t mbi_ptr) +{ + struct multiboot_tag *tag = (struct multiboot_tag *) (uintptr_t) mbi_ptr; + + tag->type = MULTIBOOT_TAG_TYPE_END; + tag->size = sizeof (struct multiboot_tag); + mbi_ptr += ALIGN_UP (tag->size, MULTIBOOT_TAG_ALIGN); + + return mbi_ptr; +} + +static inline int multiboot2_rel_valid(struct multiboot_header_tag_relocatable *rel_tag, + uint64_t rel_start, uint64_t rel_end) +{ + if (rel_start >= rel_tag->min_addr && rel_end <= rel_tag->max_addr) + return 1; + + return 0; +} + +int multiboot2_x86_load(int argc, char **argv, const char *buf, off_t len, + struct kexec_info *info) +{ + struct mem_ehdr ehdr; + void *mbi_buf; + size_t mbi_bytes; + unsigned long addr; + struct entry32_regs regs; + char *command_line = NULL, *tmp_cmdline = NULL; + int command_line_len; + char *imagename, *cp, *append = NULL;; + int opt; + int modules, mod_command_line_space; + uint64_t mbi_ptr; + char *mod_clp_base; + /* See options.h -- add any more there, too. */ + static const struct option options[] = { + KEXEC_ARCH_OPTIONS + { "command-line", 1, 0, OPT_CL }, + { "append", 1, 0, OPT_CL }, + { "reuse-cmdline", 0, 0, OPT_REUSE_CMDLINE }, + { "module", 1, 0, OPT_MOD }, + { 0, 0, 0, 0 }, + }; + static const char short_options[] = KEXEC_ARCH_OPT_STR ""; + uint64_t rel_min, rel_max; + + /* Probe for the MB header if it's not already found */ + if (mbh == NULL && multiboot_x86_probe(buf, len) != 1) + { + fprintf(stderr, "Cannot find a loadable multiboot2 header.\n"); + return -1; + } + + /* Parse the header tags. */ + multiboot2_read_header_tags(); + + /* Parse the command line */ + command_line_len = 0; + modules = 0; + mod_command_line_space = 0; + while((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) + { + switch(opt) { + default: + /* Ignore core options */ + if (opt < OPT_ARCH_MAX) { + break; + } + case OPT_CL: + append = optarg; + break; + case OPT_REUSE_CMDLINE: + tmp_cmdline = get_command_line(); + break; + case OPT_MOD: + modules++; + mod_command_line_space += strlen(optarg) + 1; + break; + } + } + imagename = argv[optind]; + + /* Final command line = imagename + <OPT_REUSE_CMDLINE> + <OPT_CL> */ + tmp_cmdline = concat_cmdline(command_line, append); + if (command_line) { + free(command_line); + } + command_line = concat_cmdline(imagename, tmp_cmdline); + if (tmp_cmdline) { + free(tmp_cmdline); + } + + if (xen_present() && info->kexec_flags & KEXEC_LIVE_UPDATE ) { + if (!mhi.rel_tag) { + fprintf(stderr, "Multiboot2 image must be relocatable" + "for KEXEC_LIVE_UPDATE.\n"); + return -1; + } + cmdline_add_liveupdate(&command_line); + } + + command_line_len = strlen(command_line) + 1; + + /* Load the ELF executable */ + if (mhi.rel_tag) { + rel_min = mhi.rel_tag->min_addr; + rel_max = mhi.rel_tag->max_addr; + + if (info->kexec_flags & KEXEC_LIVE_UPDATE && xen_present()) { + /* TODO also check if elf is xen */ + /* On a live update, load target xen over the current xen image. */ + uint64_t xen_start, xen_end; + + xen_get_kexec_range(KEXEC_RANGE_MA_XEN, &xen_start, &xen_end); + if (multiboot2_rel_valid(mhi.rel_tag, xen_start, xen_end)) { + rel_min = xen_start; + } else { + fprintf(stderr, "Cannot place Elf into " + "KEXEC_RANGE_MA_XEN for KEXEC_LIVE_UPDATE.\n"); + return -1; + } + } + + elf_exec_build_load_relocatable(info, &ehdr, buf, len, 0, + rel_min, rel_max, mhi.rel_tag->align); + } else + elf_exec_build_load(info, &ehdr, buf, len, 0); + + if (info->kexec_flags & KEXEC_LIVE_UPDATE && xen_present()) { + uint64_t lu_start, lu_end; + + xen_get_kexec_range(7 /* KEXEC_RANGE_MA_LIVEUPDATE */, &lu_start, &lu_end); + /* Fit everything else into lu_start-lu_end. First page after lu_start is + * reserved for LU breadcrumb. */ + rel_min = lu_start + 4096; + rel_max = lu_end; + } else { + rel_min = 0x500; + rel_max = ULONG_MAX; + } + + /* Load the setup code */ + elf_rel_build_load(info, &info->rhdr, purgatory, purgatory_size, + rel_min, rel_max, 1, 0); + + /* Construct information tags. */ + mbi_bytes = multiboot2_get_mbi_size(info->memory_ranges, command_line_len, modules, mod_command_line_space); + mbi_buf = xmalloc(mbi_bytes); + + mbi_ptr = multiboot2_make_mbi(info, command_line, command_line_len, info->rhdr.rel_addr, mbi_buf, mbi_bytes); + free(command_line); + + if (info->kexec_flags & KEXEC_LIVE_UPDATE && xen_present()) { + if (multiboot2_rel_valid(mhi.rel_tag, rel_min, rel_max)) { + /* Shrink the reloc range to fit into LU region for xen. */ + mhi.rel_tag->min_addr = rel_min; + mhi.rel_tag->max_addr = rel_max; + } else { + fprintf(stderr, "Multiboot2 image cannot be relocated into " + "KEXEC_RANGE_MA_LIVEUPDATE for KEXEC_LIVE_UPDATE.\n"); + return -1; + } + } + + /* Load modules */ + if (modules) { + char *mod_filename, *mod_command_line, *mod_clp, *buf; + off_t mod_size; + int i = 0; + + mod_clp_base = xmalloc(mod_command_line_space); + + /* Go back and parse the module command lines */ + mod_clp = mod_clp_base; + optind = opterr = 1; + while((opt = getopt_long(argc, argv, + short_options, options, 0)) != -1) { + if (opt != OPT_MOD) continue; + + /* Split module filename from command line */ + mod_command_line = mod_filename = optarg; + if ((cp = strchr(mod_filename, ' ')) != NULL) { + /* See as I discard the 'const' modifier */ + *cp = '\0'; + } + + /* Load the module */ + buf = slurp_decompress_file(mod_filename, &mod_size); + + if (cp != NULL) *cp = ' '; + + /* Pick the next aligned spot to load it in. Always page align. */ + addr = add_buffer(info, buf, mod_size, mod_size, getpagesize(), + rel_min, rel_max, 1); + + /* Add the module command line */ + sprintf(mod_clp, "%s", mod_command_line); + + mbi_ptr = multiboot2_mbi_add_module(mbi_buf, mbi_ptr, addr, addr + mod_size, mod_clp); + + mod_clp += strlen(mod_clp) + 1; + i++; + } + + free(mod_clp_base); + } + + mbi_ptr = multiboot2_mbi_end(mbi_buf, mbi_ptr); + + if (sort_segments(info) < 0) + return -1; + + addr = add_buffer(info, mbi_buf, mbi_bytes, mbi_bytes, 4, + rel_min, rel_max, 1); + + elf_rel_get_symbol(&info->rhdr, "entry32_regs", ®s, sizeof(regs)); + regs.eax = MULTIBOOT2_BOOTLOADER_MAGIC; + regs.ebx = addr; + regs.eip = ehdr.e_entry; + elf_rel_set_symbol(&info->rhdr, "entry32_regs", ®s, sizeof(regs)); + + return 0; +} diff --git a/kexec/arch/i386/kexec-multiboot-x86.c b/kexec/arch/i386/kexec-multiboot-x86.c new file mode 100644 index 0000000..33c885a --- /dev/null +++ b/kexec/arch/i386/kexec-multiboot-x86.c @@ -0,0 +1,505 @@ +/* + * kexec-multiboot-x86.c + * + * (partial) multiboot support for kexec. Only supports ELF32 + * kernels, and a subset of the multiboot info page options + * (i.e. enough to boot the Xen hypervisor). + * + * TODO: + * - smarter allocation of new segments + * - proper support for the MULTIBOOT_VIDEO_MODE bit + * + * + * Copyright (C) 2003 Tim Deegan (tjd21 at cl.cam.ac.uk) + * + * Parts based on GNU GRUB, Copyright (C) 2000 Free Software Foundation, Inc + * Parts copied from kexec-elf32-x86.c, written by Eric Biederman + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + */ + +#define _GNU_SOURCE +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <limits.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <getopt.h> +#include <elf.h> +#include <boot/elf_boot.h> +#include <ip_checksum.h> +#include "../../kexec.h" +#include "../../kexec-elf.h" +#include "kexec-x86.h" +#include <arch/options.h> + +/* From GNU GRUB */ +#include <x86/mb_header.h> +#include <x86/mb_info.h> + +/* Framebuffer */ +#include <sys/ioctl.h> +#include <linux/fb.h> + +extern struct arch_options_t arch_options; + +/* Static storage */ +static char headerbuf[MULTIBOOT_SEARCH]; +static struct multiboot_header *mbh = NULL; +static off_t mbh_offset = 0; + +#define MIN(_x,_y) (((_x)<=(_y))?(_x):(_y)) + + +int multiboot_x86_probe(const char *buf, off_t buf_len) +/* Is it a good idea to try booting this file? */ +{ + int i, len; + /* Now look for a multiboot header in the first 8KB */ + len = MULTIBOOT_SEARCH; + if (len > buf_len) { + len = buf_len; + } + memcpy(headerbuf, buf, len); + if (len < 12) { + /* Short file */ + return -1; + } + for (mbh_offset = 0; mbh_offset <= (len - 12); mbh_offset += 4) + { + /* Search for a multiboot header */ + mbh = (struct multiboot_header *)(headerbuf + mbh_offset); + if (mbh->magic != MULTIBOOT_MAGIC + || ((mbh->magic+mbh->flags+mbh->checksum) & 0xffffffff)) + { + /* Not a multiboot header */ + continue; + } + if (mbh->flags & MULTIBOOT_AOUT_KLUDGE) { + if (mbh->load_addr & 0xfff) { + fprintf(stderr, "multiboot load address not 4k aligned\n"); + return -1; + } + if (mbh->load_addr > mbh->header_addr) { + fprintf(stderr, "multiboot header address > load address\n"); + return -1; + } + if (mbh->load_end_addr < mbh->load_addr) { + fprintf(stderr, "multiboot load end address < load address\n"); + return -1; + } + if (mbh->bss_end_addr < mbh->load_end_addr) { + fprintf(stderr, "multiboot bss end address < load end address\n"); + return -1; + } + if (mbh->load_end_addr - mbh->header_addr > buf_len - mbh_offset) { + fprintf(stderr, "multiboot file truncated\n"); + return -1; + } + if (mbh->entry_addr < mbh->load_addr || mbh->entry_addr >= mbh->load_end_addr) { + fprintf(stderr, "multiboot entry out of range\n"); + return -1; + } + } else { + if ((i=elf_x86_probe(buf, buf_len)) < 0) + return i; + } + if (mbh->flags & MULTIBOOT_UNSUPPORTED) { + /* Requires options we don't support */ + fprintf(stderr, + "Found a multiboot header, but it " + "requires multiboot options that I\n" + "don't understand. Sorry.\n"); + return -1; + } + /* Bootable */ + return 0; + } + /* Not multiboot */ + return -1; +} + + +void multiboot_x86_usage(void) +/* Multiboot-specific options */ +{ + printf(" --command-line=STRING Set the kernel command line to STRING.\n"); + printf(" --reuse-cmdline Use kernel command line from running system.\n"); + printf(" --module=\"MOD arg1 arg2...\" Load module MOD with command-line \"arg1...\"\n"); + printf(" (can be used multiple times).\n"); +} + + +static int framebuffer_info(struct multiboot_info *mbi) +{ + struct fb_fix_screeninfo info; + struct fb_var_screeninfo mode; + int fd; + + /* check if purgatory will reset to standard ega text mode */ + if (arch_options.reset_vga || arch_options.console_vga) { + mbi->framebuffer_type = MB_FRAMEBUFFER_TYPE_EGA_TEXT; + mbi->framebuffer_addr = 0xb8000; + mbi->framebuffer_pitch = 80*2; + mbi->framebuffer_width = 80; + mbi->framebuffer_height = 25; + mbi->framebuffer_bpp = 16; + + mbi->flags |= MB_INFO_FRAMEBUFFER_INFO; + return 0; + } + + /* use current graphics framebuffer settings */ + fd = open("/dev/fb0", O_RDONLY); + if (fd < 0) { + fprintf(stderr, "can't open /dev/fb0: %s\n", strerror(errno)); + return -1; + } + if (ioctl(fd, FBIOGET_FSCREENINFO, &info) < 0){ + fprintf(stderr, "can't get screeninfo: %s\n", strerror(errno)); + close(fd); + return -1; + } + if (ioctl(fd, FBIOGET_VSCREENINFO, &mode) < 0){ + fprintf(stderr, "can't get modeinfo: %s\n", strerror(errno)); + close(fd); + return -1; + } + close(fd); + + if (info.smem_start == 0 || info.smem_len == 0) { + fprintf(stderr, "can't get linerar framebuffer address\n"); + return -1; + } + + if (info.type != FB_TYPE_PACKED_PIXELS) { + fprintf(stderr, "unsupported framebuffer type\n"); + return -1; + } + + if (info.visual != FB_VISUAL_TRUECOLOR) { + fprintf(stderr, "unsupported framebuffer visual\n"); + return -1; + } + + mbi->framebuffer_type = MB_FRAMEBUFFER_TYPE_RGB; + mbi->framebuffer_addr = info.smem_start; + mbi->framebuffer_pitch = info.line_length; + mbi->framebuffer_width = mode.xres; + mbi->framebuffer_height = mode.yres; + mbi->framebuffer_bpp = mode.bits_per_pixel; + mbi->framebuffer_red_field_position = mode.red.offset; + mbi->framebuffer_red_mask_size = mode.red.length; + mbi->framebuffer_green_field_position = mode.green.offset; + mbi->framebuffer_green_mask_size = mode.green.length; + mbi->framebuffer_blue_field_position = mode.blue.offset; + mbi->framebuffer_blue_mask_size = mode.blue.length; + + mbi->flags |= MB_INFO_FRAMEBUFFER_INFO; + return 0; +} + +int multiboot_x86_load(int argc, char **argv, const char *buf, off_t len, + struct kexec_info *info) +/* Marshal up a multiboot-style kernel */ +{ + struct multiboot_info *mbi; + void *mbi_buf; + struct mod_list *modp; + unsigned long freespace; + unsigned long long mem_lower = 0, mem_upper = 0; + struct mem_ehdr ehdr; + unsigned long mbi_base; + struct entry32_regs regs; + size_t mbi_bytes, mbi_offset; + char *command_line = NULL, *tmp_cmdline = NULL; + char *imagename, *cp, *append = NULL;; + struct memory_range *range; + int ranges; + struct AddrRangeDesc *mmap; + int command_line_len; + int i, result; + uint32_t u, entry; + int opt; + int modules, mod_command_line_space; + /* See options.h -- add any more there, too. */ + static const struct option options[] = { + KEXEC_ARCH_OPTIONS + { "command-line", 1, 0, OPT_CL }, + { "append", 1, 0, OPT_CL }, + { "reuse-cmdline", 0, 0, OPT_REUSE_CMDLINE }, + { "module", 1, 0, OPT_MOD }, + { 0, 0, 0, 0 }, + }; + static const char short_options[] = KEXEC_ARCH_OPT_STR ""; + + /* Probe for the MB header if it's not already found */ + if (mbh == NULL && multiboot_x86_probe(buf, len) != 1) { + fprintf(stderr, "Cannot find a loadable multiboot header.\n"); + return -1; + } + + /* Parse the command line */ + command_line_len = 0; + modules = 0; + mod_command_line_space = 0; + result = 0; + while((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) { + switch(opt) { + default: + /* Ignore core options */ + if (opt < OPT_ARCH_MAX) { + break; + } + case OPT_CL: + append = optarg; + break; + case OPT_REUSE_CMDLINE: + command_line = get_command_line(); + break; + case OPT_MOD: + modules++; + mod_command_line_space += strlen(optarg) + 1; + break; + } + } + imagename = argv[optind]; + + /* Final command line = imagename + <OPT_REUSE_CMDLINE> + <OPT_CL> */ + tmp_cmdline = concat_cmdline(command_line, append); + if (command_line) { + free(command_line); + } + command_line = concat_cmdline(imagename, tmp_cmdline); + if (tmp_cmdline) { + free(tmp_cmdline); + } + command_line_len = strlen(command_line) + 1; + + if (mbh->flags & MULTIBOOT_AOUT_KLUDGE) { + add_segment(info, + buf + (mbh_offset - (mbh->header_addr - mbh->load_addr)), + mbh->load_end_addr - mbh->load_addr, + mbh->load_addr, + mbh->bss_end_addr - mbh->load_addr); + entry = mbh->entry_addr; + } else { + /* Load the ELF executable */ + elf_exec_build_load(info, &ehdr, buf, len, 0); + entry = ehdr.e_entry; + } + + /* Load the setup code */ + elf_rel_build_load(info, &info->rhdr, purgatory, purgatory_size, 0, + ULONG_MAX, 1, 0); + + /* The first segment will contain the multiboot headers: + * ============= + * multiboot information (mbi) + * ------------- + * kernel command line + * ------------- + * bootloader name + * ------------- + * module information entries + * ------------- + * module command lines + * ============== + */ + mbi_bytes = _ALIGN(sizeof(*mbi) + command_line_len + + strlen (BOOTLOADER " " BOOTLOADER_VERSION) + 1, 4); + mbi_buf = xmalloc(mbi_bytes); + mbi = mbi_buf; + memset(mbi, 0, sizeof(*mbi)); + sprintf(((char *)mbi) + sizeof(*mbi), "%s", command_line); + sprintf(((char *)mbi) + sizeof(*mbi) + command_line_len, "%s", + BOOTLOADER " " BOOTLOADER_VERSION); + mbi->flags = MB_INFO_CMDLINE | MB_INFO_BOOT_LOADER_NAME; + /* We'll relocate these to absolute addresses later. For now, + * all addresses within the first segment are relative to the + * start of the MBI. */ + mbi->cmdline = sizeof(*mbi); + mbi->boot_loader_name = sizeof(*mbi) + command_line_len; + + /* Memory map */ + range = info->memory_range; + ranges = info->memory_ranges; + mmap = xmalloc(ranges * sizeof(*mmap)); + for (i=0; i<ranges; i++) { + unsigned long long length; + length = range[i].end - range[i].start + 1; + /* Translate bzImage mmap to multiboot-speak */ + mmap[i].size = sizeof(mmap[i]) - 4; + mmap[i].base_addr_low = range[i].start & 0xffffffff; + mmap[i].base_addr_high = range[i].start >> 32; + mmap[i].length_low = length & 0xffffffff; + mmap[i].length_high = length >> 32; + switch (range[i].type) { + case RANGE_RAM: + mmap[i].Type = 1; /* RAM */ + /* + * Is this the "low" memory? Can't just test + * against zero, because Linux protects (and + * hides) the first few pages of physical + * memory. + */ + + if ((range[i].start <= 64*1024) + && (range[i].end > mem_lower)) { + range[i].start = 0; + mem_lower = range[i].end; + } + /* Is this the "high" memory? */ + if ((range[i].start <= 0x100000) + && (range[i].end > mem_upper + 0x100000)) + mem_upper = range[i].end - 0x100000; + break; + case RANGE_ACPI: + mmap[i].Type = 3; + break; + case RANGE_ACPI_NVS: + mmap[i].Type = 4; + break; + case RANGE_RESERVED: + default: + mmap[i].Type = 2; /* Not RAM (reserved) */ + } + } + + if (mbh->flags & MULTIBOOT_MEMORY_INFO) { + /* Provide a copy of the memory map to the kernel */ + + mbi->flags |= MB_INFO_MEMORY | MB_INFO_MEM_MAP; + + freespace = add_buffer(info, + mmap, ranges * sizeof(*mmap), ranges * sizeof(*mmap), + 4, 0, 0xFFFFFFFFUL, 1); + + mbi->mmap_addr = freespace; + mbi->mmap_length = ranges * sizeof(*mmap); + + /* For kernels that care naught for fancy memory maps + * and just want the size of low and high memory */ + mbi->mem_lower = MIN(mem_lower>>10, 0xffffffff); + mbi->mem_upper = MIN(mem_upper>>10, 0xffffffff); + + /* done */ + } + + /* Video */ + if (mbh->flags & MULTIBOOT_VIDEO_MODE) { + if (framebuffer_info(mbi) < 0) + fprintf(stderr, "not providing framebuffer information.\n"); + } + + /* Load modules */ + if (modules) { + char *mod_filename, *mod_command_line, *mod_clp, *buf; + off_t mod_size; + + /* We'll relocate this to an absolute address later */ + mbi->mods_addr = mbi_bytes; + mbi->mods_count = 0; + mbi->flags |= MB_INFO_MODS; + + /* Add room for the module descriptors to the MBI buffer */ + mbi_bytes += (sizeof(*modp) * modules) + + mod_command_line_space; + mbi_buf = xrealloc(mbi_buf, mbi_bytes); + + /* mbi might have moved */ + mbi = mbi_buf; + /* module descriptors go in the newly added space */ + modp = ((void *)mbi) + mbi->mods_addr; + /* module command lines go after the descriptors */ + mod_clp = ((void *)modp) + (sizeof(*modp) * modules); + + /* Go back and parse the module command lines */ + optind = opterr = 1; + while((opt = getopt_long(argc, argv, + short_options, options, 0)) != -1) { + if (opt != OPT_MOD) continue; + + /* Split module filename from command line */ + mod_command_line = mod_filename = optarg; + if ((cp = strchr(mod_filename, ' ')) != NULL) { + /* See as I discard the 'const' modifier */ + *cp = '\0'; + } + + /* Load the module */ + buf = slurp_decompress_file(mod_filename, &mod_size); + + if (cp != NULL) *cp = ' '; + + /* Pick the next aligned spot to load it in */ + freespace = add_buffer(info, + buf, mod_size, mod_size, + getpagesize(), 0, 0xffffffffUL, 1); + + /* Add the module command line */ + sprintf(mod_clp, "%s", mod_command_line); + + modp->mod_start = freespace; + modp->mod_end = freespace + mod_size; + modp->cmdline = (void *)mod_clp - (void *)mbi; + modp->pad = 0; + + /* Done */ + mbi->mods_count++; + mod_clp += strlen(mod_clp) + 1; + modp++; + } + } + + /* Find a place for the MBI to live */ + if (sort_segments(info) < 0) { + result = -1; + goto out; + } + mbi_base = add_buffer(info, + mbi_buf, mbi_bytes, mbi_bytes, 4, 0, 0xFFFFFFFFUL, 1); + + /* Relocate offsets in the MBI to absolute addresses */ + mbi_offset = mbi_base; + modp = ((void *)mbi) + mbi->mods_addr; + for (u = 0; u < mbi->mods_count; u++) { + modp[u].cmdline += mbi_offset; + } + mbi->mods_addr += mbi_offset; + mbi->cmdline += mbi_offset; + mbi->boot_loader_name += mbi_offset; + + /* Specify the initial CPU state and copy the setup code */ + elf_rel_get_symbol(&info->rhdr, "entry32_regs", ®s, sizeof(regs)); + regs.eax = 0x2BADB002; + regs.ebx = mbi_offset; + regs.eip = entry; + elf_rel_set_symbol(&info->rhdr, "entry32_regs", ®s, sizeof(regs)); + +out: + free(command_line); + return result; +} + +/* + * EOF (kexec-multiboot-x86.c) + */ diff --git a/kexec/arch/i386/kexec-nbi.c b/kexec/arch/i386/kexec-nbi.c new file mode 100644 index 0000000..8eb2154 --- /dev/null +++ b/kexec/arch/i386/kexec-nbi.c @@ -0,0 +1,249 @@ +/* + * kexec: Linux boots Linux + * + * Copyright (C) 2005 Eric Biederman (ebiederm@xmission.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation (version 2 of the License). + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#define _GNU_SOURCE +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <limits.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <getopt.h> +#include <elf.h> +#include "../../kexec.h" +#include "../../kexec-elf.h" +#include "../../kexec-elf-boot.h" +#include "kexec-x86.h" +#include <arch/options.h> + +struct segheader +{ + uint8_t length; + uint8_t vendortag; + uint8_t reserved; + uint8_t flags; +#define NBI_SEG 0x3 +#define NBI_SEG_ABSOLUTE 0 +#define NBI_SEG_APPEND 1 +#define NBI_SEG_NEGATIVE 2 +#define NBI_SEG_PREPEND 3 +#define NBI_LAST_SEG (1 << 2) + uint32_t loadaddr; + uint32_t imglength; + uint32_t memlength; +}; + +struct imgheader +{ +#define NBI_MAGIC "\x36\x13\x03\x1b" + uint8_t magic[4]; +#define NBI_RETURNS (1 << 8) +#define NBI_ENTRY32 (1 << 31) + uint32_t length; /* and flags */ + struct { uint16_t bx, ds; } segoff; + union { + struct { uint16_t ip, cs; } segoff; + uint32_t linear; + } execaddr; +}; + + +static const int probe_debug = 0; + +int nbi_probe(const char *buf, off_t len) +{ + struct imgheader hdr; + struct segheader seg; + off_t seg_off; + /* If we don't have enough data give up */ + if (((uintmax_t)len < (uintmax_t)sizeof(hdr)) || (len < 512)) { + return -1; + } + memcpy(&hdr, buf, sizeof(hdr)); + if (memcmp(hdr.magic, NBI_MAGIC, sizeof(hdr.magic)) != 0) { + return -1; + } + /* Ensure we have a properly sized header */ + if (((hdr.length & 0xf)*4) != sizeof(hdr)) { + if (probe_debug) { + fprintf(stderr, "NBI: Bad vendor header size\n"); + } + return -1; + } + /* Ensure the vendor header is not too large. + * This can't actually happen but.... + */ + if ((((hdr.length & 0xf0) >> 4)*4) > (512 - sizeof(hdr))) { + if (probe_debug) { + fprintf(stderr, "NBI: vendor headr too large\n"); + } + return -1; + } + /* Reserved bits are set in the image... */ + if ((hdr.length & 0x7ffffe00)) { + if (probe_debug) { + fprintf(stderr, "NBI: Reserved header bits set\n"); + } + return -1; + } + /* If the image can return refuse to load it */ + if (hdr.length & (1 << 8)) { + if (probe_debug) { + printf("NBI: image wants to return\n"); + } + return -1; + } + /* Now verify the segments all fit within 512 bytes */ + seg_off = (((hdr.length & 0xf0) >> 4) + (hdr.length & 0x0f)) << 2; + do { + memcpy(&seg, buf + seg_off, sizeof(seg)); + if ((seg.length & 0xf) != 4) { + if (probe_debug) { + fprintf(stderr, "NBI: Invalid segment length\n"); + } + return -1; + } + seg_off += ((seg.length & 0xf) + ((seg.length >> 4) & 0xf)) << 2; + if (seg.flags & 0xf8) { + if (probe_debug) { + fprintf(stderr, "NBI: segment reserved flags set\n"); + } + return -1; + } + if ((seg.flags & NBI_SEG) == NBI_SEG_NEGATIVE) { + if (probe_debug) { + fprintf(stderr, "NBI: negative segment addresses not supported\n"); + } + return -1; + } + if (seg_off > 512) { + if (probe_debug) { + fprintf(stderr, "NBI: segment outside 512 header\n"); + } + return -1; + } + } while(!(seg.flags & NBI_LAST_SEG)); + return 0; +} + +void nbi_usage(void) +{ + printf( "\n" + ); +} + +int nbi_load(int argc, char **argv, const char *buf, off_t UNUSED(len), + struct kexec_info *info) +{ + struct imgheader hdr; + struct segheader seg; + off_t seg_off; + off_t file_off; + uint32_t last0, last1; + int opt; + + static const struct option options[] = { + KEXEC_ARCH_OPTIONS + { 0, 0, NULL, 0 }, + }; + + static const char short_options[] = KEXEC_OPT_STR ""; + + /* + * Parse the command line arguments + */ + while((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) { + switch(opt) { + default: + /* Ignore core options */ + if (opt < OPT_ARCH_MAX) { + break; + } + } + } + /* Get a copy of the header */ + memcpy(&hdr, buf, sizeof(hdr)); + + /* Load the first 512 bytes */ + add_segment(info, buf + 0, 512, + (hdr.segoff.ds << 4) + hdr.segoff.bx, 512); + + /* Initialize variables */ + file_off = 512; + last0 = (hdr.segoff.ds << 4) + hdr.segoff.bx; + last1 = last0 + 512; + + /* Load the segments */ + seg_off = (((hdr.length & 0xf0) >> 4) + (hdr.length & 0x0f)) << 2; + do { + uint32_t loadaddr; + memcpy(&seg, buf + seg_off, sizeof(seg)); + seg_off += ((seg.length & 0xf) + ((seg.length >> 4) & 0xf)) << 2; + if ((seg.flags & NBI_SEG) == NBI_SEG_ABSOLUTE) { + loadaddr = seg.loadaddr; + } + else if ((seg.flags & NBI_SEG) == NBI_SEG_APPEND) { + loadaddr = last1 + seg.loadaddr; + } +#if 0 + else if ((seg.flags & NBI_SEG) == NBI_SEG_NEGATIVE) { + loadaddr = memsize - seg.loadaddr; + } +#endif + else if ((seg.flags & NBI_SEG) == NBI_SEG_PREPEND) { + loadaddr = last0 - seg.loadaddr; + } + else { + printf("warning: unhandled segment of type %0x\n", + seg.flags & NBI_SEG); + continue; + } + add_segment(info, buf + file_off, seg.imglength, + loadaddr, seg.memlength); + last0 = loadaddr; + last1 = last0 + seg.memlength; + file_off += seg.imglength; + } while(!(seg.flags & NBI_LAST_SEG)); + + if (hdr.length & NBI_ENTRY32) { + struct entry32_regs regs32; + /* Initialize the registers */ + elf_rel_get_symbol(&info->rhdr, "entry32_regs32", ®s32, sizeof(regs32)); + regs32.eip = hdr.execaddr.linear; + elf_rel_set_symbol(&info->rhdr, "entry32_regs32", ®s32, sizeof(regs32)); + } + else { + struct entry32_regs regs32; + struct entry16_regs regs16; + + /* Initialize the 16 bit registers */ + elf_rel_get_symbol(&info->rhdr, "entry16_regs", ®s16, sizeof(regs16)); + regs16.cs = hdr.execaddr.segoff.cs; + regs16.ip = hdr.execaddr.segoff.ip; + elf_rel_set_symbol(&info->rhdr, "entry16_regs", ®s16, sizeof(regs16)); + + /* Initialize the 32 bit registers */ + elf_rel_get_symbol(&info->rhdr, "entry32_regs", ®s32, sizeof(regs32)); + regs32.eip = elf_rel_get_addr(&info->rhdr, "entry16"); + elf_rel_set_symbol(&info->rhdr, "entry32_regs", ®s32, sizeof(regs32)); + } + return 0; +} diff --git a/kexec/arch/i386/kexec-x86-common.c b/kexec/arch/i386/kexec-x86-common.c new file mode 100644 index 0000000..ffc95a9 --- /dev/null +++ b/kexec/arch/i386/kexec-x86-common.c @@ -0,0 +1,444 @@ +/* + * kexec: Linux boots Linux + * + * Copyright (C) 2003-2005 Eric Biederman (ebiederm@xmission.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation (version 2 of the License). + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#define _XOPEN_SOURCE 600 +#define _BSD_SOURCE +#define _DEFAULT_SOURCE + +#include <fcntl.h> +#include <stddef.h> +#include <stdio.h> +#include <errno.h> +#include <stdint.h> +#include <string.h> +#include <limits.h> +#include <stdlib.h> +#include <stdio.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <unistd.h> +#include "../../kexec.h" +#include "../../kexec-syscall.h" +#include "../../firmware_memmap.h" +#include "../../crashdump.h" +#include "../../kexec-xen.h" +#include "kexec-x86.h" +#include "x86-linux-setup.h" + +/* Used below but not present in (older?) xenctrl.h */ +#ifndef E820_PMEM +#define E820_PMEM 7 +#define E820_PRAM 12 +#endif + +/* + * The real mode IVT ends at 0x400. + * See https://wiki.osdev.org/Interrupt_Vector_Table. + */ +#define REALMODE_IVT_END 0x400 + +static struct memory_range memory_range[MAX_MEMORY_RANGES]; + +/** + * The old /proc/iomem parsing code. + * + * @param[out] range pointer that will be set to an array that holds the + * memory ranges + * @param[out] ranges number of ranges valid in @p range + * + * @return 0 on success, any other value on failure. + */ +static int get_memory_ranges_proc_iomem(struct memory_range **range, int *ranges) +{ + const char *iomem= proc_iomem(); + int memory_ranges = 0; + char line[MAX_LINE]; + FILE *fp; + fp = fopen(iomem, "r"); + if (!fp) { + fprintf(stderr, "Cannot open %s: %s\n", + iomem, strerror(errno)); + return -1; + } + while(fgets(line, sizeof(line), fp) != 0) { + unsigned long long start, end; + char *str; + int type; + int consumed; + int count; + if (memory_ranges >= MAX_MEMORY_RANGES) + break; + count = sscanf(line, "%llx-%llx : %n", + &start, &end, &consumed); + if (count != 2) + continue; + str = line + consumed; + + dbgprintf("%016Lx-%016Lx : %s", start, end, str); + + if (memcmp(str, "System RAM\n", 11) == 0) { + type = RANGE_RAM; + } + else if (strncasecmp(str, "reserved\n", 9) == 0) { + type = RANGE_RESERVED; + } + else if (memcmp(str, "ACPI Tables\n", 12) == 0) { + type = RANGE_ACPI; + } + else if (memcmp(str, "ACPI Non-volatile Storage\n", 26) == 0) { + type = RANGE_ACPI_NVS; + } + else if (memcmp(str, "Persistent Memory (legacy)\n", 27) == 0) { + type = RANGE_PRAM; + } + else if (memcmp(str, "Persistent Memory\n", 18) == 0) { + type = RANGE_PMEM; + } + else { + continue; + } + memory_range[memory_ranges].start = start; + memory_range[memory_ranges].end = end; + memory_range[memory_ranges].type = type; + + dbgprintf("%016Lx-%016Lx : %x\n", start, end, type); + + memory_ranges++; + } + fclose(fp); + *range = memory_range; + *ranges = memory_ranges; + return 0; +} + +/** + * Calls the architecture independent get_firmware_memmap_ranges() to parse + * /sys/firmware/memmap and then do some x86 only modifications. + * + * @param[out] range pointer that will be set to an array that holds the + * memory ranges + * @param[out] ranges number of ranges valid in @p range + * + * @return 0 on success, any other value on failure. + */ +static int get_memory_ranges_sysfs(struct memory_range **range, int *ranges) +{ + int ret; + size_t range_number = MAX_MEMORY_RANGES; + + ret = get_firmware_memmap_ranges(memory_range, &range_number); + if (ret != 0) { + fprintf(stderr, "Parsing the /sys/firmware memory map failed. " + "Falling back to /proc/iomem.\n"); + return get_memory_ranges_proc_iomem(range, ranges); + } + + *range = memory_range; + *ranges = range_number; + + return 0; +} + +#ifdef HAVE_LIBXENCTRL +unsigned xen_e820_to_kexec_type(uint32_t type) +{ + switch (type) { + case E820_RAM: + return RANGE_RAM; + case E820_ACPI: + return RANGE_ACPI; + case E820_NVS: + return RANGE_ACPI_NVS; + case E820_PMEM: + return RANGE_PMEM; + case E820_PRAM: + return RANGE_PRAM; + case E820_RESERVED: + default: + return RANGE_RESERVED; + } +} + +/** + * Memory map detection for Xen. + * + * @param[out] range pointer that will be set to an array that holds the + * memory ranges + * @param[out] ranges number of ranges valid in @p range + * + * @return 0 on success, any other value on failure. + */ +static int get_memory_ranges_xen(struct memory_range **range, int *ranges) +{ + int rc, ret = -1; + struct e820entry e820entries[MAX_MEMORY_RANGES]; + unsigned int i; + xc_interface *xc; + + xc = xc_interface_open(NULL, NULL, 0); + + if (!xc) { + fprintf(stderr, "%s: Failed to open Xen control interface\n", __func__); + return -1; + } + + rc = xc_get_machine_memory_map(xc, e820entries, MAX_MEMORY_RANGES); + + if (rc < 0) { + fprintf(stderr, "%s: xc_get_machine_memory_map: %s\n", __func__, strerror(rc)); + goto err; + } + + for (i = 0; i < rc; ++i) { + memory_range[i].start = e820entries[i].addr; + memory_range[i].end = e820entries[i].addr + e820entries[i].size - 1; + memory_range[i].type = xen_e820_to_kexec_type(e820entries[i].type); + } + + qsort(memory_range, rc, sizeof(struct memory_range), compare_ranges); + + *range = memory_range; + *ranges = rc; + + ret = 0; + +err: + xc_interface_close(xc); + + return ret; +} +#else +static int get_memory_ranges_xen(struct memory_range **range, int *ranges) +{ + return 0; +} +#endif /* HAVE_LIBXENCTRL */ + +static void remove_range(struct memory_range *range, int nr_ranges, int index) +{ + int i, j; + + for (i = index; i < (nr_ranges-1); i++) { + j = i+1; + range[i] = range[j]; + } +} + +/** + * Verifies and corrects any overlapping ranges. + * The ranges array is assumed to be sorted already. + * + * @param[out] range pointer that will be set to an array that holds the + * memory ranges + * @param[out] ranges number of ranges valid in @p range + * + * @return 0 on success, any other value on failure. + */ +static int fixup_memory_ranges(struct memory_range **range, int *ranges) +{ + int i; + int j; + int change_made; + int nr_ranges = *ranges; + struct memory_range *rp = *range; + +again: + change_made = 0; + for (i = 0; i < (nr_ranges-1); i++) { + j = i+1; + if (rp[i].start > rp[j].start) { + fprintf(stderr, "memory out of order!!\n"); + return 1; + } + + if (rp[i].type != rp[j].type) + continue; + + if (rp[i].start == rp[j].start) { + if (rp[i].end >= rp[j].end) { + remove_range(rp, nr_ranges, j); + nr_ranges--; + change_made++; + } else { + remove_range(rp, nr_ranges, i); + nr_ranges--; + change_made++; + } + } else { + if (rp[i].end > rp[j].start) { + if (rp[i].end < rp[j].end) { + rp[j].start = rp[i].end; + change_made++; + } else if (rp[i].end >= rp[j].end) { + remove_range(rp, nr_ranges, j); + nr_ranges--; + change_made++; + } + } + } + } + + /* fixing/removing an entry may make it wrong relative to the next */ + if (change_made) + goto again; + + *ranges = nr_ranges; + return 0; +} + +/** + * Detect the add_efi_memmap kernel parameter. + * + * On some EFI-based systems, the e820 map is empty, or does not contain a + * complete memory map. The add_efi_memmap parameter adds these entries to + * the kernel's memory map, but does not add them under sysfs, which causes + * kexec to fail in a way similar to how it does not work on Xen. + * + * @return 1 if parameter is present, 0 if not or if an error occurs. + */ +int efi_map_added( void ) { + char buf[512]; + FILE *fp = fopen( "/proc/cmdline", "r" ); + if( fp ) { + fgets( buf, 512, fp ); + fclose( fp ); + return strstr( buf, "add_efi_memmap" ) != NULL; + } else { + return 0; + } +} + +/** + * Return a sorted list of memory ranges. + * + * If we have the /sys/firmware/memmap interface, then use that. If not, + * or if parsing of that fails, use /proc/iomem as fallback. + * + * @param[out] range pointer that will be set to an array that holds the + * memory ranges + * @param[out] ranges number of ranges valid in @p range + * @param[in] kexec_flags the kexec_flags to determine if we load a normal + * or a crashdump kernel + * + * @return 0 on success, any other value on failure. + */ +int get_memory_ranges(struct memory_range **range, int *ranges, + unsigned long kexec_flags) +{ + int ret, i; + + if (!efi_map_added() && !xen_present() && have_sys_firmware_memmap()) { + ret = get_memory_ranges_sysfs(range, ranges); + if (!ret) + ret = fixup_memory_ranges(range, ranges); + } else if (xen_present()) { + ret = get_memory_ranges_xen(range, ranges); + if (!ret) + ret = fixup_memory_ranges(range, ranges); + } else + ret = get_memory_ranges_proc_iomem(range, ranges); + + /* + * get_memory_ranges_sysfs(), get_memory_ranges_proc_iomem() and + * get_memory_ranges_xen() have already printed an error message, + * so fail silently here. + */ + if (ret != 0) + return ret; + + /* Don't report the interrupt table as ram */ + for (i = 0; i < *ranges; i++) { + if ((*range)[i].type == RANGE_RAM && + ((*range)[i].start < REALMODE_IVT_END)) { + (*range)[i].start = REALMODE_IVT_END; + break; + } + } + + /* + * Redefine the memory region boundaries if kernel + * exports the limits and if it is panic kernel. + * Override user values only if kernel exported values are + * subset of user defined values. + */ + if ((kexec_flags & KEXEC_ON_CRASH) && + !(kexec_flags & KEXEC_PRESERVE_CONTEXT)) { + uint64_t start, end; + + ret = get_crash_kernel_load_range(&start, &end); + if (ret != 0) { + fprintf(stderr, "get_crash_kernel_load_range failed.\n"); + return -1; + } + + if (start > mem_min) + mem_min = start; + if (end < mem_max) + mem_max = end; + } + + dbgprint_mem_range("MEMORY RANGES", *range, *ranges); + + return ret; +} + +static uint64_t bootparam_get_acpi_rsdp(void) { + uint64_t acpi_rsdp = 0; + off_t offset = offsetof(struct x86_linux_param_header, acpi_rsdp_addr); + + if (get_bootparam(&acpi_rsdp, offset, sizeof(acpi_rsdp))) + return 0; + + return acpi_rsdp; +} + +static uint64_t efi_get_acpi_rsdp(void) { + FILE *fp; + char line[MAX_LINE], *s; + uint64_t acpi_rsdp = 0; + + fp = fopen("/sys/firmware/efi/systab", "r"); + if (!fp) + return acpi_rsdp; + + while(fgets(line, sizeof(line), fp) != 0) { + /* ACPI20= always goes before ACPI= */ + if ((strstr(line, "ACPI20=")) || (strstr(line, "ACPI="))) { + s = strchr(line, '=') + 1; + sscanf(s, "0x%lx", &acpi_rsdp); + break; + } + } + fclose(fp); + + return acpi_rsdp; +} + +uint64_t get_acpi_rsdp(void) +{ + uint64_t acpi_rsdp = 0; + + acpi_rsdp = bootparam_get_acpi_rsdp(); + + if (!acpi_rsdp) + acpi_rsdp = efi_get_acpi_rsdp(); + + return acpi_rsdp; +} diff --git a/kexec/arch/i386/kexec-x86.c b/kexec/arch/i386/kexec-x86.c new file mode 100644 index 0000000..444cb69 --- /dev/null +++ b/kexec/arch/i386/kexec-x86.c @@ -0,0 +1,210 @@ +/* + * kexec: Linux boots Linux + * + * Copyright (C) 2003-2005 Eric Biederman (ebiederm@xmission.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation (version 2 of the License). + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <stddef.h> +#include <stdio.h> +#include <errno.h> +#include <stdint.h> +#include <string.h> +#include <limits.h> +#include <stdlib.h> +#include <getopt.h> +#include "../../kexec.h" +#include "../../kexec-elf.h" +#include "../../kexec-syscall.h" +#include "../../firmware_memmap.h" +#include "kexec-x86.h" +#include "crashdump-x86.h" +#include <arch/options.h> + +struct file_type file_type[] = { + { "multiboot-x86", multiboot_x86_probe, multiboot_x86_load, + multiboot_x86_usage }, + { "multiboot2-x86", multiboot2_x86_probe, multiboot2_x86_load, + multiboot2_x86_usage }, + { "elf-x86", elf_x86_probe, elf_x86_load, elf_x86_usage }, + { "bzImage", bzImage_probe, bzImage_load, bzImage_usage }, + { "beoboot-x86", beoboot_probe, beoboot_load, beoboot_usage }, + { "nbi-x86", nbi_probe, nbi_load, nbi_usage }, +}; +int file_types = sizeof(file_type)/sizeof(file_type[0]); + + +void arch_usage(void) +{ + printf( + " --reset-vga Attempt to reset a standard vga device\n" + " --serial=<port> Specify the serial port for debug output\n" + " --serial-baud=<baud_rate> Specify the serial port baud rate\n" + " --console-vga Enable the vga console\n" + " --console-serial Enable the serial console\n" + " --elf32-core-headers Prepare core headers in ELF32 format\n" + " --elf64-core-headers Prepare core headers in ELF64 format\n" + " --pass-memmap-cmdline Pass memory map via command line in kexec on panic case\n" + " --noefi Disable efi support\n" + ); +} + +struct arch_options_t arch_options = { + .reset_vga = 0, + .serial_base = 0x3f8, + .serial_baud = 0, + .console_vga = 0, + .console_serial = 0, + .core_header_type = CORE_TYPE_UNDEF, + .pass_memmap_cmdline = 0, + .noefi = 0, +}; + +int arch_process_options(int argc, char **argv) +{ + static const struct option options[] = { + KEXEC_ALL_OPTIONS + { 0, 0, NULL, 0 }, + }; + static const char short_options[] = KEXEC_ALL_OPT_STR; + int opt; + unsigned long value; + char *end; + + opterr = 0; /* Don't complain about unrecognized options here */ + while((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) { + switch(opt) { + default: + break; + case OPT_RESET_VGA: + arch_options.reset_vga = 1; + break; + case OPT_CONSOLE_VGA: + arch_options.console_vga = 1; + break; + case OPT_CONSOLE_SERIAL: + arch_options.console_serial = 1; + break; + case OPT_SERIAL: + value = ULONG_MAX; + if (strcmp(optarg, "ttyS0") == 0) { + value = 0x3f8; + } + else if (strcmp(optarg, "ttyS1") == 0) { + value = 0x2f8; + } + else if (strncmp(optarg, "0x", 2) == 0) { + value = strtoul(optarg +2, &end, 16); + if (*end != '\0') { + value = ULONG_MAX; + } + } + if (value >= 65536) { + fprintf(stderr, "Bad serial port base '%s'\n", + optarg); + usage(); + return -1; + + } + arch_options.serial_base = value; + break; + case OPT_SERIAL_BAUD: + value = strtoul(optarg, &end, 0); + if ((value > 115200) || ((115200 %value) != 0) || + (value < 9600) || (*end)) + { + fprintf(stderr, "Bad serial port baud rate '%s'\n", + optarg); + usage(); + return -1; + + } + arch_options.serial_baud = value; + break; + case OPT_ELF32_CORE: + arch_options.core_header_type = CORE_TYPE_ELF32; + break; + case OPT_ELF64_CORE: + arch_options.core_header_type = CORE_TYPE_ELF64; + break; + case OPT_PASS_MEMMAP_CMDLINE: + arch_options.pass_memmap_cmdline = 1; + break; + case OPT_NOEFI: + arch_options.noefi = 1; + break; + } + } + /* Reset getopt for the next pass; called in other source modules */ + opterr = 1; + optind = 1; + return 0; +} + +const struct arch_map_entry arches[] = { + /* For compatibility with older patches + * use KEXEC_ARCH_DEFAULT instead of KEXEC_ARCH_386 here. + */ + { "i386", KEXEC_ARCH_DEFAULT }, + { "i486", KEXEC_ARCH_DEFAULT }, + { "i586", KEXEC_ARCH_DEFAULT }, + { "i686", KEXEC_ARCH_DEFAULT }, + { "x86_64", KEXEC_ARCH_X86_64 }, + { 0, 0 }, +}; + +int arch_compat_trampoline(struct kexec_info *info) +{ + if ((info->kexec_flags & KEXEC_ARCH_MASK) == KEXEC_ARCH_X86_64) + { + if (!info->rhdr.e_shdr) { + fprintf(stderr, + "A trampoline is required for cross architecture support\n"); + return -1; + } + elf_rel_set_symbol(&info->rhdr, "compat_x86_64_entry32", + &info->entry, sizeof(info->entry)); + + info->entry = (void *)elf_rel_get_addr(&info->rhdr, "compat_x86_64"); + } + return 0; +} + +void arch_update_purgatory(struct kexec_info *info) +{ + uint8_t panic_kernel = 0; + + elf_rel_set_symbol(&info->rhdr, "reset_vga", + &arch_options.reset_vga, sizeof(arch_options.reset_vga)); + elf_rel_set_symbol(&info->rhdr, "serial_base", + &arch_options.serial_base, sizeof(arch_options.serial_base)); + elf_rel_set_symbol(&info->rhdr, "serial_baud", + &arch_options.serial_baud, sizeof(arch_options.serial_baud)); + elf_rel_set_symbol(&info->rhdr, "console_vga", + &arch_options.console_vga, sizeof(arch_options.console_vga)); + elf_rel_set_symbol(&info->rhdr, "console_serial", + &arch_options.console_serial, sizeof(arch_options.console_serial)); + elf_rel_set_symbol(&info->rhdr, "backup_src_start", + &info->backup_src_start, sizeof(info->backup_src_start)); + elf_rel_set_symbol(&info->rhdr, "backup_src_size", + &info->backup_src_size, sizeof(info->backup_src_size)); + if (info->kexec_flags & KEXEC_ON_CRASH) { + panic_kernel = 1; + elf_rel_set_symbol(&info->rhdr, "backup_start", + &info->backup_start, sizeof(info->backup_start)); + } + elf_rel_set_symbol(&info->rhdr, "panic_kernel", + &panic_kernel, sizeof(panic_kernel)); +} diff --git a/kexec/arch/i386/kexec-x86.h b/kexec/arch/i386/kexec-x86.h new file mode 100644 index 0000000..46e2898 --- /dev/null +++ b/kexec/arch/i386/kexec-x86.h @@ -0,0 +1,97 @@ +#ifndef KEXEC_X86_H +#define KEXEC_X86_H + +#define MAX_MEMORY_RANGES 2048 + +enum coretype { + CORE_TYPE_UNDEF = 0, + CORE_TYPE_ELF32 = 1, + CORE_TYPE_ELF64 = 2 +}; + +extern unsigned char compat_x86_64[]; +extern uint32_t compat_x86_64_size, compat_x86_64_entry32; + +struct entry32_regs { + uint32_t eax; + uint32_t ebx; + uint32_t ecx; + uint32_t edx; + uint32_t esi; + uint32_t edi; + uint32_t esp; + uint32_t ebp; + uint32_t eip; +}; + +struct entry16_regs { + uint32_t eax; + uint32_t ebx; + uint32_t ecx; + uint32_t edx; + uint32_t esi; + uint32_t edi; + uint32_t esp; + uint32_t ebp; + uint16_t ds; + uint16_t es; + uint16_t ss; + uint16_t fs; + uint16_t gs; + uint16_t ip; + uint16_t cs; + uint16_t pad; +}; + +struct arch_options_t { + uint8_t reset_vga; + uint16_t serial_base; + uint32_t serial_baud; + uint8_t console_vga; + uint8_t console_serial; + enum coretype core_header_type; + uint8_t pass_memmap_cmdline; + uint8_t noefi; + uint8_t reuse_video_type; +}; + +int multiboot_x86_probe(const char *buf, off_t len); +int multiboot_x86_load(int argc, char **argv, const char *buf, off_t len, + struct kexec_info *info); +void multiboot_x86_usage(void); + +int multiboot2_x86_load(int argc, char **argv, const char *buf, off_t len, + struct kexec_info *info); +void multiboot2_x86_usage(void); +int multiboot2_x86_probe(const char *buf, off_t buf_len); + +int elf_x86_probe(const char *buf, off_t len); +int elf_x86_any_probe(const char *buf, off_t len, enum coretype arch); +int elf_x86_load(int argc, char **argv, const char *buf, off_t len, + struct kexec_info *info); +void elf_x86_usage(void); + +int bzImage_probe(const char *buf, off_t len); +int bzImage_load(int argc, char **argv, const char *buf, off_t len, + struct kexec_info *info); +void bzImage_usage(void); +int do_bzImage_load(struct kexec_info *info, + const char *kernel, off_t kernel_len, + const char *command_line, off_t command_line_len, + const char *initrd, off_t initrd_len, + const char *dtb, off_t dtb_len, + int real_mode_entry); + +int beoboot_probe(const char *buf, off_t len); +int beoboot_load(int argc, char **argv, const char *buf, off_t len, + struct kexec_info *info); +void beoboot_usage(void); + +int nbi_probe(const char *buf, off_t len); +int nbi_load(int argc, char **argv, const char *buf, off_t len, + struct kexec_info *info); +void nbi_usage(void); + +extern unsigned xen_e820_to_kexec_type(uint32_t type); +extern uint64_t get_acpi_rsdp(void); +#endif /* KEXEC_X86_H */ diff --git a/kexec/arch/i386/x86-linux-setup.c b/kexec/arch/i386/x86-linux-setup.c new file mode 100644 index 0000000..9a281dc --- /dev/null +++ b/kexec/arch/i386/x86-linux-setup.c @@ -0,0 +1,971 @@ +/* + * kexec: Linux boots Linux + * + * Copyright (C) 2003,2004 Eric Biederman (ebiederm@xmission.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation (version 2 of the License). + * + * 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. + * + */ +#define _GNU_SOURCE +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <stdarg.h> +#include <stdlib.h> +#include <stddef.h> +#include <errno.h> +#include <limits.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/random.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <linux/fb.h> +#include <linux/screen_info.h> +#include <unistd.h> +#include <dirent.h> +#include <mntent.h> +#include <x86/x86-linux.h> +#include "../../kexec.h" +#include "kexec-x86.h" +#include "x86-linux-setup.h" +#include "../../kexec/kexec-syscall.h" + +#ifndef VIDEO_CAPABILITY_64BIT_BASE +#define VIDEO_CAPABILITY_64BIT_BASE (1 << 1) /* Frame buffer base is 64-bit */ +#endif + +void init_linux_parameters(struct x86_linux_param_header *real_mode) +{ + /* Fill in the values that are usually provided by the kernel. */ + + /* Boot block magic */ + memcpy(real_mode->header_magic, "HdrS", 4); + real_mode->protocol_version = 0x0206; + real_mode->initrd_addr_max = DEFAULT_INITRD_ADDR_MAX; + real_mode->cmdline_size = COMMAND_LINE_SIZE; +} + +void setup_linux_bootloader_parameters_high( + struct kexec_info *info, struct x86_linux_param_header *real_mode, + unsigned long real_mode_base, unsigned long cmdline_offset, + const char *cmdline, off_t cmdline_len, + const char *initrd_buf, off_t initrd_size, int initrd_high) +{ + char *cmdline_ptr; + unsigned long initrd_base, initrd_addr_max; + + /* Say I'm a boot loader */ + real_mode->loader_type = LOADER_TYPE_KEXEC << 4; + + /* No loader flags */ + real_mode->loader_flags = 0; + + /* Find the maximum initial ramdisk address */ + if (initrd_high) + initrd_addr_max = ULONG_MAX; + else { + initrd_addr_max = DEFAULT_INITRD_ADDR_MAX; + if (real_mode->protocol_version >= 0x0203) { + initrd_addr_max = real_mode->initrd_addr_max; + dbgprintf("initrd_addr_max is 0x%lx\n", + initrd_addr_max); + } + } + + /* Load the initrd if we have one */ + if (initrd_buf) { + initrd_base = add_buffer(info, + initrd_buf, initrd_size, initrd_size, + 4096, INITRD_BASE, initrd_addr_max, -1); + dbgprintf("Loaded initrd at 0x%lx size 0x%lx\n", initrd_base, + initrd_size); + } else { + initrd_base = 0; + initrd_size = 0; + } + + /* Ramdisk address and size */ + real_mode->initrd_start = initrd_base & 0xffffffffUL; + real_mode->initrd_size = initrd_size & 0xffffffffUL; + + if (real_mode->protocol_version >= 0x020c && + (initrd_base & 0xffffffffUL) != initrd_base) + real_mode->ext_ramdisk_image = initrd_base >> 32; + + if (real_mode->protocol_version >= 0x020c && + (initrd_size & 0xffffffffUL) != initrd_size) + real_mode->ext_ramdisk_size = initrd_size >> 32; + + /* The location of the command line */ + /* if (real_mode_base == 0x90000) { */ + real_mode->cl_magic = CL_MAGIC_VALUE; + real_mode->cl_offset = cmdline_offset; + /* setup_move_size */ + /* } */ + if (real_mode->protocol_version >= 0x0202) { + unsigned long cmd_line_ptr = real_mode_base + cmdline_offset; + + real_mode->cmd_line_ptr = cmd_line_ptr & 0xffffffffUL; + if ((real_mode->protocol_version >= 0x020c) && + ((cmd_line_ptr & 0xffffffffUL) != cmd_line_ptr)) + real_mode->ext_cmd_line_ptr = cmd_line_ptr >> 32; + } + + /* Fill in the command line */ + if (cmdline_len > COMMAND_LINE_SIZE) { + cmdline_len = COMMAND_LINE_SIZE; + } + cmdline_ptr = ((char *)real_mode) + cmdline_offset; + memcpy(cmdline_ptr, cmdline, cmdline_len); + cmdline_ptr[cmdline_len - 1] = '\0'; +} + +static int setup_linux_vesafb(struct x86_linux_param_header *real_mode) +{ + struct fb_fix_screeninfo fix; + struct fb_var_screeninfo var; + int fd; + + fd = open("/dev/fb0", O_RDONLY); + if (-1 == fd) + return -1; + + if (-1 == ioctl(fd, FBIOGET_FSCREENINFO, &fix)) + goto out; + if (-1 == ioctl(fd, FBIOGET_VSCREENINFO, &var)) + goto out; + if (0 == strcmp(fix.id, "VESA VGA")) { + /* VIDEO_TYPE_VLFB */ + real_mode->orig_video_isVGA = 0x23; + } else if (0 == strcmp(fix.id, "EFI VGA")) { + /* VIDEO_TYPE_EFI */ + real_mode->orig_video_isVGA = 0x70; + } else if (arch_options.reuse_video_type) { + int err; + off_t offset = offsetof(typeof(*real_mode), orig_video_isVGA); + + /* blindly try old boot time video type */ + err = get_bootparam(&real_mode->orig_video_isVGA, offset, 1); + if (err) + goto out; + } else { + real_mode->orig_video_isVGA = 0; + close(fd); + return 0; + } + close(fd); + + real_mode->lfb_width = var.xres; + real_mode->lfb_height = var.yres; + real_mode->lfb_depth = var.bits_per_pixel; + real_mode->lfb_base = fix.smem_start & 0xffffffffUL; + real_mode->lfb_linelength = fix.line_length; + real_mode->vesapm_seg = 0; + + if (fix.smem_start > 0xffffffffUL) { + real_mode->ext_lfb_base = fix.smem_start >> 32; + real_mode->capabilities |= VIDEO_CAPABILITY_64BIT_BASE; + } + + /* FIXME: better get size from the file returned by proc_iomem() */ + real_mode->lfb_size = (fix.smem_len + 65535) / 65536; + real_mode->pages = (fix.smem_len + 4095) / 4096; + + if (var.bits_per_pixel > 8) { + real_mode->red_pos = var.red.offset; + real_mode->red_size = var.red.length; + real_mode->green_pos = var.green.offset; + real_mode->green_size = var.green.length; + real_mode->blue_pos = var.blue.offset; + real_mode->blue_size = var.blue.length; + real_mode->rsvd_pos = var.transp.offset; + real_mode->rsvd_size = var.transp.length; + } + return 0; + + out: + close(fd); + return -1; +} + +#define EDD_SYFS_DIR "/sys/firmware/edd" + +#define EDD_EXT_FIXED_DISK_ACCESS (1 << 0) +#define EDD_EXT_DEVICE_LOCKING_AND_EJECTING (1 << 1) +#define EDD_EXT_ENHANCED_DISK_DRIVE_SUPPORT (1 << 2) +#define EDD_EXT_64BIT_EXTENSIONS (1 << 3) + +/* + * Scans one line from a given filename. Returns on success the number of + * items written (same like scanf()). + */ +static int file_scanf(const char *dir, const char *file, const char *scanf_line, ...) +{ + va_list argptr; + FILE *fp; + int retno; + char filename[PATH_MAX]; + + snprintf(filename, PATH_MAX, "%s/%s", dir, file); + filename[PATH_MAX-1] = 0; + + fp = fopen(filename, "r"); + if (!fp) { + return -errno; + } + + va_start(argptr, scanf_line); + retno = vfscanf(fp, scanf_line, argptr); + va_end(argptr); + + fclose(fp); + + return retno; +} + +static int parse_edd_extensions(const char *dir, struct edd_info *edd_info) +{ + char filename[PATH_MAX]; + char line[1024]; + uint16_t flags = 0; + FILE *fp; + int ret; + + ret = snprintf(filename, PATH_MAX, "%s/%s", dir, "extensions"); + if (ret < 0 || ret >= PATH_MAX) { + fprintf(stderr, "snprintf failed: %s\n", strerror(errno)); + return -1; + } + + filename[PATH_MAX-1] = 0; + + fp = fopen(filename, "r"); + if (!fp) { + return -errno; + } + + while (fgets(line, 1024, fp)) { + /* + * strings are in kernel source, function edd_show_extensions() + * drivers/firmware/edd.c + */ + if (strstr(line, "Fixed disk access") == line) + flags |= EDD_EXT_FIXED_DISK_ACCESS; + else if (strstr(line, "Device locking and ejecting") == line) + flags |= EDD_EXT_DEVICE_LOCKING_AND_EJECTING; + else if (strstr(line, "Enhanced Disk Drive support") == line) + flags |= EDD_EXT_ENHANCED_DISK_DRIVE_SUPPORT; + else if (strstr(line, "64-bit extensions") == line) + flags |= EDD_EXT_64BIT_EXTENSIONS; + } + + fclose(fp); + + edd_info->interface_support = flags; + + return 0; +} + +static int read_edd_raw_data(const char *dir, struct edd_info *edd_info) +{ + char filename[PATH_MAX]; + FILE *fp; + size_t read_chars; + uint16_t len; + int ret; + + ret = snprintf(filename, PATH_MAX, "%s/%s", dir, "raw_data"); + if (ret < 0 || ret >= PATH_MAX) { + fprintf(stderr, "snprintf failed: %s\n", strerror(errno)); + return -1; + } + + filename[PATH_MAX-1] = 0; + + fp = fopen(filename, "r"); + if (!fp) { + return -errno; + } + + memset(edd_info->edd_device_params, 0, EDD_DEVICE_PARAM_SIZE); + read_chars = fread(edd_info->edd_device_params, sizeof(uint8_t), + EDD_DEVICE_PARAM_SIZE, fp); + fclose(fp); + + len = ((uint16_t *)edd_info->edd_device_params)[0]; + dbgprintf("EDD raw data has length %d\n", len); + + if (read_chars < len) { + fprintf(stderr, "BIOS reported EDD length of %hd but only " + "%d chars read.\n", len, (int)read_chars); + return -1; + } + + return 0; +} + +static int add_edd_entry(struct x86_linux_param_header *real_mode, + const char *sysfs_name, int *current_edd, int *current_mbr) +{ + uint8_t devnum, version; + uint32_t mbr_sig; + struct edd_info *edd_info; + + if (!current_mbr || !current_edd) { + fprintf(stderr, "%s: current_edd and current_edd " + "must not be NULL", __FUNCTION__); + return -1; + } + + edd_info = &real_mode->eddbuf[*current_edd]; + memset(edd_info, 0, sizeof(struct edd_info)); + + /* extract the device number */ + if (sscanf(basename(sysfs_name), "int13_dev%hhx", &devnum) != 1) { + fprintf(stderr, "Invalid format of int13_dev dir " + "entry: %s\n", basename(sysfs_name)); + return -1; + } + + /* if there's a MBR signature, then add it */ + if (file_scanf(sysfs_name, "mbr_signature", "0x%x", &mbr_sig) == 1) { + real_mode->edd_mbr_sig_buffer[*current_mbr] = mbr_sig; + (*current_mbr)++; + dbgprintf("EDD Device 0x%x: mbr_sig=0x%x\n", devnum, mbr_sig); + } + + /* set the device number */ + edd_info->device = devnum; + + /* set the version */ + if (file_scanf(sysfs_name, "version", "0x%hhx", &version) != 1) + return -1; + + edd_info->version = version; + + /* if version == 0, that's some kind of dummy entry */ + if (version != 0) { + /* legacy_max_cylinder */ + if (file_scanf(sysfs_name, "legacy_max_cylinder", "%hu", + &edd_info->legacy_max_cylinder) != 1) { + fprintf(stderr, "Reading legacy_max_cylinder failed.\n"); + return -1; + } + + /* legacy_max_head */ + if (file_scanf(sysfs_name, "legacy_max_head", "%hhu", + &edd_info->legacy_max_head) != 1) { + fprintf(stderr, "Reading legacy_max_head failed.\n"); + return -1; + } + + /* legacy_sectors_per_track */ + if (file_scanf(sysfs_name, "legacy_sectors_per_track", "%hhu", + &edd_info->legacy_sectors_per_track) != 1) { + fprintf(stderr, "Reading legacy_sectors_per_track failed.\n"); + return -1; + } + + /* Parse the EDD extensions */ + if (parse_edd_extensions(sysfs_name, edd_info) != 0) { + fprintf(stderr, "Parsing EDD extensions failed.\n"); + return -1; + } + + /* Parse the raw info */ + if (read_edd_raw_data(sysfs_name, edd_info) != 0) { + fprintf(stderr, "Reading EDD raw data failed.\n"); + return -1; + } + } + + (*current_edd)++; + + return 0; +} + +static void zero_edd(struct x86_linux_param_header *real_mode) +{ + real_mode->eddbuf_entries = 0; + real_mode->edd_mbr_sig_buf_entries = 0; + memset(real_mode->eddbuf, 0, + EDDMAXNR * sizeof(struct edd_info)); + memset(real_mode->edd_mbr_sig_buffer, 0, + EDD_MBR_SIG_MAX * sizeof(uint32_t)); +} + +void setup_edd_info(struct x86_linux_param_header *real_mode) +{ + DIR *edd_dir; + struct dirent *cursor; + int current_edd = 0; + int current_mbr = 0; + + edd_dir = opendir(EDD_SYFS_DIR); + if (!edd_dir) { + dbgprintf(EDD_SYFS_DIR " does not exist.\n"); + return; + } + + zero_edd(real_mode); + while ((cursor = readdir(edd_dir))) { + char full_dir_name[PATH_MAX]; + + /* only read the entries that start with "int13_dev" */ + if (strstr(cursor->d_name, "int13_dev") != cursor->d_name) + continue; + + snprintf(full_dir_name, PATH_MAX, "%s/%s", + EDD_SYFS_DIR, cursor->d_name); + full_dir_name[PATH_MAX-1] = 0; + + if (add_edd_entry(real_mode, full_dir_name, ¤t_edd, + ¤t_mbr) != 0) { + zero_edd(real_mode); + goto out; + } + } + + real_mode->eddbuf_entries = current_edd; + real_mode->edd_mbr_sig_buf_entries = current_mbr; + +out: + closedir(edd_dir); + + dbgprintf("Added %d EDD MBR entries and %d EDD entries.\n", + real_mode->edd_mbr_sig_buf_entries, + real_mode->eddbuf_entries); +} + +/* + * This really only makes sense for virtual filesystems that are only expected + * to be mounted once (sysfs, debugsfs, proc), as it will return the first + * instance listed in /proc/mounts, falling back to mtab if absent. + * We search by type and not by name because the name can be anything; + * while setting the name equal to the mount point is common, it cannot be + * relied upon, as even kernel documentation examples recommends using + * "none" as the name e.g. for debugfs. + */ +char *find_mnt_by_type(char *type) +{ + FILE *mtab; + struct mntent *mnt; + char *mntdir; + + mtab = setmntent("/proc/mounts", "r"); + if (!mtab) { + // Fall back to mtab + mtab = setmntent("/etc/mtab", "r"); + } + if (!mtab) + return NULL; + for(mnt = getmntent(mtab); mnt; mnt = getmntent(mtab)) { + if (strcmp(mnt->mnt_type, type) == 0) + break; + } + mntdir = mnt ? strdup(mnt->mnt_dir) : NULL; + endmntent(mtab); + return mntdir; +} + +int get_bootparam(void *buf, off_t offset, size_t size) +{ + int data_file; + char *debugfs_mnt, *sysfs_mnt; + char filename[PATH_MAX]; + int err, has_sysfs_params = 0; + + sysfs_mnt = find_mnt_by_type("sysfs"); + if (sysfs_mnt) { + snprintf(filename, PATH_MAX, "%s/%s", sysfs_mnt, + "kernel/boot_params/data"); + free(sysfs_mnt); + err = access(filename, F_OK); + if (!err) + has_sysfs_params = 1; + } + + if (!has_sysfs_params) { + debugfs_mnt = find_mnt_by_type("debugfs"); + if (!debugfs_mnt) + return 1; + snprintf(filename, PATH_MAX, "%s/%s", debugfs_mnt, + "boot_params/data"); + free(debugfs_mnt); + } + + data_file = open(filename, O_RDONLY); + if (data_file < 0) + return 1; + if (lseek(data_file, offset, SEEK_SET) < 0) + goto close; + read(data_file, buf, size); +close: + close(data_file); + return 0; +} + +void setup_subarch(struct x86_linux_param_header *real_mode) +{ + off_t offset = offsetof(typeof(*real_mode), hardware_subarch); + + get_bootparam(&real_mode->hardware_subarch, offset, sizeof(uint32_t)); +} + +struct efi_mem_descriptor { + uint32_t type; + uint32_t pad; + uint64_t phys_addr; + uint64_t virt_addr; + uint64_t num_pages; + uint64_t attribute; +}; + +struct efi_setup_data { + uint64_t fw_vendor; + uint64_t runtime; + uint64_t tables; + uint64_t smbios; + uint64_t reserved[8]; +}; + +struct setup_data { + uint64_t next; + uint32_t type; +#define SETUP_NONE 0 +#define SETUP_E820_EXT 1 +#define SETUP_DTB 2 +#define SETUP_PCI 3 +#define SETUP_EFI 4 +#define SETUP_RNG_SEED 9 + uint32_t len; + uint8_t data[0]; +} __attribute__((packed)); + +static int get_efi_value(const char *filename, + const char *pattern, uint64_t *val) +{ + FILE *fp; + char line[1024], *s, *end; + + fp = fopen(filename, "r"); + if (!fp) + return 1; + + while (fgets(line, sizeof(line), fp) != 0) { + s = strstr(line, pattern); + if (!s) + continue; + *val = strtoull(s + strlen(pattern), &end, 16); + if (*val == ULLONG_MAX) { + fclose(fp); + return 2; + } + break; + } + + fclose(fp); + return 0; +} + +static int get_efi_values(struct efi_setup_data *esd) +{ + int ret = 0; + + ret = get_efi_value("/sys/firmware/efi/systab", "SMBIOS=0x", + &esd->smbios); + ret |= get_efi_value("/sys/firmware/efi/fw_vendor", "0x", + &esd->fw_vendor); + ret |= get_efi_value("/sys/firmware/efi/runtime", "0x", + &esd->runtime); + ret |= get_efi_value("/sys/firmware/efi/config_table", "0x", + &esd->tables); + return ret; +} + +static int get_efi_runtime_map(struct efi_mem_descriptor **map) +{ + DIR *dirp; + struct dirent *entry; + char filename[1024]; + struct efi_mem_descriptor md, *p = NULL; + int nr_maps = 0; + + dirp = opendir("/sys/firmware/efi/runtime-map"); + if (!dirp) + return 0; + while ((entry = readdir(dirp)) != NULL) { + sprintf(filename, + "/sys/firmware/efi/runtime-map/%s", + (char *)entry->d_name); + if (*entry->d_name == '.') + continue; + file_scanf(filename, "type", "0x%x", (unsigned int *)&md.type); + file_scanf(filename, "phys_addr", "0x%llx", + (unsigned long long *)&md.phys_addr); + file_scanf(filename, "virt_addr", "0x%llx", + (unsigned long long *)&md.virt_addr); + file_scanf(filename, "num_pages", "0x%llx", + (unsigned long long *)&md.num_pages); + file_scanf(filename, "attribute", "0x%llx", + (unsigned long long *)&md.attribute); + p = realloc(p, (nr_maps + 1) * sizeof(md)); + if (!p) + goto err_out; + + *(p + nr_maps) = md; + *map = p; + nr_maps++; + } + + closedir(dirp); + return nr_maps; +err_out: + if (*map) + free(*map); + closedir(dirp); + return 0; +} + +struct efi_info { + uint32_t efi_loader_signature; + uint32_t efi_systab; + uint32_t efi_memdesc_size; + uint32_t efi_memdesc_version; + uint32_t efi_memmap; + uint32_t efi_memmap_size; + uint32_t efi_systab_hi; + uint32_t efi_memmap_hi; +}; + +/* + * Add another instance to single linked list of struct setup_data. + * Please refer to kernel Documentation/x86/boot.txt for more details + * about setup_data structure. + */ +static void add_setup_data(struct kexec_info *info, + struct x86_linux_param_header *real_mode, + struct setup_data *sd) +{ + int sdsize = sizeof(struct setup_data) + sd->len; + + sd->next = real_mode->setup_data; + real_mode->setup_data = add_buffer(info, sd, sdsize, sdsize, getpagesize(), + 0x100000, ULONG_MAX, INT_MAX); +} + +/* + * setup_efi_data will collect below data and pass them to 2nd kernel. + * 1) SMBIOS, fw_vendor, runtime, config_table, they are passed via x86 + * setup_data. + * 2) runtime memory regions, set the memmap related fields in efi_info. + */ +static int setup_efi_data(struct kexec_info *info, + struct x86_linux_param_header *real_mode) +{ + int64_t memmap_paddr; + struct setup_data *sd; + struct efi_setup_data *esd; + struct efi_mem_descriptor *maps; + int nr_maps, size, ret = 0; + struct efi_info *ei = (struct efi_info *)real_mode->efi_info; + + ret = access("/sys/firmware/efi/systab", F_OK); + if (ret < 0) + goto out; + + esd = malloc(sizeof(struct efi_setup_data)); + if (!esd) { + ret = 1; + goto out; + } + memset(esd, 0, sizeof(struct efi_setup_data)); + ret = get_efi_values(esd); + if (ret) + goto free_esd; + nr_maps = get_efi_runtime_map(&maps); + if (!nr_maps) { + ret = 2; + goto free_esd; + } + sd = malloc(sizeof(struct setup_data) + sizeof(*esd)); + if (!sd) { + ret = 3; + goto free_maps; + } + + memset(sd, 0, sizeof(struct setup_data) + sizeof(*esd)); + sd->next = 0; + sd->type = SETUP_EFI; + sd->len = sizeof(*esd); + memcpy(sd->data, esd, sizeof(*esd)); + free(esd); + + add_setup_data(info, real_mode, sd); + + size = nr_maps * sizeof(struct efi_mem_descriptor); + memmap_paddr = add_buffer(info, maps, size, size, getpagesize(), + 0x100000, ULONG_MAX, INT_MAX); + ei->efi_memmap = memmap_paddr & 0xffffffff; + ei->efi_memmap_hi = memmap_paddr >> 32; + ei->efi_memmap_size = size; + ei->efi_memdesc_size = sizeof(struct efi_mem_descriptor); + + return 0; +free_maps: + free(maps); +free_esd: + free(esd); +out: + return ret; +} + +static void add_e820_map_from_mr(struct x86_linux_param_header *real_mode, + struct e820entry *e820, struct memory_range *range, int nr_range) +{ + int i; + + for (i = 0; i < nr_range; i++) { + e820[i].addr = range[i].start; + e820[i].size = range[i].end - range[i].start + 1; + switch (range[i].type) { + case RANGE_RAM: + e820[i].type = E820_RAM; + break; + case RANGE_ACPI: + e820[i].type = E820_ACPI; + break; + case RANGE_ACPI_NVS: + e820[i].type = E820_NVS; + break; + case RANGE_PMEM: + e820[i].type = E820_PMEM; + break; + case RANGE_PRAM: + e820[i].type = E820_PRAM; + break; + default: + case RANGE_RESERVED: + e820[i].type = E820_RESERVED; + break; + } + dbgprintf("%016lx-%016lx (%d)\n", + e820[i].addr, + e820[i].addr + e820[i].size - 1, + e820[i].type); + + if (range[i].type != RANGE_RAM) + continue; + if ((range[i].start <= 0x100000) && range[i].end > 0x100000) { + unsigned long long mem_k = (range[i].end >> 10) - (0x100000 >> 10); + real_mode->ext_mem_k = mem_k; + real_mode->alt_mem_k = mem_k; + if (mem_k > 0xfc00) { + real_mode->ext_mem_k = 0xfc00; /* 64M */ + } + if (mem_k > 0xffffffff) { + real_mode->alt_mem_k = 0xffffffff; + } + } + } +} + +static void setup_e820_ext(struct kexec_info *info, struct x86_linux_param_header *real_mode, + struct memory_range *range, int nr_range) +{ + struct setup_data *sd; + struct e820entry *e820; + int nr_range_ext; + + nr_range_ext = nr_range - E820MAX; + sd = xmalloc(sizeof(struct setup_data) + nr_range_ext * sizeof(struct e820entry)); + sd->next = 0; + sd->len = nr_range_ext * sizeof(struct e820entry); + sd->type = SETUP_E820_EXT; + + e820 = (struct e820entry *) sd->data; + dbgprintf("Extended E820 via setup_data:\n"); + add_e820_map_from_mr(real_mode, e820, range + E820MAX, nr_range_ext); + add_setup_data(info, real_mode, sd); +} + +static void setup_e820(struct kexec_info *info, struct x86_linux_param_header *real_mode) +{ + struct memory_range *range; + int nr_range, nr_range_saved; + + + if (info->kexec_flags & KEXEC_ON_CRASH && !arch_options.pass_memmap_cmdline) { + range = info->crash_range; + nr_range = info->nr_crash_ranges; + } else { + range = info->memory_range; + nr_range = info->memory_ranges; + } + + nr_range_saved = nr_range; + if (nr_range > E820MAX) { + nr_range = E820MAX; + } + + real_mode->e820_map_nr = nr_range; + dbgprintf("E820 memmap:\n"); + add_e820_map_from_mr(real_mode, real_mode->e820_map, range, nr_range); + + if (nr_range_saved > E820MAX) { + dbgprintf("extra E820 memmap are passed via setup_data\n"); + setup_e820_ext(info, real_mode, range, nr_range_saved); + } +} + +static void setup_rng_seed(struct kexec_info *info, + struct x86_linux_param_header *real_mode) +{ + struct { + struct setup_data header; + uint8_t rng_seed[32]; + } *sd; + + sd = xmalloc(sizeof(*sd)); + sd->header.next = 0; + sd->header.len = sizeof(sd->rng_seed); + sd->header.type = SETUP_RNG_SEED; + + if (getrandom(sd->rng_seed, sizeof(sd->rng_seed), GRND_NONBLOCK) != + sizeof(sd->rng_seed)) + return; /* Not initialized, so don't pass a seed. */ + + add_setup_data(info, real_mode, &sd->header); +} + +static int +get_efi_mem_desc_version(struct x86_linux_param_header *real_mode) +{ + struct efi_info *ei = (struct efi_info *)real_mode->efi_info; + + return ei->efi_memdesc_version; +} + +static void setup_efi_info(struct kexec_info *info, + struct x86_linux_param_header *real_mode) +{ + int ret, desc_version; + off_t offset = offsetof(typeof(*real_mode), efi_info); + + ret = get_bootparam(&real_mode->efi_info, offset, 32); + if (ret) + return; + if (((struct efi_info *)real_mode->efi_info)->efi_memmap_size == 0) + /* zero filled efi_info */ + goto out; + desc_version = get_efi_mem_desc_version(real_mode); + if (desc_version != 1) { + fprintf(stderr, + "efi memory descriptor version %d is not supported!\n", + desc_version); + goto out; + } + ret = setup_efi_data(info, real_mode); + if (ret) + goto out; + + return; + +out: + memset(&real_mode->efi_info, 0, 32); + return; +} + +void setup_linux_system_parameters(struct kexec_info *info, + struct x86_linux_param_header *real_mode) +{ + int err; + + /* get subarch from running kernel */ + setup_subarch(real_mode); + if (bzImage_support_efi_boot && !arch_options.noefi) + setup_efi_info(info, real_mode); + + /* Default screen size */ + real_mode->orig_x = 0; + real_mode->orig_y = 0; + real_mode->orig_video_page = 0; + real_mode->orig_video_mode = 0; + real_mode->orig_video_cols = 80; + real_mode->orig_video_lines = 25; + real_mode->orig_video_ega_bx = 0; + real_mode->orig_video_isVGA = 1; + real_mode->orig_video_points = 16; + + /* setup vesa fb if possible, or just use original screen_info */ + err = setup_linux_vesafb(real_mode); + if (err) { + uint16_t cl_magic, cl_offset; + + /* save and restore the old cmdline param if needed */ + cl_magic = real_mode->cl_magic; + cl_offset = real_mode->cl_offset; + + err = get_bootparam(real_mode, 0, sizeof(struct screen_info)); + if (!err) { + real_mode->cl_magic = cl_magic; + real_mode->cl_offset = cl_offset; + } + } + /* Fill in the memsize later */ + real_mode->ext_mem_k = 0; + real_mode->alt_mem_k = 0; + real_mode->e820_map_nr = 0; + + /* Default APM info */ + memset(&real_mode->apm_bios_info, 0, sizeof(real_mode->apm_bios_info)); + /* Default drive info */ + memset(&real_mode->drive_info, 0, sizeof(real_mode->drive_info)); + /* Default sysdesc table */ + real_mode->sys_desc_table.length = 0; + + /* default yes: this can be overridden on the command line */ + real_mode->mount_root_rdonly = 0xFFFF; + + /* default /dev/hda + * this can be overrident on the command line if necessary. + */ + real_mode->root_dev = (0x3 <<8)| 0; + + /* another safe default */ + real_mode->aux_device_info = 0; + + setup_e820(info, real_mode); + + /* pass RNG seed */ + setup_rng_seed(info, real_mode); + + /* fill the EDD information */ + setup_edd_info(real_mode); + + /* Always try to fill acpi_rsdp_addr */ + real_mode->acpi_rsdp_addr = get_acpi_rsdp(); +} + +void setup_linux_dtb(struct kexec_info *info, struct x86_linux_param_header *real_mode, + const char *dtb_buf, int dtb_len) +{ + struct setup_data *sd; + + sd = xmalloc(sizeof(struct setup_data) + dtb_len); + sd->next = 0; + sd->len = dtb_len; + sd->type = SETUP_DTB; + memcpy(sd->data, dtb_buf, dtb_len); + + + add_setup_data(info, real_mode, sd); +} diff --git a/kexec/arch/i386/x86-linux-setup.h b/kexec/arch/i386/x86-linux-setup.h new file mode 100644 index 0000000..b5e1ad5 --- /dev/null +++ b/kexec/arch/i386/x86-linux-setup.h @@ -0,0 +1,38 @@ +#ifndef X86_LINUX_SETUP_H +#define X86_LINUX_SETUP_H +#include <x86/x86-linux.h> + +void init_linux_parameters(struct x86_linux_param_header *real_mode); +void setup_linux_bootloader_parameters_high( + struct kexec_info *info, struct x86_linux_param_header *real_mode, + unsigned long real_mode_base, unsigned long cmdline_offset, + const char *cmdline, off_t cmdline_len, + const char *initrd_buf, off_t initrd_size, int initrd_high); +static inline void setup_linux_bootloader_parameters( + struct kexec_info *info, struct x86_linux_param_header *real_mode, + unsigned long real_mode_base, unsigned long cmdline_offset, + const char *cmdline, off_t cmdline_len, + const char *initrd_buf, off_t initrd_size) +{ + setup_linux_bootloader_parameters_high(info, + real_mode, real_mode_base, + cmdline_offset, cmdline, cmdline_len, + initrd_buf, initrd_size, 0); +} +void setup_linux_system_parameters(struct kexec_info *info, + struct x86_linux_param_header *real_mode); +void setup_linux_dtb(struct kexec_info *info, struct x86_linux_param_header *real_mode, + const char *dtb_buf, int dtb_len); +int get_bootparam(void *buf, off_t offset, size_t size); + + +#define SETUP_BASE 0x90000 +#define KERN32_BASE 0x100000 /* 1MB */ +#define INITRD_BASE 0x1000000 /* 16MB */ + +/* command line parameter may be appended by purgatory */ +#define PURGATORY_CMDLINE_SIZE 64 +extern int bzImage_support_efi_boot; +extern struct arch_options_t arch_options; + +#endif /* X86_LINUX_SETUP_H */ diff --git a/kexec/arch/ia64/Makefile b/kexec/arch/ia64/Makefile new file mode 100644 index 0000000..f5b212b --- /dev/null +++ b/kexec/arch/ia64/Makefile @@ -0,0 +1,16 @@ +# +# kexec ia64 (linux booting linux) +# +ia64_KEXEC_SRCS = kexec/arch/ia64/kexec-iomem.c +ia64_KEXEC_SRCS += kexec/arch/ia64/kexec-ia64.c +ia64_KEXEC_SRCS += kexec/arch/ia64/kexec-elf-ia64.c +ia64_KEXEC_SRCS += kexec/arch/ia64/kexec-elf-rel-ia64.c +ia64_KEXEC_SRCS += kexec/arch/ia64/crashdump-ia64.c + +ia64_PROC_IOMEM = + +dist += kexec/arch/ia64/Makefile $(ia64_KEXEC_SRCS) \ + kexec/arch/ia64/kexec-ia64.h kexec/arch/ia64/crashdump-ia64.h \ + kexec/arch/ia64/include/arch/options.h + + diff --git a/kexec/arch/ia64/crashdump-ia64.c b/kexec/arch/ia64/crashdump-ia64.c new file mode 100644 index 0000000..755ee5e --- /dev/null +++ b/kexec/arch/ia64/crashdump-ia64.c @@ -0,0 +1,293 @@ +/* + * kexec: crashdump support + * Copyright (C) 2005-2006 Zou Nan hai <nanhai.zou@intel.com> Intel Corp + * + * 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 (version 2 of the License). + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <limits.h> +#include <elf.h> +#include "../../kexec.h" +#include "../../kexec-elf.h" +#include "../../kexec-syscall.h" +#include "kexec-ia64.h" +#include "crashdump-ia64.h" +#include "../kexec/crashdump.h" + +int memory_ranges = 0; +#define LOAD_OFFSET (0xa000000000000000UL + 0x100000000UL - \ + kernel_code_start) + +static struct crash_elf_info elf_info = +{ + class: ELFCLASS64, + data: ELFDATA2LSB, + machine: EM_IA_64, + page_offset: PAGE_OFFSET, +}; + +/* Stores a sorted list of RAM memory ranges for which to create elf headers. + * A separate program header is created for backup region. + * The number of entries in memory_range array is always smaller than + * the number of entries in the file returned by proc_iomem(), + * stored in max_memory_ranges. */ +static struct memory_range *crash_memory_range; +/* Memory region reserved for storing panic kernel and other data. */ +static struct memory_range crash_reserved_mem; +unsigned long elfcorehdr; +static unsigned long kernel_code_start; +static unsigned long kernel_code_end; +struct loaded_segment { + unsigned long start; + unsigned long end; +}; + +#define MAX_LOAD_SEGMENTS 128 +struct loaded_segment loaded_segments[MAX_LOAD_SEGMENTS]; + +unsigned long loaded_segments_num, loaded_segments_base; +static int seg_comp(const void *a, const void *b) +{ + const struct loaded_segment *x = a, *y = b; + /* avoid overflow */ + if (x->start > y->start) return 1; + if (x->start < y->start) return -1; + return 0; +} + +/* purgatory code need this info to patch the EFI memmap + */ +static void add_loaded_segments_info(struct mem_ehdr *ehdr) +{ + unsigned i = 0; + while(i < ehdr->e_phnum) { + struct mem_phdr *phdr; + phdr = &ehdr->e_phdr[i]; + if (phdr->p_type != PT_LOAD) { + i++; + continue; + } + + loaded_segments[loaded_segments_num].start = + _ALIGN_DOWN(phdr->p_paddr, ELF_PAGE_SIZE); + loaded_segments[loaded_segments_num].end = + loaded_segments[loaded_segments_num].start; + + /* Consolidate consecutive PL_LOAD segments into one. + * The end addr of the last PL_LOAD segment, calculated by + * adding p_memsz to p_paddr & rounded up to ELF_PAGE_SIZE, + * will be the end address of this loaded_segments entry. + */ + while (i < ehdr->e_phnum) { + phdr = &ehdr->e_phdr[i]; + if (phdr->p_type != PT_LOAD) + break; + loaded_segments[loaded_segments_num].end = + _ALIGN(phdr->p_paddr + phdr->p_memsz, + ELF_PAGE_SIZE); + i++; + } + loaded_segments_num++; + } +} + +/* Removes crash reserve region from list of memory chunks for whom elf program + * headers have to be created. Assuming crash reserve region to be a single + * continuous area fully contained inside one of the memory chunks */ +static int exclude_crash_reserve_region(int *nr_ranges) +{ + int i, j, tidx = -1; + unsigned long cstart, cend; + struct memory_range temp_region; + + /* Crash reserved region. */ + cstart = crash_reserved_mem.start; + cend = crash_reserved_mem.end; + + for (i = 0; i < (*nr_ranges); i++) { + unsigned long mstart, mend; + mstart = crash_memory_range[i].start; + mend = crash_memory_range[i].end; + if (cstart < mend && cend > mstart) { + if (cstart != mstart && cend != mend) { + /* Split memory region */ + crash_memory_range[i].end = cstart - 1; + temp_region.start = cend + 1; + temp_region.end = mend; + temp_region.type = RANGE_RAM; + tidx = i+1; + } else if (cstart != mstart) + crash_memory_range[i].end = cstart - 1; + else + crash_memory_range[i].start = cend + 1; + } + } + /* Insert split memory region, if any. */ + if (tidx >= 0) { + if (*nr_ranges == max_memory_ranges) { + /* No space to insert another element. */ + fprintf(stderr, "Error: Number of crash memory ranges" + " excedeed the max limit\n"); + return -1; + } + for (j = (*nr_ranges - 1); j >= tidx; j--) + crash_memory_range[j+1] = crash_memory_range[j]; + crash_memory_range[tidx].start = temp_region.start; + crash_memory_range[tidx].end = temp_region.end; + crash_memory_range[tidx].type = temp_region.type; + (*nr_ranges)++; + } + return 0; +} + +static int get_crash_memory_ranges(int *ranges) +{ + const char *iomem = proc_iomem(); + char line[MAX_LINE]; + FILE *fp; + unsigned long start, end; + + crash_memory_range = xmalloc(sizeof(struct memory_range) * + max_memory_ranges); + fp = fopen(iomem, "r"); + if (!fp) { + fprintf(stderr, "Cannot open %s: %s\n", + iomem, strerror(errno)); + return -1; + } + while(fgets(line, sizeof(line), fp) != 0) { + char *str; + int type, consumed, count; + if (memory_ranges >= max_memory_ranges) + break; + count = sscanf(line, "%lx-%lx : %n", + &start, &end, &consumed); + str = line + consumed; + if (count != 2) + continue; + + if (memcmp(str, "System RAM\n", 11) == 0) { + type = RANGE_RAM; + } else if (memcmp(str, "Crash kernel\n", 13) == 0) { + /* Reserved memory region. New kernel can + * use this region to boot into. */ + crash_reserved_mem.start = start; + crash_reserved_mem.end = end; + crash_reserved_mem.type = RANGE_RAM; + continue; + } + else if (memcmp(str, "Kernel code\n", 12) == 0) { + kernel_code_start = start; + kernel_code_end = end; + continue; + } else if (memcmp(str, "Uncached RAM\n", 13) == 0) { + type = RANGE_UNCACHED; + } else { + continue; + } + crash_memory_range[memory_ranges].start = start; + crash_memory_range[memory_ranges].end = end; + crash_memory_range[memory_ranges].type = type; + memory_ranges++; + } + fclose(fp); + if (exclude_crash_reserve_region(&memory_ranges) < 0) + return -1; + *ranges = memory_ranges; + return 0; +} + +/* + * Note that this assignes a malloced pointer to *cmdline, + * which is likely never freed by the caller + */ +static void +cmdline_add_elfcorehdr(const char **cmdline, unsigned long addr) +{ + char *str; + char buf[64]; + size_t len; + sprintf(buf, " elfcorehdr=%ldK", addr/1024); + len = strlen(*cmdline) + strlen(buf) + 1; + str = xmalloc(len); + sprintf(str, "%s%s", *cmdline, buf); + *cmdline = str; +} + +int load_crashdump_segments(struct kexec_info *info, struct mem_ehdr *ehdr, + unsigned long max_addr, unsigned long min_base, + const char **cmdline) +{ + int nr_ranges; + unsigned long sz; + size_t size; + void *tmp; + if (info->kexec_flags & KEXEC_ON_CRASH && + get_crash_memory_ranges(&nr_ranges) == 0) { + int i; + + elf_info.kern_paddr_start = kernel_code_start; + for (i=0; i < nr_ranges; i++) { + unsigned long long mstart = crash_memory_range[i].start; + unsigned long long mend = crash_memory_range[i].end; + if (!mstart && !mend) + continue; + if (kernel_code_start >= mstart && + kernel_code_start < mend) { + elf_info.kern_vaddr_start = mstart + LOAD_OFFSET; + break; + } + } + elf_info.kern_size = kernel_code_end - kernel_code_start + 1; + if (crash_create_elf64_headers(info, &elf_info, + crash_memory_range, nr_ranges, + &tmp, &sz, EFI_PAGE_SIZE) < 0) + return -1; + + elfcorehdr = add_buffer(info, tmp, sz, sz, EFI_PAGE_SIZE, + min_base, max_addr, -1); + loaded_segments[loaded_segments_num].start = elfcorehdr; + loaded_segments[loaded_segments_num].end = elfcorehdr + sz; + loaded_segments_num++; + cmdline_add_elfcorehdr(cmdline, elfcorehdr); + } + add_loaded_segments_info(ehdr); + size = sizeof(struct loaded_segment) * loaded_segments_num; + qsort(loaded_segments, loaded_segments_num, + sizeof(struct loaded_segment), seg_comp); + loaded_segments_base = add_buffer(info, loaded_segments, + size, size, 16, 0, max_addr, -1); + + elf_rel_set_symbol(&info->rhdr, "__loaded_segments", + &loaded_segments_base, sizeof(long)); + elf_rel_set_symbol(&info->rhdr, "__loaded_segments_num", + &loaded_segments_num, sizeof(long)); + return 0; +} + +int is_crashkernel_mem_reserved(void) +{ + uint64_t start, end; + + return parse_iomem_single("Crash kernel\n", &start, + &end) == 0 ? (start != end) : 0; +} + +int get_crash_kernel_load_range(uint64_t *start, uint64_t *end) +{ + return parse_iomem_single("Crash kernel\n", start, end); +} diff --git a/kexec/arch/ia64/crashdump-ia64.h b/kexec/arch/ia64/crashdump-ia64.h new file mode 100644 index 0000000..72ba054 --- /dev/null +++ b/kexec/arch/ia64/crashdump-ia64.h @@ -0,0 +1,12 @@ +#ifndef CRASHDUMP_IA64_H +#define CRASHDUMP_IA64_H + +#define PAGE_OFFSET 0xe000000000000000UL +#define __pa(x) ((unsigned long)(x)-PAGE_OFFSET) +extern int load_crashdump_segments(struct kexec_info *info, + struct mem_ehdr *ehdr, unsigned long max_addr, + unsigned long min_base, const char **cmdline); + +#define CRASH_MAX_MEMMAP_NR (KEXEC_MAX_SEGMENTS + 1) + +#endif diff --git a/kexec/arch/ia64/include/arch/options.h b/kexec/arch/ia64/include/arch/options.h new file mode 100644 index 0000000..e8754ad --- /dev/null +++ b/kexec/arch/ia64/include/arch/options.h @@ -0,0 +1,42 @@ +#ifndef KEXEC_ARCH_IA64_OPTIONS_H +#define KEXEC_ARCH_IA64_OPTIONS_H + +#define OPT_ARCH_MAX (OPT_MAX+0) +#define OPT_APPEND (OPT_ARCH_MAX+0) +#define OPT_RAMDISK (OPT_ARCH_MAX+1) +#define OPT_NOIO (OPT_ARCH_MAX+2) +#define OPT_VMM (OPT_ARCH_MAX+3) + +/* Options relevant to the architecture (excluding loader-specific ones), + * in this case none: + */ +#define KEXEC_ARCH_OPTIONS \ + KEXEC_OPTIONS \ + +#define KEXEC_ARCH_OPT_STR KEXEC_OPT_STR "" + +/* The following two #defines list ALL of the options added by all of the + * architecture's loaders. + * o main() uses this complete list to scan for its options, ignoring + * arch-specific/loader-specific ones. + * o Then, arch_process_options() uses this complete list to scan for its + * options, ignoring general/loader-specific ones. + * o Then, the file_type[n].load re-scans for options, using + * KEXEC_ARCH_OPTIONS plus its loader-specific options subset. + * Any unrecognised options cause an error here. + * + * This is done so that main()'s/arch_process_options()'s getopt_long() calls + * don't choose a kernel filename from random arguments to options they don't + * recognise -- as they now recognise (if not act upon) all possible options. + */ +#define KEXEC_ALL_OPTIONS \ + KEXEC_ARCH_OPTIONS \ + {"command-line", 1, 0, OPT_APPEND}, \ + {"append", 1, 0, OPT_APPEND}, \ + {"initrd", 1, 0, OPT_RAMDISK}, \ + {"noio", 0, 0, OPT_NOIO}, \ + {"vmm", 1, 0, OPT_VMM}, \ + +#define KEXEC_ALL_OPT_STR KEXEC_OPT_STR + +#endif /* KEXEC_ARCH_IA64_OPTIONS_H */ diff --git a/kexec/arch/ia64/kexec-elf-ia64.c b/kexec/arch/ia64/kexec-elf-ia64.c new file mode 100644 index 0000000..142dee3 --- /dev/null +++ b/kexec/arch/ia64/kexec-elf-ia64.c @@ -0,0 +1,303 @@ +/* + * kexec: Linux boots Linux + * + * Copyright (C) 2003,2004 Eric Biederman (ebiederm@xmission.com) + * Copyright (C) 2004 Albert Herranz + * Copyright (C) 2004 Silicon Graphics, Inc. + * Jesse Barnes <jbarnes@sgi.com> + * Copyright (C) 2004 Khalid Aziz <khalid.aziz@hp.com> Hewlett Packard Co + * Copyright (C) 2005 Zou Nan hai <nanhai.zou@intel.com> Intel Corp + * + * 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 (version 2 of the License). + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#define _GNU_SOURCE +#include <stddef.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <stdint.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <getopt.h> +#include <limits.h> +#include <elf.h> +#include <boot/elf_boot.h> +#include <ip_checksum.h> +#include "../../kexec.h" +#include "../../kexec-syscall.h" +#include "../../kexec-elf.h" +#include "kexec-ia64.h" +#include "crashdump-ia64.h" +#include <arch/options.h> + +static const int probe_debug = 0; +extern unsigned long saved_efi_memmap_size; + +/* + * elf_ia64_probe - sanity check the elf image + * + * Make sure that the file image has a reasonable chance of working. + */ +int elf_ia64_probe(const char *buf, off_t len) +{ + struct mem_ehdr ehdr; + int result; + result = build_elf_exec_info(buf, len, &ehdr, 0); + if (result < 0) { + if (probe_debug) { + fprintf(stderr, "Not an ELF executable\n"); + } + return -1; + } + /* Verify the architecuture specific bits */ + if (ehdr.e_machine != EM_IA_64) { + /* for a different architecture */ + if (probe_debug) { + fprintf(stderr, "Not for this architecture.\n"); + } + return -1; + } + return 0; +} + +void elf_ia64_usage(void) +{ + printf(" --command-line=STRING Set the kernel command line to " + "STRING.\n" + " --append=STRING Set the kernel command line to " + "STRING.\n" + " --initrd=FILE Use FILE as the kernel's initial " + "ramdisk.\n" + " --noio Disable I/O in purgatory code.\n" + " --vmm=FILE Use FILE as the kernel image for a\n" + " virtual machine monitor " + "(aka hypervisor)\n"); +} + +/* Move the crash kerenl physical offset to reserved region + */ +void move_loaded_segments(struct mem_ehdr *ehdr, unsigned long addr) +{ + unsigned i; + long offset = 0; + int found = 0; + struct mem_phdr *phdr; + for(i = 0; i < ehdr->e_phnum; i++) { + phdr = &ehdr->e_phdr[i]; + if (phdr->p_type == PT_LOAD) { + offset = addr - phdr->p_paddr; + found++; + break; + } + } + if (!found) + die("move_loaded_segments: no PT_LOAD region 0x%016x\n", addr); + ehdr->e_entry += offset; + for(i = 0; i < ehdr->e_phnum; i++) { + phdr = &ehdr->e_phdr[i]; + if (phdr->p_type == PT_LOAD) + phdr->p_paddr += offset; + } +} + +int elf_ia64_load(int argc, char **argv, const char *buf, off_t len, + struct kexec_info *info) +{ + struct mem_ehdr ehdr; + const char *command_line, *ramdisk=0, *vmm=0, *kernel_buf; + char *ramdisk_buf = NULL; + off_t ramdisk_size = 0, kernel_size; + unsigned long command_line_len; + unsigned long entry, max_addr, gp_value; + unsigned long command_line_base, ramdisk_base, image_base; + unsigned long efi_memmap_base, efi_memmap_size; + unsigned long boot_param_base; + unsigned long noio=0; + int result; + int opt; + char *efi_memmap_buf, *boot_param; + + /* See options.h -- add any more there, too. */ + static const struct option options[] = { + KEXEC_ARCH_OPTIONS + {"command-line", 1, 0, OPT_APPEND}, + {"append", 1, 0, OPT_APPEND}, + {"initrd", 1, 0, OPT_RAMDISK}, + {"noio", 0, 0, OPT_NOIO}, + {"vmm", 1, 0, OPT_VMM}, + {0, 0, 0, 0}, + }; + + static const char short_options[] = KEXEC_ARCH_OPT_STR ""; + + command_line = 0; + while ((opt = getopt_long(argc, argv, short_options, + options, 0)) != -1) { + switch (opt) { + default: + /* Ignore core options */ + if (opt < OPT_ARCH_MAX) { + break; + } + case OPT_APPEND: + command_line = optarg; + break; + case OPT_RAMDISK: + ramdisk = optarg; + break; + case OPT_NOIO: /* disable PIO and MMIO in purgatory code*/ + noio = 1; + break; + case OPT_VMM: + vmm = optarg; + break; + } + } + command_line_len = 0; + if (command_line) { + command_line_len = strlen(command_line) + 16; + } + + if (vmm) + kernel_buf = slurp_decompress_file(vmm, &kernel_size); + else { + kernel_buf = buf; + kernel_size = len; + } + + /* Parse the Elf file */ + result = build_elf_exec_info(kernel_buf, kernel_size, &ehdr, 0); + if (result < 0) { + fprintf(stderr, "ELF parse failed\n"); + free_elf_info(&ehdr); + return result; + } + + if (info->kexec_flags & KEXEC_ON_CRASH ) { + if ((mem_min == 0x00) && (mem_max == ULONG_MAX)) { + fprintf(stderr, "Failed to find crash kernel region " + "in %s\n", proc_iomem()); + free_elf_info(&ehdr); + return -1; + } + move_loaded_segments(&ehdr, mem_min); + } else if (update_loaded_segments(&ehdr) < 0) { + fprintf(stderr, "Failed to place kernel\n"); + return -1; + } + + entry = ehdr.e_entry; + max_addr = elf_max_addr(&ehdr); + + /* Load the Elf data */ + result = elf_exec_load(&ehdr, info); + if (result < 0) { + fprintf(stderr, "ELF load failed\n"); + free_elf_info(&ehdr); + return result; + } + + + /* Load the setup code */ + elf_rel_build_load(info, &info->rhdr, purgatory, purgatory_size, + 0x0, ULONG_MAX, -1, 0); + + + if (load_crashdump_segments(info, &ehdr, max_addr, 0, + &command_line) < 0) + return -1; + + // reverve 4k for ia64_boot_param + boot_param = xmalloc(4096); + boot_param_base = add_buffer(info, boot_param, 4096, 4096, 4096, 0, + max_addr, -1); + + elf_rel_set_symbol(&info->rhdr, "__noio", + &noio, sizeof(long)); + + elf_rel_set_symbol(&info->rhdr, "__boot_param_base", + &boot_param_base, sizeof(long)); + + // reserve efi_memmap of actual size allocated in production kernel + efi_memmap_size = saved_efi_memmap_size; + efi_memmap_buf = xmalloc(efi_memmap_size); + efi_memmap_base = add_buffer(info, efi_memmap_buf, + efi_memmap_size, efi_memmap_size, 4096, 0, + max_addr, -1); + + elf_rel_set_symbol(&info->rhdr, "__efi_memmap_base", + &efi_memmap_base, sizeof(long)); + + elf_rel_set_symbol(&info->rhdr, "__efi_memmap_size", + &efi_memmap_size, sizeof(long)); + if (command_line) { + command_line_len = strlen(command_line) + 1; + } + if (command_line_len || (info->kexec_flags & KEXEC_ON_CRASH )) { + char *cmdline = xmalloc(command_line_len); + strcpy(cmdline, command_line); + + if (info->kexec_flags & KEXEC_ON_CRASH) { + char buf[128]; + sprintf(buf," max_addr=%lluM min_addr=%lluM", + mem_max>>20, mem_min>>20); + command_line_len = strlen(cmdline) + strlen(buf) + 1; + cmdline = xrealloc(cmdline, command_line_len); + strcat(cmdline, buf); + } + + command_line_len = _ALIGN(command_line_len, 16); + command_line_base = add_buffer(info, cmdline, + command_line_len, command_line_len, + getpagesize(), 0UL, + max_addr, -1); + elf_rel_set_symbol(&info->rhdr, "__command_line_len", + &command_line_len, sizeof(long)); + elf_rel_set_symbol(&info->rhdr, "__command_line", + &command_line_base, sizeof(long)); + } + + if (ramdisk) { + ramdisk_buf = slurp_file(ramdisk, &ramdisk_size); + ramdisk_base = add_buffer(info, ramdisk_buf, ramdisk_size, + ramdisk_size, + getpagesize(), 0, max_addr, -1); + elf_rel_set_symbol(&info->rhdr, "__ramdisk_base", + &ramdisk_base, sizeof(long)); + elf_rel_set_symbol(&info->rhdr, "__ramdisk_size", + &ramdisk_size, sizeof(long)); + } + + if (vmm) { + image_base = add_buffer(info, buf, len, len, + getpagesize(), 0, max_addr, -1); + elf_rel_set_symbol(&info->rhdr, "__vmcode_base", + &image_base, sizeof(long)); + elf_rel_set_symbol(&info->rhdr, "__vmcode_size", + &len, sizeof(long)); + } + + gp_value = info->rhdr.rel_addr + 0x200000; + elf_rel_set_symbol(&info->rhdr, "__gp_value", &gp_value, + sizeof(gp_value)); + + elf_rel_set_symbol(&info->rhdr, "__kernel_entry", &entry, + sizeof(entry)); + free_elf_info(&ehdr); + return 0; +} diff --git a/kexec/arch/ia64/kexec-elf-rel-ia64.c b/kexec/arch/ia64/kexec-elf-rel-ia64.c new file mode 100644 index 0000000..500f247 --- /dev/null +++ b/kexec/arch/ia64/kexec-elf-rel-ia64.c @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2005-2006 Zou Nan hai (nanhai.zou@intel.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation (version 2 of the License). + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* pugatory relocation code + * Most of the code in this file is + * based on arch/ia64/kernel/module.c in Linux kernel + */ + + +/* Most of the code in this file is + * based on arch/ia64/kernel/module.c in Linux kernel + */ + +#include <stdio.h> +#include <elf.h> +#include "../../kexec.h" +#include "../../kexec-elf.h" + +#define MAX_LTOFF ((uint64_t) (1 << 22)) + +int machine_verify_elf_rel(struct mem_ehdr *ehdr) +{ + if (ehdr->ei_data != ELFDATA2LSB) { + return 0; + } + if (ehdr->ei_class != ELFCLASS64) { + return 0; + } + if (ehdr->e_machine != EM_IA_64) { + return 0; + } + return 1; +} + +static void +ia64_patch (uint64_t insn_addr, uint64_t mask, uint64_t val) +{ + uint64_t m0, m1, v0, v1, b0, b1, *b = (uint64_t *) (insn_addr & -16); +# define insn_mask ((1UL << 41) - 1) + unsigned long shift; + + b0 = b[0]; b1 = b[1]; + shift = 5 + 41 * (insn_addr % 16); /* 5 bits of template, then 3 x 41-bit instructions */ + if (shift >= 64) { + m1 = mask << (shift - 64); + v1 = val << (shift - 64); + } else { + m0 = mask << shift; m1 = mask >> (64 - shift); + v0 = val << shift; v1 = val >> (64 - shift); + b[0] = (b0 & ~m0) | (v0 & m0); + } + b[1] = (b1 & ~m1) | (v1 & m1); +} + +static inline uint64_t +bundle (const uint64_t insn) +{ + return insn & ~0xfUL; +} + +void machine_apply_elf_rel(struct mem_ehdr *ehdr, + struct mem_sym *UNUSED(sym), unsigned long r_type, void *location, + unsigned long address, unsigned long value) +{ + uint64_t gp_value = ehdr->rel_addr + 0x200000; + switch(r_type) { + case R_IA64_NONE: + break; + case R_IA64_SEGREL64LSB: + case R_IA64_DIR64LSB: + *((uint64_t *)location) = value; + break; + case R_IA64_DIR32LSB: + *((uint32_t *)location) = value; + if (value != *((uint32_t *)location)) + goto overflow; + break; + case R_IA64_IMM64: + ia64_patch((uint64_t)location, 0x01fffefe000UL, + /* bit 63 -> 36 */ + (((value & 0x8000000000000000UL) >> 27) + /* bit 21 -> 21 */ + | ((value & 0x0000000000200000UL) << 0) + /* bit 16 -> 22 */ + | ((value & 0x00000000001f0000UL) << 6) + /* bit 7 -> 27 */ + | ((value & 0x000000000000ff80UL) << 20) + /* bit 0 -> 13 */ + | ((value & 0x000000000000007fUL) << 13))); + ia64_patch((uint64_t)location - 1, 0x1ffffffffffUL, value>>22); + break; + case R_IA64_IMM22: + if (value + (1 << 21) >= (1 << 22)) + die("value out of IMM22 range\n"); + ia64_patch((uint64_t)location, 0x01fffcfe000UL, + /* bit 21 -> 36 */ + (((value & 0x200000UL) << 15) + /* bit 16 -> 22 */ + | ((value & 0x1f0000UL) << 6) + /* bit 7 -> 27 */ + | ((value & 0x00ff80UL) << 20) + /* bit 0 -> 13 */ + | ((value & 0x00007fUL) << 13) )); + break; + case R_IA64_PCREL21B: { + uint64_t delta = ((int64_t)value - (int64_t)address)/16; + if (delta + (1 << 20) >= (1 << 21)) + die("value out of IMM21B range\n"); + value = ((int64_t)(value - bundle(address)))/16; + ia64_patch((uint64_t)location, 0x11ffffe000UL, + (((value & 0x100000UL) << 16) /* bit 20 -> 36 */ + | ((value & 0x0fffffUL) << 13) /* bit 0 -> 13 */)); + } + break; + case R_IA64_PCREL64LSB: { + value = value - address; + put_unaligned(value, (uint64_t *)location); + } break; + case R_IA64_GPREL22: + case R_IA64_LTOFF22X: + if (value - gp_value + MAX_LTOFF/2 >= MAX_LTOFF) + die("value out of gp relative range"); + value -= gp_value; + ia64_patch((uint64_t)location, 0x01fffcfe000UL, + (((value & 0x200000UL) << 15) /* bit 21 -> 36 */ + |((value & 0x1f0000UL) << 6) /* bit 16 -> 22 */ + |((value & 0x00ff80UL) << 20) /* bit 7 -> 27 */ + |((value & 0x00007fUL) << 13) /* bit 0 -> 13 */)); + break; + case R_IA64_LDXMOV: + if (value - gp_value + MAX_LTOFF/2 >= MAX_LTOFF) + die("value out of gp relative range"); + ia64_patch((uint64_t)location, 0x1fff80fe000UL, 0x10000000000UL); + break; + case R_IA64_LTOFF22: + + default: + die("Unknown rela relocation: 0x%lx 0x%lx\n", + r_type, address); + break; + } + return; +overflow: + die("overflow in relocation type %lu val %llx\n", + r_type, value); +} diff --git a/kexec/arch/ia64/kexec-ia64.c b/kexec/arch/ia64/kexec-ia64.c new file mode 100644 index 0000000..418d997 --- /dev/null +++ b/kexec/arch/ia64/kexec-ia64.c @@ -0,0 +1,247 @@ +/* + * kexec: Linux boots Linux + * + * Copyright (C) 2003-2005 Eric Biederman (ebiederm@xmission.com) + * Copyright (C) 2004 Albert Herranz + * Copyright (C) 2004 Silicon Graphics, Inc. + * Jesse Barnes <jbarnes@sgi.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation (version 2 of the License). + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#define _GNU_SOURCE +#include <stddef.h> +#include <stdio.h> +#include <errno.h> +#include <stdint.h> +#include <string.h> +#include <getopt.h> +#include <sched.h> +#include <limits.h> +#include "../../kexec.h" +#include "../../kexec-syscall.h" +#include "elf.h" +#include "kexec-ia64.h" +#include <arch/options.h> + +/* The number of entries in memory_range array is always smaller than + * the number of entries in the file returned by proc_iomem(), + * stored in max_memory_ranges. */ +static struct memory_range *memory_range; +int max_memory_ranges; +static int memory_ranges; +unsigned long saved_efi_memmap_size; + +/* Reserve range for EFI memmap and Boot parameter */ +static int split_range(int range, unsigned long start, unsigned long end) +{ + unsigned long ram_end = memory_range[range - 1].end; + unsigned int type = memory_range[range - 1].type; + int i; + //align end and start to page size of EFI + start = _ALIGN_DOWN(start, 1UL<<12); + end = _ALIGN(end, 1UL<<12); + for (i = 0; i < range; i++) + if(memory_range[i].start <= start && memory_range[i].end >=end) + break; + if (i >= range) + return range; + range = i; + if (memory_range[range].start < start) { + memory_range[range].end = start; + range++; + } + memory_range[range].start = start; + memory_range[range].end = end; + memory_range[range].type = RANGE_RESERVED; + range++; + if (end < ram_end) { + memory_range[range].start = end; + memory_range[range].end = ram_end; + memory_range[range].type = type; + range++; + } + return range; +} + +/* Return a sorted list of available memory ranges. */ +int get_memory_ranges(struct memory_range **range, int *ranges, + unsigned long kexec_flags) +{ + const char *iomem = proc_iomem(); + char line[MAX_LINE]; + FILE *fp; + fp = fopen(iomem, "r"); + if (!fp) { + fprintf(stderr, "Cannot open %s: %s\n", + iomem, strerror(errno)); + return -1; + } + + /* allocate memory_range dynamically */ + max_memory_ranges = 0; + while(fgets(line, sizeof(line), fp) != 0) { + max_memory_ranges++; + } + memory_range = xmalloc(sizeof(struct memory_range) * + max_memory_ranges); + rewind(fp); + + while(fgets(line, sizeof(line), fp) != 0) { + unsigned long start, end; + char *str; + unsigned type; + int consumed; + int count; + if (memory_ranges >= max_memory_ranges) + break; + count = sscanf(line, "%lx-%lx : %n", + &start, &end, &consumed); + if (count != 2) + continue; + str = line + consumed; + end = end + 1; + if (memcmp(str, "System RAM\n", 11) == 0) { + type = RANGE_RAM; + } + else if (memcmp(str, "reserved\n", 9) == 0) { + type = RANGE_RESERVED; + } + else if (memcmp(str, "Crash kernel\n", 13) == 0) { + /* Redefine the memory region boundaries if kernel + * exports the limits and if it is panic kernel. + * Override user values only if kernel exported + * values are subset of user defined values. + */ + + if (kexec_flags & KEXEC_ON_CRASH) { + if (start > mem_min) + mem_min = start; + if (end < mem_max) + mem_max = end; + } + continue; + } else if (memcmp(str, "Boot parameter\n", 14) == 0) { + memory_ranges = split_range(memory_ranges, start, end); + continue; + } else if (memcmp(str, "EFI Memory Map\n", 14) == 0) { + memory_ranges = split_range(memory_ranges, start, end); + saved_efi_memmap_size = end - start; + continue; + } else if (memcmp(str, "Uncached RAM\n", 13) == 0) { + type = RANGE_UNCACHED; + } else { + continue; + } + /* + * Check if this memory range can be coalesced with + * the previous range + */ + if ((memory_ranges > 0) && + (start == memory_range[memory_ranges-1].end) && + (type == memory_range[memory_ranges-1].type)) { + memory_range[memory_ranges-1].end = end; + } + else { + memory_range[memory_ranges].start = start; + memory_range[memory_ranges].end = end; + memory_range[memory_ranges].type = type; + memory_ranges++; + } + } + fclose(fp); + *range = memory_range; + *ranges = memory_ranges; + + return 0; +} + +/* Supported file types and callbacks */ +struct file_type file_type[] = { + {"elf-ia64", elf_ia64_probe, elf_ia64_load, elf_ia64_usage}, +}; +int file_types = sizeof(file_type) / sizeof(file_type[0]); + + +void arch_usage(void) +{ +} + +int arch_process_options(int argc, char **argv) +{ + /* This doesn't belong here! Some sort of arch_init() ? */ + + /* execute from monarch processor */ + cpu_set_t affinity; + CPU_ZERO(&affinity); + CPU_SET(0, &affinity); + sched_setaffinity(0, sizeof(affinity), &affinity); + + return 0; +} + +const struct arch_map_entry arches[] = { + { "ia64", KEXEC_ARCH_IA_64 }, + { NULL, 0 }, +}; + +int arch_compat_trampoline(struct kexec_info *UNUSED(info)) +{ + return 0; +} + +int update_loaded_segments(struct mem_ehdr *ehdr) +{ + int i; + unsigned u; + struct mem_phdr *phdr; + unsigned long start_addr = ULONG_MAX, end_addr = 0; + unsigned long align = 1UL<<26; /* 64M */ + unsigned long start, end; + + for (u = 0; u < ehdr->e_phnum; u++) { + phdr = &ehdr->e_phdr[u]; + if (phdr->p_type != PT_LOAD) + continue; + if (phdr->p_paddr < start_addr) + start_addr = phdr->p_paddr; + if ((phdr->p_paddr + phdr->p_memsz) > end_addr) + end_addr = phdr->p_paddr + phdr->p_memsz; + } + + for (i = 0; i < memory_ranges && memory_range[i].start <= start_addr; + i++) { + if (memory_range[i].type == RANGE_RAM && + memory_range[i].end > end_addr) + return 0; + } + + for (i = 0; i < memory_ranges; i++) { + if (memory_range[i].type != RANGE_RAM) + continue; + start = _ALIGN(memory_range[i].start, align); + end = memory_range[i].end; + if (end > start && (end - start) > (end_addr - start_addr)) { + move_loaded_segments(ehdr, start); + return 0; + } + } + + return -1; +} + +void arch_update_purgatory(struct kexec_info *UNUSED(info)) +{ +} + diff --git a/kexec/arch/ia64/kexec-ia64.h b/kexec/arch/ia64/kexec-ia64.h new file mode 100644 index 0000000..31e4041 --- /dev/null +++ b/kexec/arch/ia64/kexec-ia64.h @@ -0,0 +1,14 @@ +#ifndef KEXEC_IA64_H +#define KEXEC_IA64_H + +extern int max_memory_ranges; +int elf_ia64_probe(const char *buf, off_t len); +int elf_ia64_load(int argc, char **argv, const char *buf, off_t len, + struct kexec_info *info); +void elf_ia64_usage(void); +int update_loaded_segments(struct mem_ehdr *ehdr); +void move_loaded_segments(struct mem_ehdr *ehdr, unsigned long addr); + +#define EFI_PAGE_SIZE (1UL<<12) +#define ELF_PAGE_SIZE (1UL<<16) +#endif /* KEXEC_IA64_H */ diff --git a/kexec/arch/ia64/kexec-iomem.c b/kexec/arch/ia64/kexec-iomem.c new file mode 100644 index 0000000..7fd50cd --- /dev/null +++ b/kexec/arch/ia64/kexec-iomem.c @@ -0,0 +1,23 @@ +#include <stdint.h> +#include <stdio.h> +#include "../../kexec.h" +#include "../../crashdump.h" + +static const char proc_iomem_str[]= "/proc/iomem"; +static const char proc_iomem_machine_str[]= "/proc/iomem_machine"; + +/* + * On IA64 XEN the EFI tables are virtualised. + * For this reason on such systems /proc/iomem_machine is provided, + * which is based on the hypervisor's (machine's) EFI tables. + * If Xen is in use, then /proc/iomem is used for memory regions relating + * to the currently running dom0 kernel, and /proc/iomem_machine is used + * for regions relating to the machine itself or the hypervisor. + * If Xen is not in used, then /proc/iomem used. + */ +const char *proc_iomem(void) +{ + if (xen_present()) + return proc_iomem_machine_str; + return proc_iomem_str; +} diff --git a/kexec/arch/loongarch/Makefile b/kexec/arch/loongarch/Makefile new file mode 100644 index 0000000..3b33b96 --- /dev/null +++ b/kexec/arch/loongarch/Makefile @@ -0,0 +1,22 @@ +# +# kexec loongarch (linux booting linux) +# +loongarch_KEXEC_SRCS = kexec/arch/loongarch/kexec-loongarch.c +loongarch_KEXEC_SRCS += kexec/arch/loongarch/kexec-elf-loongarch.c +loongarch_KEXEC_SRCS += kexec/arch/loongarch/kexec-pei-loongarch.c +loongarch_KEXEC_SRCS += kexec/arch/loongarch/kexec-elf-rel-loongarch.c +loongarch_KEXEC_SRCS += kexec/arch/loongarch/crashdump-loongarch.c + +loongarch_MEM_REGIONS = kexec/mem_regions.c + +loongarch_CPPFLAGS += -I $(srcdir)/kexec/ + +loongarch_ADD_BUFFER = +loongarch_ADD_SEGMENT = +loongarch_VIRT_TO_PHYS = + +dist += kexec/arch/loongarch/Makefile $(loongarch_KEXEC_SRCS) \ + kexec/arch/loongarch/kexec-loongarch.h \ + kexec/arch/loongarch/image-header.h \ + kexec/arch/loongarch/crashdump-loongarch.h \ + kexec/arch/loongarch/include/arch/options.h diff --git a/kexec/arch/loongarch/crashdump-loongarch.c b/kexec/arch/loongarch/crashdump-loongarch.c new file mode 100644 index 0000000..81250e4 --- /dev/null +++ b/kexec/arch/loongarch/crashdump-loongarch.c @@ -0,0 +1,220 @@ +/* + * LoongArch crashdump. + * + * Copyright (C) 2022 Loongson Technology Corporation Limited. + * Youling Tang <tangyouling@loongson.cn> + * + * derived from crashdump-arm64.c + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#define _GNU_SOURCE + +#include <errno.h> +#include <linux/elf.h> + +#include "kexec.h" +#include "crashdump.h" +#include "crashdump-loongarch.h" +#include "iomem.h" +#include "kexec-loongarch.h" +#include "kexec-elf.h" +#include "mem_regions.h" + +/* memory ranges of crashed kernel */ +static struct memory_ranges system_memory_rgns; + +/* memory range reserved for crashkernel */ +struct memory_range crash_reserved_mem[CRASH_MAX_RESERVED_RANGES]; +struct memory_ranges usablemem_rgns = { + .size = 0, + .max_size = CRASH_MAX_RESERVED_RANGES, + .ranges = crash_reserved_mem, +}; + +struct memory_range elfcorehdr_mem; + +static struct crash_elf_info elf_info64 = { + .class = ELFCLASS64, + .data = ELFDATA2LSB, + .machine = EM_LOONGARCH, + .page_offset = PAGE_OFFSET, +}; + +/* + * iomem_range_callback() - callback called for each iomem region + * @data: not used + * @nr: not used + * @str: name of the memory region + * @base: start address of the memory region + * @length: size of the memory region + * + * This function is called once for each memory region found in /proc/iomem. + * It locates system RAM and crashkernel reserved memory and places these to + * variables, respectively, system_memory_rgns and usablemem_rgns. + */ + +static int iomem_range_callback(void *UNUSED(data), int UNUSED(nr), + char *str, unsigned long long base, + unsigned long long length) +{ + if (strncmp(str, CRASH_KERNEL, strlen(CRASH_KERNEL)) == 0) + return mem_regions_alloc_and_add(&usablemem_rgns, + base, length, RANGE_RAM); + else if (strncmp(str, SYSTEM_RAM, strlen(SYSTEM_RAM)) == 0) + return mem_regions_alloc_and_add(&system_memory_rgns, + base, length, RANGE_RAM); + else if (strncmp(str, KERNEL_CODE, strlen(KERNEL_CODE)) == 0) + elf_info64.kern_paddr_start = base; + else if (strncmp(str, KERNEL_DATA, strlen(KERNEL_DATA)) == 0) + elf_info64.kern_size = base + length - elf_info64.kern_paddr_start; + + return 0; +} + +int is_crashkernel_mem_reserved(void) +{ + if (!usablemem_rgns.size) + kexec_iomem_for_each_line(NULL, iomem_range_callback, NULL); + + return usablemem_rgns.size; +} + +/* + * crash_get_memory_ranges() - read system physical memory + * + * Function reads through system physical memory and stores found memory + * regions in system_memory_ranges. + * Regions are sorted in ascending order. + * + * Returns 0 in case of success and a negative value otherwise. + */ +static int crash_get_memory_ranges(void) +{ + int i; + + /* + * First read all memory regions that can be considered as + * system memory including the crash area. + */ + if (!usablemem_rgns.size) + kexec_iomem_for_each_line(NULL, iomem_range_callback, NULL); + + /* allow one or two regions for crash dump kernel */ + if (!usablemem_rgns.size) + return -EINVAL; + + dbgprint_mem_range("Reserved memory range", + usablemem_rgns.ranges, usablemem_rgns.size); + + for (i = 0; i < usablemem_rgns.size; i++) { + if (mem_regions_alloc_and_exclude(&system_memory_rgns, + &crash_reserved_mem[i])) { + fprintf(stderr, "Cannot allocate memory for ranges\n"); + return -ENOMEM; + } + } + + /* + * Make sure that the memory regions are sorted. + */ + mem_regions_sort(&system_memory_rgns); + + dbgprint_mem_range("Coredump memory ranges", + system_memory_rgns.ranges, system_memory_rgns.size); + + /* + * For additional kernel code/data segment. + * kern_paddr_start/kern_size are determined in iomem_range_callback + */ + elf_info64.kern_vaddr_start = get_kernel_sym("_text"); + if (!elf_info64.kern_vaddr_start) + elf_info64.kern_vaddr_start = UINT64_MAX; + + return 0; +} + +/* + * load_crashdump_segments() - load the elf core header + * @info: kexec info structure + * + * This function creates and loads an additional segment of elf core header + : which is used to construct /proc/vmcore on crash dump kernel. + * + * Return 0 in case of success and -1 in case of error. + */ + +int load_crashdump_segments(struct kexec_info *info) +{ + unsigned long elfcorehdr; + unsigned long bufsz; + void *buf; + int err; + + /* + * First fetch all the memory (RAM) ranges that we are going to + * pass to the crash dump kernel during panic. + */ + + err = crash_get_memory_ranges(); + + if (err) + return EFAILED; + + err = crash_create_elf64_headers(info, &elf_info64, + system_memory_rgns.ranges, system_memory_rgns.size, + &buf, &bufsz, ELF_CORE_HEADER_ALIGN); + + if (err) + return EFAILED; + + elfcorehdr = add_buffer(info, buf, bufsz, bufsz, 1024, + crash_reserved_mem[usablemem_rgns.size - 1].start, + crash_reserved_mem[usablemem_rgns.size - 1].end, -1); + + elfcorehdr_mem.start = elfcorehdr; + elfcorehdr_mem.end = elfcorehdr + bufsz - 1; + + dbgprintf("%s: elfcorehdr 0x%llx-0x%llx\n", __func__, + elfcorehdr_mem.start, elfcorehdr_mem.end); + + return 0; +} + +/* + * e_entry and p_paddr are actually in virtual address space. + * Those values will be translated to physcal addresses by using + * virt_to_phys() in add_segment(). + * So let's fix up those values for later use so the memory base will be + * correctly replaced with crash_reserved_mem[usablemem_rgns.size - 1].start. + */ +void fixup_elf_addrs(struct mem_ehdr *ehdr) +{ + struct mem_phdr *phdr; + int i; + + ehdr->e_entry += crash_reserved_mem[usablemem_rgns.size - 1].start; + + for (i = 0; i < ehdr->e_phnum; i++) { + phdr = &ehdr->e_phdr[i]; + if (phdr->p_type != PT_LOAD) + continue; + phdr->p_paddr += crash_reserved_mem[usablemem_rgns.size - 1].start; + } +} + +int get_crash_kernel_load_range(uint64_t *start, uint64_t *end) +{ + if (!usablemem_rgns.size) + kexec_iomem_for_each_line(NULL, iomem_range_callback, NULL); + + if (!usablemem_rgns.size) + return -1; + + *start = crash_reserved_mem[usablemem_rgns.size - 1].start; + *end = crash_reserved_mem[usablemem_rgns.size - 1].end; + + return 0; +} diff --git a/kexec/arch/loongarch/crashdump-loongarch.h b/kexec/arch/loongarch/crashdump-loongarch.h new file mode 100644 index 0000000..25ff24b --- /dev/null +++ b/kexec/arch/loongarch/crashdump-loongarch.h @@ -0,0 +1,26 @@ +#ifndef CRASHDUMP_LOONGARCH_H +#define CRASHDUMP_LOONGARCH_H + +struct kexec_info; +extern struct memory_ranges usablemem_rgns; +extern struct memory_range crash_reserved_mem[]; +extern struct memory_range elfcorehdr_mem; + +int load_crashdump_segments(struct kexec_info *info); +int is_crashkernel_mem_reserved(void); +void fixup_elf_addrs(struct mem_ehdr *ehdr); +int get_crash_kernel_load_range(uint64_t *start, uint64_t *end); + +#define PAGE_OFFSET 0x9000000000000000ULL +#define MAXMEM 0 + +#define CRASH_MAX_MEMMAP_NR (KEXEC_MAX_SEGMENTS + 1) +#define CRASH_MAX_MEMORY_RANGES (MAX_MEMORY_RANGES + 2) + +/* crash dump kernel support at most two regions, low_region and high region. */ +#define CRASH_MAX_RESERVED_RANGES 2 + +#define COMMAND_LINE_SIZE 512 + +extern struct arch_options_t arch_options; +#endif /* CRASHDUMP_LOONGARCH_H */ diff --git a/kexec/arch/loongarch/image-header.h b/kexec/arch/loongarch/image-header.h new file mode 100644 index 0000000..3b75765 --- /dev/null +++ b/kexec/arch/loongarch/image-header.h @@ -0,0 +1,79 @@ +/* + * LoongArch binary image header. + */ + +#if !defined(__LOONGARCH_IMAGE_HEADER_H) +#define __LOONGARCH_IMAGE_HEADER_H + +#include <endian.h> +#include <stdint.h> + +/** + * struct loongarch_image_header + * + * @pe_sig: Optional PE format 'MZ' signature. + * @reserved_1: Reserved. + * @kernel_entry: Kernel image entry pointer. + * @image_size: An estimated size of the memory image size in LSB byte order. + * @text_offset: The image load offset in LSB byte order. + * @reserved_2: Reserved. + * @reserved_3: Reserved. + * @pe_header: Optional offset to a PE format header. + **/ + +struct loongarch_image_header { + uint8_t pe_sig[2]; + uint16_t reserved_1[3]; + uint64_t kernel_entry; + uint64_t image_size; + uint64_t text_offset; + uint64_t reserved_2[3]; + uint32_t reserved_3; + uint32_t pe_header; +}; + +static const uint8_t loongarch_image_pe_sig[2] = {'M', 'Z'}; + +/** + * loongarch_header_check_pe_sig - Helper to check the loongarch image header. + * + * Returns non-zero if 'MZ' signature is found. + */ + +static inline int loongarch_header_check_pe_sig(const struct loongarch_image_header *h) +{ + if (!h) + return 0; + + return (h->pe_sig[0] == loongarch_image_pe_sig[0] + && h->pe_sig[1] == loongarch_image_pe_sig[1]); +} + +static inline uint64_t loongarch_header_text_offset( + const struct loongarch_image_header *h) +{ + if (!h) + return 0; + + return le64toh(h->text_offset); +} + +static inline uint64_t loongarch_header_image_size( + const struct loongarch_image_header *h) +{ + if (!h) + return 0; + + return le64toh(h->image_size); +} + +static inline uint64_t loongarch_header_kernel_entry( + const struct loongarch_image_header *h) +{ + if (!h) + return 0; + + return le64toh(h->kernel_entry); +} + +#endif diff --git a/kexec/arch/loongarch/include/arch/options.h b/kexec/arch/loongarch/include/arch/options.h new file mode 100644 index 0000000..25a7dc1 --- /dev/null +++ b/kexec/arch/loongarch/include/arch/options.h @@ -0,0 +1,28 @@ +#ifndef KEXEC_ARCH_LOONGARCH_OPTIONS_H +#define KEXEC_ARCH_LOONGARCH_OPTIONS_H + +#define OPT_APPEND ((OPT_MAX)+0) +#define OPT_INITRD ((OPT_MAX)+1) +#define OPT_REUSE_CMDLINE ((OPT_MAX)+2) +#define OPT_ARCH_MAX ((OPT_MAX)+3) + +#define KEXEC_ARCH_OPTIONS \ + KEXEC_OPTIONS \ + { "append", 1, NULL, OPT_APPEND }, \ + { "command-line", 1, NULL, OPT_APPEND }, \ + { "initrd", 1, NULL, OPT_INITRD }, \ + { "ramdisk", 1, NULL, OPT_INITRD }, \ + { "reuse-cmdline", 0, NULL, OPT_REUSE_CMDLINE }, \ + +#define KEXEC_ARCH_OPT_STR KEXEC_OPT_STR /* Only accept long arch options. */ +#define KEXEC_ALL_OPTIONS KEXEC_ARCH_OPTIONS +#define KEXEC_ALL_OPT_STR KEXEC_ARCH_OPT_STR + +static const char loongarch_opts_usage[] __attribute__ ((unused)) = +" --append=STRING Set the kernel command line to STRING.\n" +" --command-line=STRING Set the kernel command line to STRING.\n" +" --initrd=FILE Use FILE as the kernel initial ramdisk.\n" +" --ramdisk=FILE Use FILE as the kernel initial ramdisk.\n" +" --reuse-cmdline Use kernel command line from running system.\n"; + +#endif /* KEXEC_ARCH_LOONGARCH_OPTIONS_H */ diff --git a/kexec/arch/loongarch/kexec-elf-loongarch.c b/kexec/arch/loongarch/kexec-elf-loongarch.c new file mode 100644 index 0000000..45387ca --- /dev/null +++ b/kexec/arch/loongarch/kexec-elf-loongarch.c @@ -0,0 +1,125 @@ +/* + * kexec-elf-loongarch.c - kexec Elf loader for loongarch + * + * Copyright (C) 2022 Loongson Technology Corporation Limited. + * Youling Tang <tangyouling@loongson.cn> + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. +*/ + +#define _GNU_SOURCE + +#include <limits.h> +#include <errno.h> +#include <elf.h> + +#include "kexec.h" +#include "kexec-elf.h" +#include "kexec-syscall.h" +#include "crashdump-loongarch.h" +#include "kexec-loongarch.h" +#include "arch/options.h" + +off_t initrd_base, initrd_size; + +int elf_loongarch_probe(const char *kernel_buf, off_t kernel_size) +{ + struct mem_ehdr ehdr; + int result; + + result = build_elf_exec_info(kernel_buf, kernel_size, &ehdr, 0); + if (result < 0) { + dbgprintf("%s: Not an ELF executable.\n", __func__); + goto out; + } + + /* Verify the architecuture specific bits. */ + if (ehdr.e_machine != EM_LOONGARCH) { + dbgprintf("%s: Not an LoongArch ELF executable.\n", __func__); + result = -1; + goto out; + } + + result = 0; +out: + free_elf_info(&ehdr); + return result; +} + +int elf_loongarch_load(int argc, char **argv, const char *kernel_buf, + off_t kernel_size, struct kexec_info *info) +{ + const struct loongarch_image_header *header = NULL; + unsigned long kernel_segment; + struct mem_ehdr ehdr; + int result; + + result = build_elf_exec_info(kernel_buf, kernel_size, &ehdr, 0); + + if (result < 0) { + dbgprintf("%s: build_elf_exec_info failed\n", __func__); + goto exit; + } + + kernel_segment = loongarch_locate_kernel_segment(info); + + if (kernel_segment == ULONG_MAX) { + dbgprintf("%s: Kernel segment is not allocated\n", __func__); + result = EFAILED; + goto exit; + } + + dbgprintf("%s: kernel_segment: %016lx\n", __func__, kernel_segment); + dbgprintf("%s: image_size: %016lx\n", __func__, + kernel_size); + dbgprintf("%s: text_offset: %016lx\n", __func__, + loongarch_mem.text_offset); + dbgprintf("%s: phys_offset: %016lx\n", __func__, + loongarch_mem.phys_offset); + dbgprintf("%s: PE format: %s\n", __func__, + (loongarch_header_check_pe_sig(header) ? "yes" : "no")); + + /* create and initialize elf core header segment */ + if (info->kexec_flags & KEXEC_ON_CRASH) { + result = load_crashdump_segments(info); + if (result) { + dbgprintf("%s: Creating eflcorehdr failed.\n", + __func__); + goto exit; + } + } + + /* load the kernel */ + if (info->kexec_flags & KEXEC_ON_CRASH) + /* + * offset addresses in elf header in order to load + * vmlinux (elf_exec) into crash kernel's memory. + */ + fixup_elf_addrs(&ehdr); + + info->entry = (void *)virt_to_phys(ehdr.e_entry); + + result = elf_exec_load(&ehdr, info); + + if (result) { + dbgprintf("%s: elf_exec_load failed\n", __func__); + goto exit; + } + + /* load additional data */ + result = loongarch_load_other_segments(info, kernel_segment + kernel_size); + +exit: + free_elf_info(&ehdr); + if (result) + fprintf(stderr, "kexec: Bad elf image file, load failed.\n"); + return result; +} + +void elf_loongarch_usage(void) +{ + printf( +" An LoongArch ELF image, little endian.\n" +" Typically vmlinux or a stripped version of vmlinux.\n\n"); +} diff --git a/kexec/arch/loongarch/kexec-elf-rel-loongarch.c b/kexec/arch/loongarch/kexec-elf-rel-loongarch.c new file mode 100644 index 0000000..59f7f5d --- /dev/null +++ b/kexec/arch/loongarch/kexec-elf-rel-loongarch.c @@ -0,0 +1,42 @@ +/* + * kexec-elf-rel-loongarch.c - kexec Elf relocation routines + * + * Copyright (C) 2022 Loongson Technology Corporation Limited. + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. +*/ + +#include <stdio.h> +#include <elf.h> +#include "../../kexec.h" +#include "../../kexec-elf.h" + +int machine_verify_elf_rel(struct mem_ehdr *ehdr) +{ + if (ehdr->ei_data != ELFDATA2MSB) + return 0; + + if (ehdr->ei_class != ELFCLASS32) + return 0; + + if (ehdr->e_machine != EM_LOONGARCH) + return 0; + + return 1; +} + +void machine_apply_elf_rel(struct mem_ehdr *UNUSED(ehdr), + struct mem_sym *UNUSED(sym), + unsigned long r_type, + void *UNUSED(location), + unsigned long UNUSED(address), + unsigned long UNUSED(value)) +{ + switch (r_type) { + + default: + die("Unknown rela relocation: %lu\n", r_type); + break; + } +} diff --git a/kexec/arch/loongarch/kexec-loongarch.c b/kexec/arch/loongarch/kexec-loongarch.c new file mode 100644 index 0000000..f47c998 --- /dev/null +++ b/kexec/arch/loongarch/kexec-loongarch.c @@ -0,0 +1,375 @@ +/* + * kexec-loongarch.c - kexec for loongarch + * + * Copyright (C) 2022 Loongson Technology Corporation Limited. + * Youling Tang <tangyouling@loongson.cn> + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#include <assert.h> +#include <errno.h> +#include <getopt.h> +#include <inttypes.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <linux/elf-em.h> +#include <elf.h> +#include <elf_info.h> + +#include "kexec.h" +#include "kexec-loongarch.h" +#include "crashdump-loongarch.h" +#include "iomem.h" +#include "kexec-syscall.h" +#include "mem_regions.h" +#include "arch/options.h" + +#define CMDLINE_PREFIX "kexec " +static char cmdline[COMMAND_LINE_SIZE] = CMDLINE_PREFIX; + +/* Adds "initrd=start,size" parameters to command line. */ +static int cmdline_add_initrd(char *cmdline, unsigned long addr, + unsigned long size) +{ + int cmdlen, len; + char str[50], *ptr; + + ptr = str; + strcpy(str, " initrd="); + ptr += strlen(str); + ultoa(addr, ptr); + strcat(str, ","); + ptr = str + strlen(str); + ultoa(size, ptr); + len = strlen(str); + cmdlen = strlen(cmdline) + len; + if (cmdlen > (COMMAND_LINE_SIZE - 1)) + die("Command line overflow\n"); + strcat(cmdline, str); + + return 0; +} + +/* Adds the appropriate "mem=size@start" options to command line, indicating the + * memory region the new kernel can use to boot into. */ +static int cmdline_add_mem(char *cmdline, unsigned long addr, + unsigned long size) +{ + int cmdlen, len; + char str[50], *ptr; + + addr = addr/1024; + size = size/1024; + ptr = str; + strcpy(str, " mem="); + ptr += strlen(str); + ultoa(size, ptr); + strcat(str, "K@"); + ptr = str + strlen(str); + ultoa(addr, ptr); + strcat(str, "K"); + len = strlen(str); + cmdlen = strlen(cmdline) + len; + if (cmdlen > (COMMAND_LINE_SIZE - 1)) + die("Command line overflow\n"); + strcat(cmdline, str); + + return 0; +} + +/* Adds the "elfcorehdr=size@start" command line parameter to command line. */ +static int cmdline_add_elfcorehdr(char *cmdline, unsigned long addr, + unsigned long size) +{ + int cmdlen, len; + char str[50], *ptr; + + addr = addr/1024; + size = size/1024; + ptr = str; + strcpy(str, " elfcorehdr="); + ptr += strlen(str); + ultoa(size, ptr); + strcat(str, "K@"); + ptr = str + strlen(str); + ultoa(addr, ptr); + strcat(str, "K"); + len = strlen(str); + cmdlen = strlen(cmdline) + len; + if (cmdlen > (COMMAND_LINE_SIZE - 1)) + die("Command line overflow\n"); + strcat(cmdline, str); + + return 0; +} + +/* Return a sorted list of memory ranges. */ +static struct memory_range memory_range[MAX_MEMORY_RANGES]; + +int get_memory_ranges(struct memory_range **range, int *ranges, + unsigned long UNUSED(kexec_flags)) +{ + int memory_ranges = 0; + + const char *iomem = proc_iomem(); + char line[MAX_LINE]; + FILE *fp; + unsigned long long start, end; + char *str; + int type, consumed, count; + + fp = fopen(iomem, "r"); + if (!fp) { + fprintf(stderr, "Cannot open %s: %s\n", iomem, strerror(errno)); + return -1; + } + + while (fgets(line, sizeof(line), fp) != 0) { + if (memory_ranges >= MAX_MEMORY_RANGES) + break; + count = sscanf(line, "%llx-%llx : %n", &start, &end, &consumed); + if (count != 2) + continue; + str = line + consumed; + end = end + 1; + if (!strncmp(str, SYSTEM_RAM, strlen(SYSTEM_RAM))) + type = RANGE_RAM; + else if (!strncmp(str, IOMEM_RESERVED, strlen(IOMEM_RESERVED))) + type = RANGE_RESERVED; + else + continue; + + if (memory_ranges > 0 && + memory_range[memory_ranges - 1].end == start && + memory_range[memory_ranges - 1].type == type) { + memory_range[memory_ranges - 1].end = end; + } else { + memory_range[memory_ranges].start = start; + memory_range[memory_ranges].end = end; + memory_range[memory_ranges].type = type; + memory_ranges++; + } + } + fclose(fp); + *range = memory_range; + *ranges = memory_ranges; + + dbgprint_mem_range("MEMORY RANGES:", *range, *ranges); + return 0; +} + +struct file_type file_type[] = { + {"elf-loongarch", elf_loongarch_probe, elf_loongarch_load, elf_loongarch_usage}, + {"pei-loongarch", pei_loongarch_probe, pei_loongarch_load, pei_loongarch_usage}, +}; +int file_types = sizeof(file_type) / sizeof(file_type[0]); + +/* loongarch global varables. */ + +struct loongarch_mem loongarch_mem; + +/** + * loongarch_process_image_header - Process the loongarch image header. + */ + +int loongarch_process_image_header(const struct loongarch_image_header *h) +{ + + if (!loongarch_header_check_pe_sig(h)) + return EFAILED; + + if (h->image_size) { + loongarch_mem.text_offset = loongarch_header_text_offset(h); + loongarch_mem.image_size = loongarch_header_image_size(h); + } + + return 0; +} + +void arch_usage(void) +{ + printf(loongarch_opts_usage); +} + +struct arch_options_t arch_options = { + .core_header_type = CORE_TYPE_ELF64, +}; + +int arch_process_options(int argc, char **argv) +{ + static const char short_options[] = KEXEC_ARCH_OPT_STR ""; + static const struct option options[] = { + KEXEC_ARCH_OPTIONS + { 0 }, + }; + int opt; + char *cmdline = NULL; + const char *append = NULL; + + while ((opt = getopt_long(argc, argv, short_options, + options, 0)) != -1) { + switch (opt) { + case OPT_APPEND: + append = optarg; + break; + case OPT_REUSE_CMDLINE: + cmdline = get_command_line(); + remove_parameter(cmdline, "kexec"); + remove_parameter(cmdline, "initrd"); + break; + case OPT_INITRD: + arch_options.initrd_file = optarg; + break; + default: + break; + } + } + + arch_options.command_line = concat_cmdline(cmdline, append); + + dbgprintf("%s:%d: command_line: %s\n", __func__, __LINE__, + arch_options.command_line); + dbgprintf("%s:%d: initrd: %s\n", __func__, __LINE__, + arch_options.initrd_file); + + return 0; +} + +const struct arch_map_entry arches[] = { + { "loongarch64", KEXEC_ARCH_LOONGARCH }, + { NULL, 0 }, +}; + +unsigned long loongarch_locate_kernel_segment(struct kexec_info *info) +{ + unsigned long hole; + + if (info->kexec_flags & KEXEC_ON_CRASH) { + unsigned long hole_end; + + hole = (crash_reserved_mem[usablemem_rgns.size - 1].start < mem_min ? + mem_min : crash_reserved_mem[usablemem_rgns.size - 1].start) + + loongarch_mem.text_offset; + hole = _ALIGN_UP(hole, MiB(1)); + hole_end = hole + loongarch_mem.text_offset + loongarch_mem.image_size; + + if ((hole_end > mem_max) || + (hole_end > crash_reserved_mem[usablemem_rgns.size - 1].end)) { + dbgprintf("%s: Crash kernel out of range\n", __func__); + hole = ULONG_MAX; + } + } else { + hole = locate_hole(info, + loongarch_mem.text_offset + loongarch_mem.image_size, + MiB(1), 0, ULONG_MAX, 1); + + if (hole == ULONG_MAX) + dbgprintf("%s: locate_hole failed\n", __func__); + } + + return hole; +} + +/* + * loongarch_load_other_segments - Prepare the initrd and cmdline segments. + */ + +int loongarch_load_other_segments(struct kexec_info *info, unsigned long hole_min) +{ + unsigned long initrd_min, hole_max; + char *initrd_buf = NULL; + unsigned long pagesize = getpagesize(); + + if (arch_options.command_line) { + if (strlen(arch_options.command_line) > + sizeof(cmdline) - 1) { + fprintf(stderr, + "Kernel command line too long for kernel!\n"); + return EFAILED; + } + + strncat(cmdline, arch_options.command_line, sizeof(cmdline) - 1); + } + + /* Put the other segments after the image. */ + + initrd_min = hole_min; + if (info->kexec_flags & KEXEC_ON_CRASH) + hole_max = crash_reserved_mem[usablemem_rgns.size - 1].end; + else + hole_max = ULONG_MAX; + + if (arch_options.initrd_file) { + + initrd_buf = slurp_decompress_file(arch_options.initrd_file, &initrd_size); + + initrd_base = add_buffer(info, initrd_buf, initrd_size, + initrd_size, sizeof(void *), + _ALIGN_UP(initrd_min, + pagesize), hole_max, 1); + dbgprintf("initrd_base: %lx, initrd_size: %lx\n", initrd_base, initrd_size); + + cmdline_add_initrd(cmdline, initrd_base, initrd_size); + } + + if (info->kexec_flags & KEXEC_ON_CRASH) { + cmdline_add_elfcorehdr(cmdline, elfcorehdr_mem.start, + elfcorehdr_mem.end - elfcorehdr_mem.start + 1); + + cmdline_add_mem(cmdline, crash_reserved_mem[usablemem_rgns.size - 1].start, + crash_reserved_mem[usablemem_rgns.size - 1].end - + crash_reserved_mem[usablemem_rgns.size - 1].start + 1); + } + + cmdline[sizeof(cmdline) - 1] = 0; + add_buffer(info, cmdline, sizeof(cmdline), sizeof(cmdline), + sizeof(void *), _ALIGN_UP(hole_min, getpagesize()), + 0xffffffff, 1); + + dbgprintf("%s:%d: command_line: %s\n", __func__, __LINE__, cmdline); + + return 0; + +} + +int arch_compat_trampoline(struct kexec_info *UNUSED(info)) +{ + return 0; +} + +void arch_update_purgatory(struct kexec_info *UNUSED(info)) +{ +} + +unsigned long virt_to_phys(unsigned long addr) +{ + return addr & ((1ULL << 48) - 1); +} + +/* + * add_segment() should convert base to a physical address on loongarch, + * while the default is just to work with base as is + */ +void add_segment(struct kexec_info *info, const void *buf, size_t bufsz, + unsigned long base, size_t memsz) +{ + add_segment_phys_virt(info, buf, bufsz, virt_to_phys(base), memsz, 1); +} + +/* + * add_buffer() should convert base to a physical address on loongarch, + * while the default is just to work with base as is + */ +unsigned long add_buffer(struct kexec_info *info, const void *buf, + unsigned long bufsz, unsigned long memsz, + unsigned long buf_align, unsigned long buf_min, + unsigned long buf_max, int buf_end) +{ + return add_buffer_phys_virt(info, buf, bufsz, memsz, buf_align, + buf_min, buf_max, buf_end, 1); +} diff --git a/kexec/arch/loongarch/kexec-loongarch.h b/kexec/arch/loongarch/kexec-loongarch.h new file mode 100644 index 0000000..5120a26 --- /dev/null +++ b/kexec/arch/loongarch/kexec-loongarch.h @@ -0,0 +1,60 @@ +#ifndef KEXEC_LOONGARCH_H +#define KEXEC_LOONGARCH_H + +#include <sys/types.h> + +#include "image-header.h" + +#define BOOT_BLOCK_VERSION 17 +#define BOOT_BLOCK_LAST_COMP_VERSION 16 + +#define MAX_MEMORY_RANGES 64 +#define MAX_LINE 160 + +#define CORE_TYPE_ELF64 1 + +#define COMMAND_LINE_SIZE 512 + +#define KiB(x) ((x) * 1024UL) +#define MiB(x) (KiB(x) * 1024UL) + +int elf_loongarch_probe(const char *kernel_buf, off_t kernel_size); +int elf_loongarch_load(int argc, char **argv, const char *buf, off_t len, + struct kexec_info *info); +void elf_loongarch_usage(void); + +int pei_loongarch_probe(const char *buf, off_t len); +int pei_loongarch_load(int argc, char **argv, const char *buf, off_t len, + struct kexec_info *info); +void pei_loongarch_usage(void); + +int loongarch_process_image_header(const struct loongarch_image_header *h); + +unsigned long loongarch_locate_kernel_segment(struct kexec_info *info); +int loongarch_load_other_segments(struct kexec_info *info, + unsigned long hole_min); + +struct arch_options_t { + char *command_line; + char *initrd_file; + char *dtb; + int core_header_type; +}; + +/** + * struct loongarch_mem - Memory layout info. + */ + +struct loongarch_mem { + uint64_t phys_offset; + uint64_t text_offset; + uint64_t image_size; +}; + +extern struct loongarch_mem loongarch_mem; + +extern struct memory_ranges usablemem_rgns; +extern struct arch_options_t arch_options; +extern off_t initrd_base, initrd_size; + +#endif /* KEXEC_LOONGARCH_H */ diff --git a/kexec/arch/loongarch/kexec-pei-loongarch.c b/kexec/arch/loongarch/kexec-pei-loongarch.c new file mode 100644 index 0000000..1a11103 --- /dev/null +++ b/kexec/arch/loongarch/kexec-pei-loongarch.c @@ -0,0 +1,124 @@ +/* + * LoongArch kexec PE format binary image support. + * + * Copyright (C) 2022 Loongson Technology Corporation Limited. + * Youling Tang <tangyouling@loongson.cn> + * + * derived from kexec-image-arm64.c + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#define _GNU_SOURCE + +#include <limits.h> +#include <errno.h> +#include <elf.h> + +#include "kexec.h" +#include "kexec-elf.h" +#include "image-header.h" +#include "kexec-syscall.h" +#include "crashdump-loongarch.h" +#include "kexec-loongarch.h" +#include "arch/options.h" + +int pei_loongarch_probe(const char *kernel_buf, off_t kernel_size) +{ + const struct loongarch_image_header *h; + + if (kernel_size < sizeof(struct loongarch_image_header)) { + dbgprintf("%s: No loongarch image header.\n", __func__); + return -1; + } + + h = (const struct loongarch_image_header *)(kernel_buf); + + if (!loongarch_header_check_pe_sig(h)) { + dbgprintf("%s: Bad loongarch PE image header.\n", __func__); + return -1; + } + + return 0; +} + +int pei_loongarch_load(int argc, char **argv, const char *buf, + off_t len, struct kexec_info *info) +{ + int result; + unsigned long hole_min = 0; + unsigned long kernel_segment, kernel_entry; + const struct loongarch_image_header *header; + + header = (const struct loongarch_image_header *)(buf); + + if (loongarch_process_image_header(header)) + return EFAILED; + + kernel_segment = loongarch_locate_kernel_segment(info); + + if (kernel_segment == ULONG_MAX) { + dbgprintf("%s: Kernel segment is not allocated\n", __func__); + result = EFAILED; + goto exit; + } + + kernel_entry = virt_to_phys(loongarch_header_kernel_entry(header)); + + if (info->kexec_flags & KEXEC_ON_CRASH) + /* + * offset addresses in order to load vmlinux.efi into + * crash kernel's memory. + */ + kernel_entry += crash_reserved_mem[usablemem_rgns.size - 1].start; + + dbgprintf("%s: kernel_segment: %016lx\n", __func__, kernel_segment); + dbgprintf("%s: kernel_entry: %016lx\n", __func__, kernel_entry); + dbgprintf("%s: image_size: %016lx\n", __func__, + loongarch_mem.image_size); + dbgprintf("%s: text_offset: %016lx\n", __func__, + loongarch_mem.text_offset); + dbgprintf("%s: phys_offset: %016lx\n", __func__, + loongarch_mem.phys_offset); + dbgprintf("%s: PE format: %s\n", __func__, + (loongarch_header_check_pe_sig(header) ? "yes" : "no")); + + /* Get kernel entry point */ + info->entry = (void *)kernel_entry; + + hole_min = kernel_segment + loongarch_mem.image_size; + + /* Create and initialize elf core header segment */ + if (info->kexec_flags & KEXEC_ON_CRASH) { + result = load_crashdump_segments(info); + if (result) { + dbgprintf("%s: Creating eflcorehdr failed.\n", + __func__); + goto exit; + } + } + + /* Load the kernel */ + add_segment(info, buf, len, kernel_segment, loongarch_mem.image_size); + + /* Prepare and load dtb and initrd data */ + result = loongarch_load_other_segments(info, hole_min); + if (result) { + fprintf(stderr, "kexec: Load dtb and initrd segments failed.\n"); + goto exit; + } + +exit: + if (result) + fprintf(stderr, "kexec: load failed.\n"); + + return result; +} + +void pei_loongarch_usage(void) +{ + printf( +" An LoongArch PE format binary image, uncompressed, little endian.\n" +" Typically a vmlinux.efi file.\n\n"); +} diff --git a/kexec/arch/m68k/Makefile b/kexec/arch/m68k/Makefile new file mode 100644 index 0000000..eeaacbd --- /dev/null +++ b/kexec/arch/m68k/Makefile @@ -0,0 +1,15 @@ +# +# kexec m68k (linux booting linux) +# +m68k_KEXEC_SRCS = kexec/arch/m68k/kexec-m68k.c +m68k_KEXEC_SRCS += kexec/arch/m68k/kexec-elf-m68k.c +m68k_KEXEC_SRCS += kexec/arch/m68k/kexec-elf-rel-m68k.c +m68k_KEXEC_SRCS += kexec/arch/m68k/bootinfo.c + +m68k_ADD_SEGMENT = +m68k_VIRT_TO_PHYS = + +dist += kexec/arch/m68k/Makefile $(m68k_KEXEC_SRCS) \ + kexec/arch/m68k/bootinfo.h \ + kexec/arch/m68k/kexec-m68k.h \ + kexec/arch/m68k/include/arch/options.h diff --git a/kexec/arch/m68k/bootinfo.c b/kexec/arch/m68k/bootinfo.c new file mode 100644 index 0000000..086a34b --- /dev/null +++ b/kexec/arch/m68k/bootinfo.c @@ -0,0 +1,285 @@ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/random.h> + +#include "../../kexec.h" + +#include "bootinfo.h" + +const char *bootinfo_file = DEFAULT_BOOTINFO_FILE; +static struct bi_rec *bootinfo; +static off_t bootinfo_size; + +static unsigned int num_memchunks; + +static struct bi_rec *bi_next(struct bi_rec *bi, uint16_t size) +{ + return (void *)((unsigned long)bi + size); +} + +static struct bi_rec *bi_find(struct bi_rec *prev, uint16_t tag) +{ + struct bi_rec *bi = prev ? bi_next(prev, prev->size) : bootinfo; + + for (bi = prev ? bi_next(prev, prev->size) : bootinfo; + bi->tag != BI_LAST; bi = bi_next(bi, bi->size)) + if (bi->tag == tag) + return bi; + return NULL; +} + +static void bi_remove(uint16_t tag) +{ + struct bi_rec *bi; + off_t rem; + uint16_t size; + + bi = bootinfo; + rem = bootinfo_size; + while (1) { + if (bi->tag == BI_LAST) + break; + + size = bi->size; + if (bi->tag == tag) { + memmove(bi, bi_next(bi, size), rem - size); + bootinfo_size -= size; + rem -= size; + continue; + } + + bi = bi_next(bi, size); + rem -= size; + } +} + +static struct bi_rec *bi_add(uint16_t tag, uint16_t size) +{ + struct bi_rec *bi; + + /* Add 4-byte header and round up to multiple of 4 bytes */ + size = _ALIGN_UP(4 + size, 4); + + bootinfo = xrealloc(bootinfo, bootinfo_size + size); + + /* Replace old sentinel by new record */ + bi = bi_next(bootinfo, bootinfo_size - 2); + bootinfo_size += size; + memset(bi, 0, size); + bi->tag = tag; + bi->size = size; + + /* Re-add sentinel */ + bi_next(bi, size)->tag = BI_LAST; + + return bi; +} + +void bootinfo_load(void) +{ + struct bi_rec *bi; + off_t rem; + uint16_t tag, size; + + dbgprintf("Loading bootinfo from %s\n", bootinfo_file); + bootinfo = (void *)slurp_file_len(bootinfo_file, MAX_BOOTINFO_SIZE, + &bootinfo_size); + if (!bootinfo) + die("No bootinfo\n"); + + bi = bootinfo; + rem = bootinfo_size; + while (1) { + if (rem < 2) + die("Unexpected end of bootinfo\n"); + + tag = bi->tag; + if (tag == BI_LAST) { + rem -= 2; + break; + } + + if (rem < 4) + die("Unexpected end of bootinfo\n"); + + size = bi->size; + if (size < 4 || size % 4) + die("Invalid tag size\n"); + if (rem < size) + die("Unexpected end of bootinfo\n"); + + if (tag == BI_MEMCHUNK) + num_memchunks++; + + bi = bi_next(bi, size); + rem -= size; + } + + if (rem) + die("Trailing data at end of bootinfo\n"); +} + +void bootinfo_print(void) +{ + struct bi_rec *bi = bootinfo; + uint16_t tag, size; + + while (1) { + tag = bi->tag; + if (tag == BI_LAST) { + puts("BI_LAST"); + break; + } + + size = bi->size; + switch (tag) { + case BI_MACHTYPE: + printf("BI_MACHTYPE: 0x%08x\n", bi->machtype); + break; + + case BI_MEMCHUNK: + printf("BI_MEMCHUNK: 0x%08x bytes at 0x%08x\n", + bi->mem_info.size, bi->mem_info.addr); + break; + + case BI_RAMDISK: + printf("BI_RAMDISK: 0x%08x bytes at 0x%08x\n", + bi->mem_info.size, bi->mem_info.addr); + break; + + case BI_COMMAND_LINE: + printf("BI_COMMAND_LINE: %s\n", bi->string); + break; + + case BI_RNG_SEED: + /* These are secret, so never print them to the console */ + printf("BI_RNG_SEED: 0x%08x bytes\n", be16_to_cpu(bi->rng_seed.len)); + break; + + default: + printf("BI tag 0x%04x size %u\n", tag, size); + break; + } + bi = bi_next(bi, size); + } +} + +int bootinfo_get_memory_ranges(struct memory_range **range) +{ + struct memory_range *ranges; + unsigned int i; + struct bi_rec *bi; + + ranges = xmalloc(num_memchunks * sizeof(struct memory_range)); + for (i = 0, bi = NULL; + i < num_memchunks && (bi = bi_find(bi, BI_MEMCHUNK)); i++) { + ranges[i].start = bi->mem_info.addr; + ranges[i].end = bi->mem_info.addr + bi->mem_info.size - 1; + ranges[i].type = RANGE_RAM; + } + + *range = ranges; + return i; +} + +void bootinfo_set_cmdline(const char *cmdline) +{ + struct bi_rec *bi; + uint16_t size; + + /* Remove existing command line records */ + bi_remove(BI_COMMAND_LINE); + + if (!cmdline) + return; + + /* Add new command line record */ + size = strlen(cmdline) + 1; + bi = bi_add(BI_COMMAND_LINE, size); + memcpy(bi->string, cmdline, size); +} + +void bootinfo_set_ramdisk(unsigned long ramdisk_addr, + unsigned long ramdisk_size) +{ + struct bi_rec *bi; + + /* Remove existing ramdisk records */ + bi_remove(BI_RAMDISK); + + if (!ramdisk_size) + return; + + /* Add new ramdisk record */ + bi = bi_add(BI_RAMDISK, sizeof(bi->mem_info)); + bi->mem_info.addr = ramdisk_addr; + bi->mem_info.size = ramdisk_size; +} + +void bootinfo_add_rng_seed(void) +{ + enum { RNG_SEED_LEN = 32 }; + struct bi_rec *bi; + + /* Remove existing rng seed records */ + bi_remove(BI_RNG_SEED); + + /* Add new rng seed record */ + bi = bi_add(BI_RNG_SEED, sizeof(bi->rng_seed) + RNG_SEED_LEN); + if (getrandom(bi->rng_seed.data, RNG_SEED_LEN, GRND_NONBLOCK) != RNG_SEED_LEN) { + bi_remove(BI_RNG_SEED); + return; + } + bi->rng_seed.len = cpu_to_be16(RNG_SEED_LEN); +} + + + /* + * Check the bootinfo version in the kernel image + * All failures are non-fatal, as kexec may be used to load + * non-Linux images + */ + +void bootinfo_check_bootversion(const struct kexec_info *info) +{ + struct bi_rec *bi; + const struct bootversion *bv; + uint16_t major, minor; + unsigned int i; + + bv = info->segment[0].buf; + if (bv->magic != BOOTINFOV_MAGIC) { + printf("WARNING: No bootversion in kernel image\n"); + return; + } + + bi = bi_find(NULL, BI_MACHTYPE); + if (!bi) { + printf("WARNING: No machtype in bootinfo\n"); + return; + } + + for (i = 0; bv->machversions[i].machtype != bi->machtype; i++) + if (!bv->machversions[i].machtype) { + printf("WARNING: Machtype 0x%08x not in kernel bootversion\n", + bi->machtype); + return; + } + + major = BI_VERSION_MAJOR(bv->machversions[i].version); + minor = BI_VERSION_MINOR(bv->machversions[i].version); + dbgprintf("Kernel uses bootversion %u.%u\n", major, minor); + if (major != SUPPORTED_BOOTINFO_VERSION) + printf("WARNING: Kernel bootversion %u.%u is too %s for this kexec (expected %u.x)\n", + major, minor, + major < SUPPORTED_BOOTINFO_VERSION ? "old" : "new", + SUPPORTED_BOOTINFO_VERSION); +} + +void add_bootinfo(struct kexec_info *info, unsigned long addr) +{ + add_buffer(info, bootinfo, bootinfo_size, bootinfo_size, + sizeof(void *), addr, 0x0fffffff, 1); +} diff --git a/kexec/arch/m68k/bootinfo.h b/kexec/arch/m68k/bootinfo.h new file mode 100644 index 0000000..bb8a03a --- /dev/null +++ b/kexec/arch/m68k/bootinfo.h @@ -0,0 +1,53 @@ +#include <asm/bootinfo.h> + +#define DEFAULT_BOOTINFO_FILE "/proc/bootinfo" +#define MAX_BOOTINFO_SIZE 1536 + +/* New in 6.2's <asm/bootinfo.h>. Remove once 6.2 is baseline version. */ +#ifndef BI_RNG_SEED +#define BI_RNG_SEED 0x0008 +#endif + + + /* + * Convenience overlay of several struct bi_record variants + */ + +struct bi_rec { + __be16 tag; + __be16 size; + union { + __be32 data[0]; + /* shorthands for the types we use */ + __be32 machtype; + struct { + __be32 addr; + __be32 size; + } mem_info; + char string[0]; + struct { + __be16 len; + __u8 data[0]; + } rng_seed; + }; +}; + + + /* + * We only support the "new" tagged bootinfo (v2) + */ + +#define SUPPORTED_BOOTINFO_VERSION 2 + + +extern const char *bootinfo_file; + +extern void bootinfo_load(void); +extern void bootinfo_print(void); +extern int bootinfo_get_memory_ranges(struct memory_range **range); +extern void bootinfo_set_cmdline(const char *cmdline); +extern void bootinfo_set_ramdisk(unsigned long ramdisk_addr, + unsigned long ramdisk_size); +extern void bootinfo_add_rng_seed(void); +extern void bootinfo_check_bootversion(const struct kexec_info *info); +extern void add_bootinfo(struct kexec_info *info, unsigned long addr); diff --git a/kexec/arch/m68k/include/arch/options.h b/kexec/arch/m68k/include/arch/options.h new file mode 100644 index 0000000..f279d54 --- /dev/null +++ b/kexec/arch/m68k/include/arch/options.h @@ -0,0 +1,45 @@ +#ifndef KEXEC_ARCH_M68K_OPTIONS_H +#define KEXEC_ARCH_M68K_OPTIONS_H + +#define OPT_ARCH_MAX (OPT_MAX+0) + +/* All 'local' loader options: */ +#define OPT_APPEND (OPT_ARCH_MAX+0) +#define OPT_REUSE_CMDLINE (OPT_ARCH_MAX+1) +#define OPT_RAMDISK (OPT_ARCH_MAX+2) +#define OPT_BOOTINFO (OPT_ARCH_MAX+3) + +/* Options relevant to the architecture (excluding loader-specific ones), + * in this case none: + */ +#define KEXEC_ARCH_OPTIONS \ + KEXEC_OPTIONS \ + +#define KEXEC_ARCH_OPT_STR KEXEC_OPT_STR "" + +/* The following two #defines list ALL of the options added by all of the + * architecture's loaders. + * o main() uses this complete list to scan for its options, ignoring + * arch-specific/loader-specific ones. + * o Then, arch_process_options() uses this complete list to scan for its + * options, ignoring general/loader-specific ones. + * o Then, the file_type[n].load re-scans for options, using + * KEXEC_ARCH_OPTIONS plus its loader-specific options subset. + * Any unrecognised options cause an error here. + * + * This is done so that main()'s/arch_process_options()'s getopt_long() calls + * don't choose a kernel filename from random arguments to options they don't + * recognise -- as they now recognise (if not act upon) all possible options. + */ +#define KEXEC_ALL_OPTIONS \ + KEXEC_ARCH_OPTIONS \ + { "command-line", 1, NULL, OPT_APPEND }, \ + { "append", 1, NULL, OPT_APPEND }, \ + { "reuse-cmdline", 0, NULL, OPT_REUSE_CMDLINE }, \ + { "ramdisk", 1, NULL, OPT_RAMDISK }, \ + { "initrd", 1, NULL, OPT_RAMDISK }, \ + { "bootinfo", 1, NULL, OPT_BOOTINFO }, + +#define KEXEC_ALL_OPT_STR KEXEC_ARCH_OPT_STR + +#endif /* KEXEC_ARCH_M68K_OPTIONS_H */ diff --git a/kexec/arch/m68k/kexec-elf-m68k.c b/kexec/arch/m68k/kexec-elf-m68k.c new file mode 100644 index 0000000..a2bf7ee --- /dev/null +++ b/kexec/arch/m68k/kexec-elf-m68k.c @@ -0,0 +1,183 @@ +/* + * kexec-elf-m68k.c - kexec Elf loader for m68k + * + * Copyright (C) 2013 Geert Uytterhoeven + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. +*/ + +#define _GNU_SOURCE +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <getopt.h> +#include <elf.h> +#include <boot/elf_boot.h> +#include <ip_checksum.h> +#include "../../kexec.h" +#include "../../kexec-elf.h" +#include "../../kexec-syscall.h" +#include "kexec-m68k.h" +#include "bootinfo.h" +#include <arch/options.h> + +#define KiB * 1024 +#define MiB * 1024 KiB + +#define PAGE_SIZE 4 KiB + + +int elf_m68k_probe(const char *buf, off_t len) +{ + struct mem_ehdr ehdr; + int result; + result = build_elf_exec_info(buf, len, &ehdr, 0); + if (result < 0) + goto out; + + /* Verify the architecuture specific bits */ + if (ehdr.e_machine != EM_68K) { + /* for a different architecture */ + fprintf(stderr, "Not for this architecture.\n"); + result = -1; + goto out; + } + result = 0; + out: + free_elf_info(&ehdr); + return result; +} + +void elf_m68k_usage(void) +{ + printf(" --command-line=STRING Set the kernel command line to STRING\n" + " --append=STRING Set the kernel command line to STRING\n" + " --reuse-cmdline Use kernel command line from running system.\n" + " --ramdisk=FILE Use FILE as the kernel's initial ramdisk.\n" + " --initrd=FILE Use FILE as the kernel's initial ramdisk.\n" + " --bootinfo=FILE Use FILE as the kernel's bootinfo\n" + ); +} + +static unsigned long segment_end(const struct kexec_info *info, int i) +{ + return (unsigned long)info->segment[i].mem + info->segment[i].memsz - 1; +} + +int elf_m68k_load(int argc, char **argv, const char *buf, off_t len, + struct kexec_info *info) +{ + struct mem_ehdr ehdr; + const char *cmdline = NULL, *ramdisk_file = NULL; + int opt, result, i; + unsigned long bootinfo_addr, ramdisk_addr = 0; + off_t ramdisk_size = 0; + + /* See options.h if adding any more options. */ + static const struct option options[] = { + KEXEC_ARCH_OPTIONS + { "command-line", 1, NULL, OPT_APPEND }, + { "append", 1, NULL, OPT_APPEND }, + { "reuse-cmdline", 0, NULL, OPT_REUSE_CMDLINE }, + { "ramdisk", 1, NULL, OPT_RAMDISK }, + { "initrd", 1, NULL, OPT_RAMDISK }, + { "bootinfo", 1, NULL, OPT_BOOTINFO }, + { 0, 0, NULL, 0 }, + }; + + static const char short_options[] = KEXEC_ARCH_OPT_STR "d"; + + while ((opt = getopt_long(argc, argv, short_options, options, 0)) != + -1) { + switch (opt) { + default: + /* Ignore core options */ + if (opt < OPT_ARCH_MAX) + break; + case OPT_APPEND: + cmdline = optarg; + break; + case OPT_REUSE_CMDLINE: + cmdline = get_command_line(); + break; + case OPT_RAMDISK: + ramdisk_file = optarg; + break; + case OPT_BOOTINFO: + break; + } + } + + result = build_elf_exec_info(buf, len, &ehdr, 0); + if (result < 0) + die("ELF exec parse failed\n"); + + /* Fixup PT_LOAD segments that include the ELF header (offset zero) */ + for (i = 0; i < ehdr.e_phnum; i++) { + struct mem_phdr *phdr; + phdr = &ehdr.e_phdr[i]; + if (phdr->p_type != PT_LOAD || phdr->p_offset) + continue; + + dbgprintf("Removing ELF header from segment %d\n", i); + phdr->p_paddr += PAGE_SIZE; + phdr->p_vaddr += PAGE_SIZE; + phdr->p_filesz -= PAGE_SIZE; + phdr->p_memsz -= PAGE_SIZE; + phdr->p_offset += PAGE_SIZE; + phdr->p_data += PAGE_SIZE; + } + + /* Load the ELF data */ + result = elf_exec_load(&ehdr, info); + if (result < 0) + die("ELF exec load failed\n"); + + info->entry = (void *)virt_to_phys(ehdr.e_entry); + + /* Bootinfo must be stored right after the kernel */ + bootinfo_addr = segment_end(info, info->nr_segments - 1) + 1; + + /* Load ramdisk */ + if (ramdisk_file) { + void *ramdisk = slurp_decompress_file(ramdisk_file, + &ramdisk_size); + /* Store ramdisk at top of first memory chunk */ + ramdisk_addr = _ALIGN_DOWN(info->memory_range[0].end - + ramdisk_size + 1, + PAGE_SIZE); + if (!buf) + die("Ramdisk load failed\n"); + add_buffer(info, ramdisk, ramdisk_size, ramdisk_size, + PAGE_SIZE, ramdisk_addr, info->memory_range[0].end, + 1); + } + + /* Update and add bootinfo */ + bootinfo_set_cmdline(cmdline); + bootinfo_set_ramdisk(ramdisk_addr, ramdisk_size); + bootinfo_add_rng_seed(); + if (kexec_debug) + bootinfo_print(); + add_bootinfo(info, bootinfo_addr); + + /* + * Check if the kernel (and bootinfo) exceed 4 MiB, as current kernels + * don't support that. + * As the segments are still unsorted, the bootinfo is located in the + * last segment. + */ + if (segment_end(info, info->nr_segments - 1) >= virt_to_phys(4 MiB - 1)) + printf("WARNING: Kernel is larger than 4 MiB\n"); + + /* Check struct bootversion at start of kernel */ + bootinfo_check_bootversion(info); + + return 0; +} diff --git a/kexec/arch/m68k/kexec-elf-rel-m68k.c b/kexec/arch/m68k/kexec-elf-rel-m68k.c new file mode 100644 index 0000000..0cc38cc --- /dev/null +++ b/kexec/arch/m68k/kexec-elf-rel-m68k.c @@ -0,0 +1,39 @@ +/* + * kexec-elf-rel-m68k.c - kexec Elf relocation routines + * + * Copyright (C) 2013 Geert Uytterhoeven + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. +*/ + +#include <stdio.h> +#include <elf.h> +#include "../../kexec.h" +#include "../../kexec-elf.h" + +int machine_verify_elf_rel(struct mem_ehdr *ehdr) +{ + if (ehdr->ei_data != ELFDATA2MSB) + return 0; + if (ehdr->ei_class != ELFCLASS32) + return 0; + if (ehdr->e_machine != EM_68K) + return 0; + return 1; +} + +void machine_apply_elf_rel(struct mem_ehdr *UNUSED(ehdr), + struct mem_sym *UNUSED(sym), + unsigned long r_type, + void *UNUSED(location), + unsigned long UNUSED(address), + unsigned long UNUSED(value)) +{ + switch (r_type) { + default: + die("Unknown rela relocation: %lu\n", r_type); + break; + } + return; +} diff --git a/kexec/arch/m68k/kexec-m68k.c b/kexec/arch/m68k/kexec-m68k.c new file mode 100644 index 0000000..cb54927 --- /dev/null +++ b/kexec/arch/m68k/kexec-m68k.c @@ -0,0 +1,110 @@ +/* + * kexec-m68k.c - kexec for m68k + * + * Copyright (C) 2013 Geert Uytterhoeven + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#include <stddef.h> +#include <stdio.h> +#include <errno.h> +#include <stdint.h> +#include <string.h> +#include <getopt.h> +#include "../../kexec.h" +#include "../../kexec-syscall.h" +#include "kexec-m68k.h" +#include "bootinfo.h" +#include <arch/options.h> + + +static unsigned long m68k_memoffset; + + +/* Return a sorted list of memory ranges. */ +int get_memory_ranges(struct memory_range **range, int *ranges, + unsigned long kexec_flags) +{ + bootinfo_load(); + *ranges = bootinfo_get_memory_ranges(range); + m68k_memoffset = (*range)[0].start; + return 0; +} + + +struct file_type file_type[] = { + {"elf-m68k", elf_m68k_probe, elf_m68k_load, elf_m68k_usage}, +}; +int file_types = sizeof(file_type) / sizeof(file_type[0]); + +void arch_usage(void) +{ +} + +int arch_process_options(int argc, char **argv) +{ + static const struct option options[] = { + KEXEC_ALL_OPTIONS + { "bootinfo", 1, NULL, OPT_BOOTINFO }, + { 0, 0, NULL, 0 }, + }; + static const char short_options[] = KEXEC_ALL_OPT_STR; + int opt; + + opterr = 0; /* Don't complain about unrecognized options here */ + while ((opt = getopt_long(argc, argv, short_options, options, 0)) != + -1) { + switch (opt) { + default: + break; + case OPT_BOOTINFO: + bootinfo_file = optarg; + break; + } + } + /* Reset getopt for the next pass; called in other source modules */ + opterr = 1; + optind = 1; + return 0; +} + +const struct arch_map_entry arches[] = { + { "m68k", KEXEC_ARCH_68K }, + { NULL, 0 }, +}; + +int arch_compat_trampoline(struct kexec_info *UNUSED(info)) +{ + return 0; +} + +void arch_update_purgatory(struct kexec_info *UNUSED(info)) +{ +} + +int is_crashkernel_mem_reserved(void) +{ + return 0; +} + +int get_crash_kernel_load_range(uint64_t *start, uint64_t *end) +{ + /* Crash kernel region size is not exposed by the system */ + return -1; +} + +unsigned long virt_to_phys(unsigned long addr) +{ + return addr + m68k_memoffset; +} + +/* + * add_segment() should convert base to a physical address on m68k, + * while the default is just to work with base as is */ +void add_segment(struct kexec_info *info, const void *buf, size_t bufsz, + unsigned long base, size_t memsz) +{ + add_segment_phys_virt(info, buf, bufsz, base, memsz, 1); +} diff --git a/kexec/arch/m68k/kexec-m68k.h b/kexec/arch/m68k/kexec-m68k.h new file mode 100644 index 0000000..99482c4 --- /dev/null +++ b/kexec/arch/m68k/kexec-m68k.h @@ -0,0 +1,9 @@ +#ifndef KEXEC_M68K_H +#define KEXEC_M68K_H + +int elf_m68k_probe(const char *buf, off_t len); +int elf_m68k_load(int argc, char **argv, const char *buf, off_t len, + struct kexec_info *info); +void elf_m68k_usage(void); + +#endif /* KEXEC_M68K_H */ diff --git a/kexec/arch/mips/Makefile b/kexec/arch/mips/Makefile new file mode 100644 index 0000000..1fe7886 --- /dev/null +++ b/kexec/arch/mips/Makefile @@ -0,0 +1,29 @@ +# +# kexec mips (linux booting linux) +# +mips_KEXEC_SRCS = kexec/arch/mips/kexec-mips.c +mips_KEXEC_SRCS += kexec/arch/mips/kexec-elf-mips.c +mips_KEXEC_SRCS += kexec/arch/mips/kexec-elf-rel-mips.c +mips_KEXEC_SRCS += kexec/arch/mips/crashdump-mips.c + +mips_FS2DT = kexec/fs2dt.c +mips_FS2DT_INCLUDE = \ + -include $(srcdir)/kexec/arch/mips/crashdump-mips.h \ + -include $(srcdir)/kexec/arch/mips/kexec-mips.h + +mips_DT_OPS += kexec/dt-ops.c + +include $(srcdir)/kexec/libfdt/Makefile.libfdt + +libfdt_SRCS += $(LIBFDT_SRCS:%=kexec/libfdt/%) +mips_CPPFLAGS += -I$(srcdir)/kexec/libfdt +mips_KEXEC_SRCS += $(libfdt_SRCS) + +mips_ADD_BUFFER = +mips_ADD_SEGMENT = +mips_VIRT_TO_PHYS = + +dist += kexec/arch/mips/Makefile $(mips_KEXEC_SRCS) \ + kexec/arch/mips/kexec-mips.h \ + kexec/arch/mips/crashdump-mips.h \ + kexec/arch/mips/include/arch/options.h diff --git a/kexec/arch/mips/crashdump-mips.c b/kexec/arch/mips/crashdump-mips.c new file mode 100644 index 0000000..00f4335 --- /dev/null +++ b/kexec/arch/mips/crashdump-mips.c @@ -0,0 +1,408 @@ +/* + * kexec: Linux boots Linux + * + * 2005 (C) IBM Corporation. + * 2008 (C) MontaVista Software, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation (version 2 of the License). + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <limits.h> +#include <inttypes.h> +#include <elf.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include "../../kexec.h" +#include "../../kexec-elf.h" +#include "../../kexec-syscall.h" +#include "../../crashdump.h" +#include "kexec-mips.h" +#include "crashdump-mips.h" +#include "unused.h" + +/* Stores a sorted list of RAM memory ranges for which to create elf headers. + * A separate program header is created for backup region */ +static struct memory_range crash_memory_range[CRASH_MAX_MEMORY_RANGES]; + +/* Not used currently but required by generic fs2dt code */ +struct memory_ranges usablemem_rgns; + +/* Memory region reserved for storing panic kernel and other data. */ +static struct memory_range crash_reserved_mem; + +/* Read kernel physical load addr from the file returned by proc_iomem() + * (Kernel Code) and store in kexec_info */ +static int get_kernel_paddr(struct crash_elf_info *elf_info) +{ + uint64_t start; + + if (xen_present()) /* Kernel not entity mapped under Xen */ + return 0; + + if (parse_iomem_single("Kernel code\n", &start, NULL) == 0) { + elf_info->kern_paddr_start = start; + dbgprintf("kernel load physical addr start = 0x%" PRIu64 "\n", start); + return 0; + } + + fprintf(stderr, "Cannot determine kernel physical load addr\n"); + return -1; +} + +static int get_kernel_vaddr_and_size(struct crash_elf_info *elf_info, + unsigned long start_offset) +{ + uint64_t end; + + if (!elf_info->kern_paddr_start) + return -1; + + elf_info->kern_vaddr_start = elf_info->kern_paddr_start | + start_offset; + /* If "Kernel bss" exists, the kernel ends there, else fall + * through and say that it ends at "Kernel data" */ + if (parse_iomem_single("Kernel bss\n", NULL, &end) == 0 || + parse_iomem_single("Kernel data\n", NULL, &end) == 0) { + elf_info->kern_size = end - elf_info->kern_paddr_start; + dbgprintf("kernel_vaddr= 0x%llx paddr %llx\n", + elf_info->kern_vaddr_start, + elf_info->kern_paddr_start); + dbgprintf("kernel size = 0x%lx\n", elf_info->kern_size); + return 0; + } + fprintf(stderr, "Cannot determine kernel virtual load addr and size\n"); + return -1; +} + +/* Removes crash reserve region from list of memory chunks for whom elf program + * headers have to be created. Assuming crash reserve region to be a single + * continuous area fully contained inside one of the memory chunks */ +static int exclude_crash_reserve_region(int *nr_ranges) +{ + int i, j, tidx = -1; + unsigned long long cstart, cend; + struct memory_range temp_region = { + .start = 0, + .end = 0 + }; + + /* Crash reserved region. */ + cstart = crash_reserved_mem.start; + cend = crash_reserved_mem.end; + + for (i = 0; i < (*nr_ranges); i++) { + unsigned long long mstart, mend; + mstart = crash_memory_range[i].start; + mend = crash_memory_range[i].end; + if (cstart < mend && cend > mstart) { + if (cstart != mstart && cend != mend) { + /* Split memory region */ + crash_memory_range[i].end = cstart - 1; + temp_region.start = cend + 1; + temp_region.end = mend; + temp_region.type = RANGE_RAM; + tidx = i+1; + } else if (cstart != mstart) + crash_memory_range[i].end = cstart - 1; + else + crash_memory_range[i].start = cend + 1; + } + } + /* Insert split memory region, if any. */ + if (tidx >= 0) { + if (*nr_ranges == CRASH_MAX_MEMORY_RANGES) { + /* No space to insert another element. */ + fprintf(stderr, "Error: Number of crash memory ranges" + " excedeed the max limit\n"); + return -1; + } + for (j = (*nr_ranges - 1); j >= tidx; j--) + crash_memory_range[j+1] = crash_memory_range[j]; + crash_memory_range[tidx].start = temp_region.start; + crash_memory_range[tidx].end = temp_region.end; + crash_memory_range[tidx].type = temp_region.type; + (*nr_ranges)++; + } + return 0; +} +/* Reads the appropriate file and retrieves the SYSTEM RAM regions for whom to + * create Elf headers. Keeping it separate from get_memory_ranges() as + * requirements are different in the case of normal kexec and crashdumps. + * + * Normal kexec needs to look at all of available physical memory irrespective + * of the fact how much of it is being used by currently running kernel. + * Crashdumps need to have access to memory regions actually being used by + * running kernel. Expecting a different file/data structure than /proc/iomem + * to look into down the line. May be something like /proc/kernelmem or may + * be zone data structures exported from kernel. + */ +static int get_crash_memory_ranges(struct memory_range **range, int *ranges) +{ + const char iomem[] = "/proc/iomem"; + int memory_ranges = 0; + char line[MAX_LINE]; + FILE *fp; + unsigned long long start, end; + + fp = fopen(iomem, "r"); + if (!fp) { + fprintf(stderr, "Cannot open %s: %s\n", + iomem, strerror(errno)); + return -1; + } + /* Separate segment for backup region */ + crash_memory_range[0].start = BACKUP_SRC_START; + crash_memory_range[0].end = BACKUP_SRC_END; + crash_memory_range[0].type = RANGE_RAM; + memory_ranges++; + + while (fgets(line, sizeof(line), fp) != 0) { + char *str; + int type, consumed, count; + if (memory_ranges >= CRASH_MAX_MEMORY_RANGES) + break; + count = sscanf(line, "%llx-%llx : %n", + &start, &end, &consumed); + if (count != 2) + continue; + str = line + consumed; + + /* Only Dumping memory of type System RAM. */ + if (memcmp(str, "System RAM\n", 11) == 0) { + type = RANGE_RAM; + } else if (memcmp(str, "Crash kernel\n", 13) == 0) { + /* Reserved memory region. New kernel can + * use this region to boot into. */ + crash_reserved_mem.start = start; + crash_reserved_mem.end = end; + crash_reserved_mem.type = RANGE_RAM; + continue; + } else + continue; + + if (start == BACKUP_SRC_START && end >= (BACKUP_SRC_END + 1)) + start = BACKUP_SRC_END + 1; + + crash_memory_range[memory_ranges].start = start; + crash_memory_range[memory_ranges].end = end; + crash_memory_range[memory_ranges].type = type; + memory_ranges++; + + /* Segregate linearly mapped region. */ + if (MAXMEM && (MAXMEM - 1) >= start && (MAXMEM - 1) <= end) { + crash_memory_range[memory_ranges - 1].end = MAXMEM - 1; + + /* Add segregated region. */ + crash_memory_range[memory_ranges].start = MAXMEM; + crash_memory_range[memory_ranges].end = end; + crash_memory_range[memory_ranges].type = type; + memory_ranges++; + } + } + fclose(fp); + + if (exclude_crash_reserve_region(&memory_ranges) < 0) + return -1; + + *range = crash_memory_range; + *ranges = memory_ranges; + return 0; +} + +/* Adds the appropriate mem= options to command line, indicating the + * memory region the new kernel can use to boot into. */ +static int cmdline_add_mem(char *cmdline, unsigned long addr, + unsigned long size) +{ + int cmdlen, len; + char str[50], *ptr; + + addr = addr/1024; + size = size/1024; + ptr = str; + strcpy(str, " mem="); + ptr += strlen(str); + ultoa(size, ptr); + strcat(str, "K@"); + ptr = str + strlen(str); + ultoa(addr, ptr); + strcat(str, "K"); + len = strlen(str); + cmdlen = strlen(cmdline) + len; + if (cmdlen > (COMMAND_LINE_SIZE - 1)) + die("Command line overflow\n"); + strcat(cmdline, str); + + return 0; +} + +/* Adds the elfcorehdr= command line parameter to command line. */ +static int cmdline_add_elfcorehdr(char *cmdline, unsigned long addr) +{ + int cmdlen, len, align = 1024; + char str[30], *ptr; + + /* Passing in elfcorehdr=xxxK format. Saves space required in cmdline. + * Ensure 1K alignment*/ + if (addr%align) + return -1; + addr = addr/align; + ptr = str; + strcpy(str, " elfcorehdr="); + ptr += strlen(str); + ultoa(addr, ptr); + strcat(str, "K"); + len = strlen(str); + cmdlen = strlen(cmdline) + len; + if (cmdlen > (COMMAND_LINE_SIZE - 1)) + die("Command line overflow\n"); + strcat(cmdline, str); + return 0; +} + +#if __BYTE_ORDER == __LITTLE_ENDIAN +# define ELFDATALOCAL ELFDATA2LSB +#elif __BYTE_ORDER == __BIG_ENDIAN +# define ELFDATALOCAL ELFDATA2MSB +#else +# error Unknown byte order +#endif + +static struct crash_elf_info elf_info64 = { + class: ELFCLASS64, + data : ELFDATALOCAL, + machine : EM_MIPS, + page_offset : PAGE_OFFSET, + lowmem_limit : 0, /* 0 == no limit */ +}; + +static struct crash_elf_info elf_info32 = { + class: ELFCLASS32, + data : ELFDATALOCAL, + machine : EM_MIPS, + page_offset : PAGE_OFFSET, + lowmem_limit : MAXMEM, +}; + +static int patch_elf_info(void) +{ + const char cpuinfo[] = "/proc/cpuinfo"; + char line[MAX_LINE]; + FILE *fp; + + fp = fopen(cpuinfo, "r"); + if (!fp) { + fprintf(stderr, "Cannot open %s: %s\n", + cpuinfo, strerror(errno)); + return -1; + } + while (fgets(line, sizeof(line), fp) != 0) { + if (strncmp(line, "cpu model", 9) == 0) { + /* OCTEON uses a different page_offset. */ + if (strstr(line, "Octeon")) + elf_info64.page_offset = OCTEON_PAGE_OFFSET; + /* LOONGSON uses a different page_offset. */ + else if (strstr(line, "Loongson")) + elf_info64.page_offset = LOONGSON_PAGE_OFFSET; + break; + } + } + fclose(fp); + return 0; +} + +/* Loads additional segments in case of a panic kernel is being loaded. + * One segment for backup region, another segment for storing elf headers + * for crash memory image. + */ +int load_crashdump_segments(struct kexec_info *info, char* mod_cmdline, + unsigned long UNUSED(max_addr), + unsigned long UNUSED(min_base)) +{ + void *tmp; + unsigned long sz, elfcorehdr; + int nr_ranges, align = 1024; + struct memory_range *mem_range; + crash_create_elf_headers_func crash_create = crash_create_elf32_headers; + struct crash_elf_info *elf_info = &elf_info32; + unsigned long start_offset = 0x80000000UL; + + if (patch_elf_info()) + return -1; + + if (arch_options.core_header_type == CORE_TYPE_ELF64) { + elf_info = &elf_info64; + crash_create = crash_create_elf64_headers; + start_offset = (unsigned long)0xffffffff80000000UL; + } + + if (get_kernel_paddr(elf_info)) + return -1; + + if (get_kernel_vaddr_and_size(elf_info, start_offset)) + return -1; + + if (get_crash_memory_ranges(&mem_range, &nr_ranges) < 0) + return -1; + + info->backup_src_start = BACKUP_SRC_START; + info->backup_src_size = BACKUP_SRC_SIZE; + /* Create a backup region segment to store backup data*/ + sz = _ALIGN(BACKUP_SRC_SIZE, align); + tmp = xmalloc(sz); + memset(tmp, 0, sz); + info->backup_start = add_buffer(info, tmp, sz, sz, align, + crash_reserved_mem.start, + crash_reserved_mem.end, -1); + + if (crash_create(info, elf_info, crash_memory_range, nr_ranges, + &tmp, &sz, ELF_CORE_HEADER_ALIGN) < 0) { + free(tmp); + return -1; + } + + elfcorehdr = add_buffer(info, tmp, sz, sz, align, + crash_reserved_mem.start, + crash_reserved_mem.end, -1); + + /* + * backup segment is after elfcorehdr, so use elfcorehdr as top of + * kernel's available memory + */ + cmdline_add_mem(mod_cmdline, crash_reserved_mem.start, + crash_reserved_mem.end - crash_reserved_mem.start + 1); + cmdline_add_elfcorehdr(mod_cmdline, elfcorehdr); + + dbgprintf("CRASH MEMORY RANGES:\n"); + dbgprintf("%016Lx-%016Lx\n", crash_reserved_mem.start, + crash_reserved_mem.end); + return 0; +} + +int is_crashkernel_mem_reserved(void) +{ + uint64_t start, end; + + return parse_iomem_single("Crash kernel\n", &start, &end) == 0 ? + (start != end) : 0; +} + +int get_crash_kernel_load_range(uint64_t *start, uint64_t *end) +{ + return parse_iomem_single("Crash kernel\n", start, end); +} diff --git a/kexec/arch/mips/crashdump-mips.h b/kexec/arch/mips/crashdump-mips.h new file mode 100644 index 0000000..55c9925 --- /dev/null +++ b/kexec/arch/mips/crashdump-mips.h @@ -0,0 +1,30 @@ +#ifndef CRASHDUMP_MIPS_H +#define CRASHDUMP_MIPS_H + +struct kexec_info; +int load_crashdump_segments(struct kexec_info *info, char *mod_cmdline, + unsigned long max_addr, unsigned long min_base); +#ifdef __mips64 +#define PAGE_OFFSET 0xa800000000000000ULL +#define MAXMEM 0 +#else +#define PAGE_OFFSET 0x80000000 +#define MAXMEM 0x80000000 +#endif +#define __pa(x) ((unsigned long)(X) & 0x7fffffff) + +#define LOONGSON_PAGE_OFFSET 0xffffffff80000000ULL +#define OCTEON_PAGE_OFFSET 0x8000000000000000ULL + +#define CRASH_MAX_MEMMAP_NR (KEXEC_MAX_SEGMENTS + 1) +#define CRASH_MAX_MEMORY_RANGES (MAX_MEMORY_RANGES + 2) + +#define COMMAND_LINE_SIZE 512 + +/* Backup Region, First 1M of System RAM. */ +#define BACKUP_SRC_START 0x00000000 +#define BACKUP_SRC_END 0x000fffff +#define BACKUP_SRC_SIZE (BACKUP_SRC_END - BACKUP_SRC_START + 1) + +extern struct arch_options_t arch_options; +#endif /* CRASHDUMP_MIPS_H */ diff --git a/kexec/arch/mips/include/arch/options.h b/kexec/arch/mips/include/arch/options.h new file mode 100644 index 0000000..ba2f346 --- /dev/null +++ b/kexec/arch/mips/include/arch/options.h @@ -0,0 +1,43 @@ +#ifndef KEXEC_ARCH_MIPS_OPTIONS_H +#define KEXEC_ARCH_MIPS_OPTIONS_H + +#define OPT_ARCH_MAX (OPT_MAX+0) +#define OPT_APPEND (OPT_ARCH_MAX+0) +#define OPT_DTB (OPT_ARCH_MAX+1) +#define OPT_RAMDISK (OPT_ARCH_MAX+2) +#define OPT_REUSE_CMDLINE (OPT_ARCH_MAX+3) + +/* Options relevant to the architecture (excluding loader-specific ones), + * in this case none: + */ +#define KEXEC_ARCH_OPTIONS \ + KEXEC_OPTIONS \ + {"command-line", 1, 0, OPT_APPEND}, \ + {"append", 1, 0, OPT_APPEND}, \ + {"dtb", 1, 0, OPT_DTB }, \ + {"initrd", 1, 0, OPT_RAMDISK }, \ + { "reuse-cmdline", 0, NULL, OPT_REUSE_CMDLINE }, + + +#define KEXEC_ARCH_OPT_STR KEXEC_OPT_STR "" + +/* The following two #defines list ALL of the options added by all of the + * architecture's loaders. + * o main() uses this complete list to scan for its options, ignoring + * arch-specific/loader-specific ones. + * o Then, arch_process_options() uses this complete list to scan for its + * options, ignoring general/loader-specific ones. + * o Then, the file_type[n].load re-scans for options, using + * KEXEC_ARCH_OPTIONS plus its loader-specific options subset. + * Any unrecognised options cause an error here. + * + * This is done so that main()'s/arch_process_options()'s getopt_long() calls + * don't choose a kernel filename from random arguments to options they don't + * recognise -- as they now recognise (if not act upon) all possible options. + */ +#define KEXEC_ALL_OPTIONS \ + KEXEC_ARCH_OPTIONS + +#define KEXEC_ALL_OPT_STR KEXEC_ARCH_OPT_STR + +#endif /* KEXEC_ARCH_MIPS_OPTIONS_H */ diff --git a/kexec/arch/mips/kexec-elf-mips.c b/kexec/arch/mips/kexec-elf-mips.c new file mode 100644 index 0000000..230d806 --- /dev/null +++ b/kexec/arch/mips/kexec-elf-mips.c @@ -0,0 +1,242 @@ +/* + * kexec-elf-mips.c - kexec Elf loader for mips + * Copyright (C) 2007 Francesco Chiechi, Alessandro Rubini + * Copyright (C) 2007 Tvblob s.r.l. + * + * derived from ../ppc/kexec-elf-ppc.c + * Copyright (C) 2004 Albert Herranz + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. +*/ + +#define _GNU_SOURCE +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <getopt.h> +#include <elf.h> +#include <boot/elf_boot.h> +#include <ip_checksum.h> +#include "../../kexec.h" +#include "../../kexec-elf.h" +#include "../../kexec-syscall.h" +#include "kexec-mips.h" +#include "crashdump-mips.h" +#include <arch/options.h> +#include "../../fs2dt.h" +#include "../../dt-ops.h" + +static const int probe_debug = 0; + +#define BOOTLOADER "kexec" +#define UPSZ(X) _ALIGN_UP(sizeof(X), 4) + +#define CMDLINE_PREFIX "kexec " +static char cmdline_buf[COMMAND_LINE_SIZE] = CMDLINE_PREFIX; + +/* Adds initrd parameters to command line. */ +static int cmdline_add_initrd(char *cmdline, unsigned long addr, char *new_para) +{ + int cmdlen, len; + char str[30], *ptr; + + ptr = str; + strcpy(str, new_para); + ptr += strlen(str); + ultoa(addr, ptr); + len = strlen(str); + cmdlen = strlen(cmdline) + len; + if (cmdlen > (COMMAND_LINE_SIZE - 1)) + die("Command line overflow\n"); + strcat(cmdline, str); + + return 0; +} + +/* add initrd to cmdline to compatible with previous platforms. */ +static int patch_initrd_info(char *cmdline, unsigned long base, + unsigned long size) +{ + const char cpuinfo[] = "/proc/cpuinfo"; + char line[MAX_LINE]; + FILE *fp; + unsigned long page_offset = PAGE_OFFSET; + + fp = fopen(cpuinfo, "r"); + if (!fp) { + fprintf(stderr, "Cannot open %s: %s\n", + cpuinfo, strerror(errno)); + return -1; + } + while (fgets(line, sizeof(line), fp) != 0) { + if (strncmp(line, "cpu model", 9) == 0) { + if (strstr(line, "Loongson")) { + /* LOONGSON64 uses a different page_offset. */ + if (arch_options.core_header_type == + CORE_TYPE_ELF64) + page_offset = LOONGSON_PAGE_OFFSET; + cmdline_add_initrd(cmdline, + page_offset + base, " rd_start="); + cmdline_add_initrd(cmdline, size, " rd_size="); + break; + } + } + } + fclose(fp); + return 0; +} + +int elf_mips_probe(const char *buf, off_t len) +{ + struct mem_ehdr ehdr; + int result; + result = build_elf_exec_info(buf, len, &ehdr, 0); + if (result < 0) { + goto out; + } + + /* Verify the architecuture specific bits */ + if (ehdr.e_machine != EM_MIPS) { + /* for a different architecture */ + if (probe_debug) { + fprintf(stderr, "Not for this architecture.\n"); + } + result = -1; + goto out; + } + result = 0; + out: + free_elf_info(&ehdr); + return result; +} + +void elf_mips_usage(void) +{ +} + +int elf_mips_load(int argc, char **argv, const char *buf, off_t len, + struct kexec_info *info) +{ + struct mem_ehdr ehdr; + int command_line_len = 0; + char *crash_cmdline; + int result; + unsigned long cmdline_addr; + size_t i; + off_t dtb_length; + char *dtb_buf; + char *initrd_buf = NULL; + unsigned long long kernel_addr = 0, kernel_size = 0; + unsigned long pagesize = getpagesize(); + + /* Need to append some command line parameters internally in case of + * taking crash dumps. + */ + if (info->kexec_flags & KEXEC_ON_CRASH) { + crash_cmdline = xmalloc(COMMAND_LINE_SIZE); + memset((void *)crash_cmdline, 0, COMMAND_LINE_SIZE); + } else + crash_cmdline = NULL; + + result = build_elf_exec_info(buf, len, &ehdr, 0); + if (result < 0) + die("ELF exec parse failed\n"); + + /* Read in the PT_LOAD segments and remove CKSEG0 mask from address*/ + for (i = 0; i < ehdr.e_phnum; i++) { + struct mem_phdr *phdr; + phdr = &ehdr.e_phdr[i]; + if (phdr->p_type == PT_LOAD) { + phdr->p_paddr = virt_to_phys(phdr->p_paddr); + kernel_addr = phdr->p_paddr; + kernel_size = phdr->p_memsz; + } + } + + /* Load the Elf data */ + result = elf_exec_load(&ehdr, info); + if (result < 0) + die("ELF exec load failed\n"); + + info->entry = (void *)virt_to_phys(ehdr.e_entry); + + if (arch_options.command_line) + command_line_len = strlen(arch_options.command_line) + 1; + + if (info->kexec_flags & KEXEC_ON_CRASH) { + result = load_crashdump_segments(info, crash_cmdline, + 0, 0); + if (result < 0) { + free(crash_cmdline); + return -1; + } + } + + if (arch_options.command_line) + strncat(cmdline_buf, arch_options.command_line, command_line_len); + if (crash_cmdline) + { + strncat(cmdline_buf, crash_cmdline, + sizeof(crash_cmdline) - + strlen(crash_cmdline) - 1); + free(crash_cmdline); + } + + if (info->kexec_flags & KEXEC_ON_CRASH) + /* In case of crashdump segment[0] is kernel. + * Put cmdline just after it. */ + cmdline_addr = (unsigned long)info->segment[0].mem + + info->segment[0].memsz; + else + cmdline_addr = 0; + + /* MIPS systems that have been converted to use device tree + * passed through UHI will use commandline in the DTB and + * the DTB passed as a separate buffer. Note that + * CMDLINE_PREFIX is skipped here intentionally, as it is + * used only in the legacy method */ + + if (arch_options.dtb_file) { + dtb_buf = slurp_file(arch_options.dtb_file, &dtb_length); + } else { + create_flatten_tree(&dtb_buf, &dtb_length, cmdline_buf + strlen(CMDLINE_PREFIX)); + } + + if (arch_options.initrd_file) { + initrd_buf = slurp_file(arch_options.initrd_file, &initrd_size); + + /* Create initrd entries in dtb - although at this time + * they would not point to the correct location */ + dtb_set_initrd(&dtb_buf, &dtb_length, (off_t)initrd_buf, (off_t)initrd_buf + initrd_size); + + initrd_base = add_buffer(info, initrd_buf, initrd_size, + initrd_size, sizeof(void *), + _ALIGN_UP(kernel_addr + kernel_size + dtb_length, + pagesize), 0x0fffffff, 1); + + /* Now that the buffer for initrd is prepared, update the dtb + * with an appropriate location */ + dtb_set_initrd(&dtb_buf, &dtb_length, initrd_base, initrd_base + initrd_size); + + /* Add the initrd parameters to cmdline */ + patch_initrd_info(cmdline_buf, initrd_base, initrd_size); + } + /* This is a legacy method for commandline passing used + * currently by Octeon CPUs only */ + add_buffer(info, cmdline_buf, sizeof(cmdline_buf), + sizeof(cmdline_buf), sizeof(void *), + cmdline_addr, 0x0fffffff, 1); + + add_buffer(info, dtb_buf, dtb_length, dtb_length, 0, + _ALIGN_UP(kernel_addr + kernel_size, pagesize), + 0x0fffffff, 1); + + return 0; +} + diff --git a/kexec/arch/mips/kexec-elf-rel-mips.c b/kexec/arch/mips/kexec-elf-rel-mips.c new file mode 100644 index 0000000..5bc22d5 --- /dev/null +++ b/kexec/arch/mips/kexec-elf-rel-mips.c @@ -0,0 +1,46 @@ +/* + * kexec-elf-rel-mips.c - kexec Elf relocation routines + * Copyright (C) 2007 Francesco Chiechi, Alessandro Rubini + * Copyright (C) 2007 Tvblob s.r.l. + * + * derived from ../ppc/kexec-elf-rel-ppc.c + * Copyright (C) 2004 Albert Herranz + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. +*/ + +#include <stdio.h> +#include <elf.h> +#include "../../kexec.h" +#include "../../kexec-elf.h" + +int machine_verify_elf_rel(struct mem_ehdr *ehdr) +{ + if (ehdr->ei_data != ELFDATA2MSB) { + return 0; + } + if (ehdr->ei_class != ELFCLASS32) { + return 0; + } + if (ehdr->e_machine != EM_MIPS) { + return 0; + } + return 1; +} + +void machine_apply_elf_rel(struct mem_ehdr *UNUSED(ehdr), + struct mem_sym *UNUSED(sym), + unsigned long r_type, + void *UNUSED(location), + unsigned long UNUSED(address), + unsigned long UNUSED(value)) +{ + switch(r_type) { + + default: + die("Unknown rela relocation: %lu\n", r_type); + break; + } + return; +} diff --git a/kexec/arch/mips/kexec-mips.c b/kexec/arch/mips/kexec-mips.c new file mode 100644 index 0000000..d8cbea8 --- /dev/null +++ b/kexec/arch/mips/kexec-mips.c @@ -0,0 +1,191 @@ +/* + * kexec-mips.c - kexec for mips + * Copyright (C) 2007 Francesco Chiechi, Alessandro Rubini + * Copyright (C) 2007 Tvblob s.r.l. + * + * derived from ../ppc/kexec-ppc.c + * Copyright (C) 2004, 2005 Albert Herranz + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#include <stddef.h> +#include <stdio.h> +#include <errno.h> +#include <stdint.h> +#include <string.h> +#include <getopt.h> +#include "../../kexec.h" +#include "../../kexec-syscall.h" +#include "kexec-mips.h" +#include <arch/options.h> + +/* Currently not used but required by top-level fs2dt code */ +off_t initrd_base = 0; +off_t initrd_size = 0; + +static struct memory_range memory_range[MAX_MEMORY_RANGES]; + +/* Return a sorted list of memory ranges. */ +int get_memory_ranges(struct memory_range **range, int *ranges, + unsigned long UNUSED(kexec_flags)) +{ + int memory_ranges = 0; + + const char iomem[] = "/proc/iomem"; + char line[MAX_LINE]; + FILE *fp; + unsigned long long start, end; + char *str; + int type, consumed, count; + + fp = fopen(iomem, "r"); + if (!fp) { + fprintf(stderr, "Cannot open %s: %s\n", iomem, strerror(errno)); + return -1; + } + while (fgets(line, sizeof(line), fp) != 0) { + if (memory_ranges >= MAX_MEMORY_RANGES) + break; + count = sscanf(line, "%llx-%llx : %n", &start, &end, &consumed); + if (count != 2) + continue; + str = line + consumed; + end = end + 1; + if (memcmp(str, "System RAM\n", 11) == 0) { + type = RANGE_RAM; + } else if (memcmp(str, "reserved\n", 9) == 0) { + type = RANGE_RESERVED; + } else { + continue; + } + if (memory_ranges > 0 && + memory_range[memory_ranges - 1].end == start && + memory_range[memory_ranges - 1].type == type) { + memory_range[memory_ranges - 1].end = end; + } else { + memory_range[memory_ranges].start = start; + memory_range[memory_ranges].end = end; + memory_range[memory_ranges].type = type; + memory_ranges++; + } + } + fclose(fp); + *range = memory_range; + *ranges = memory_ranges; + return 0; +} + +struct file_type file_type[] = { + {"elf-mips", elf_mips_probe, elf_mips_load, elf_mips_usage}, +}; +int file_types = sizeof(file_type) / sizeof(file_type[0]); + +void arch_usage(void) +{ + printf( + " --command-line=STRING Set the kernel command line to STRING.\n" + " --append=STRING Set the kernel command line to STRING.\n" + " --dtb=FILE Use FILE as the device tree blob.\n" + " --initrd=FILE Use FILE as initial ramdisk.\n" + " --reuse-cmdline Use kernel command line from running system.\n" + ); +} + +struct arch_options_t arch_options = { +#ifdef __mips64 + .core_header_type = CORE_TYPE_ELF64, +#else + .core_header_type = CORE_TYPE_ELF32, +#endif +}; + +int arch_process_options(int argc, char **argv) +{ + static const struct option options[] = { + KEXEC_ARCH_OPTIONS + { 0 }, + }; + static const char short_options[] = KEXEC_ARCH_OPT_STR; + int opt; + char *cmdline = NULL; + const char *append = NULL; + + while ((opt = getopt_long(argc, argv, short_options, + options, 0)) != -1) { + switch (opt) { + case OPT_APPEND: + append = optarg; + break; + case OPT_REUSE_CMDLINE: + cmdline = get_command_line(); + break; + case OPT_DTB: + arch_options.dtb_file = optarg; + break; + case OPT_RAMDISK: + arch_options.initrd_file = optarg; + break; + default: + break; + } + } + + arch_options.command_line = concat_cmdline(cmdline, append); + + dbgprintf("%s:%d: command_line: %s\n", __func__, __LINE__, + arch_options.command_line); + dbgprintf("%s:%d: initrd: %s\n", __func__, __LINE__, + arch_options.initrd_file); + dbgprintf("%s:%d: dtb: %s\n", __func__, __LINE__, + arch_options.dtb_file); + + return 0; +} + +const struct arch_map_entry arches[] = { + /* For compatibility with older patches + * use KEXEC_ARCH_DEFAULT instead of KEXEC_ARCH_MIPS here. + */ + { "mips", KEXEC_ARCH_MIPS }, + { "mips64", KEXEC_ARCH_MIPS }, + { NULL, 0 }, +}; + +int arch_compat_trampoline(struct kexec_info *UNUSED(info)) +{ + + return 0; +} + +void arch_update_purgatory(struct kexec_info *UNUSED(info)) +{ +} + +unsigned long virt_to_phys(unsigned long addr) +{ + return addr & 0x7fffffff; +} + +/* + * add_segment() should convert base to a physical address on mips, + * while the default is just to work with base as is */ +void add_segment(struct kexec_info *info, const void *buf, size_t bufsz, + unsigned long base, size_t memsz) +{ + add_segment_phys_virt(info, buf, bufsz, virt_to_phys(base), memsz, 1); +} + +/* + * add_buffer() should convert base to a physical address on mips, + * while the default is just to work with base as is */ +unsigned long add_buffer(struct kexec_info *info, const void *buf, + unsigned long bufsz, unsigned long memsz, + unsigned long buf_align, unsigned long buf_min, + unsigned long buf_max, int buf_end) +{ + return add_buffer_phys_virt(info, buf, bufsz, memsz, buf_align, + buf_min, buf_max, buf_end, 1); +} + diff --git a/kexec/arch/mips/kexec-mips.h b/kexec/arch/mips/kexec-mips.h new file mode 100644 index 0000000..222c815 --- /dev/null +++ b/kexec/arch/mips/kexec-mips.h @@ -0,0 +1,30 @@ +#ifndef KEXEC_MIPS_H +#define KEXEC_MIPS_H + +#include <sys/types.h> + +#define BOOT_BLOCK_VERSION 17 +#define BOOT_BLOCK_LAST_COMP_VERSION 16 + +#define MAX_MEMORY_RANGES 64 +#define MAX_LINE 160 + +#define CORE_TYPE_ELF32 1 +#define CORE_TYPE_ELF64 2 + +int elf_mips_probe(const char *buf, off_t len); +int elf_mips_load(int argc, char **argv, const char *buf, off_t len, + struct kexec_info *info); +void elf_mips_usage(void); + +struct arch_options_t { + char *command_line; + char *dtb_file; + char *initrd_file; + int core_header_type; +}; + +extern struct memory_ranges usablemem_rgns; +extern off_t initrd_base, initrd_size; + +#endif /* KEXEC_MIPS_H */ diff --git a/kexec/arch/ppc/Makefile b/kexec/arch/ppc/Makefile new file mode 100644 index 0000000..71871f1 --- /dev/null +++ b/kexec/arch/ppc/Makefile @@ -0,0 +1,34 @@ +# +# kexec ppc (linux booting linux) +# +include $(srcdir)/kexec/libfdt/Makefile.libfdt + +ppc_KEXEC_SRCS = kexec/arch/ppc/kexec-ppc.c +ppc_KEXEC_SRCS += kexec/arch/ppc/kexec-elf-ppc.c +ppc_KEXEC_SRCS += kexec/arch/ppc/kexec-elf-rel-ppc.c +ppc_KEXEC_SRCS += kexec/arch/ppc/kexec-dol-ppc.c +ppc_KEXEC_SRCS += kexec/arch/ppc/kexec-uImage-ppc.c +ppc_KEXEC_SRCS += kexec/arch/ppc/ppc-setup-simple.S +ppc_KEXEC_SRCS += kexec/arch/ppc/ppc-setup-dol.S +ppc_KEXEC_SRCS += kexec/arch/ppc/fixup_dtb.c +ppc_KEXEC_SRCS += kexec/arch/ppc/fs2dt.c +ppc_KEXEC_SRCS += kexec/arch/ppc/crashdump-powerpc.c + +ppc_UIMAGE = kexec/kexec-uImage.c + +ppc_libfdt_SRCS = kexec/arch/ppc/libfdt-wrapper.c +libfdt_SRCS += $(LIBFDT_SRCS:%=kexec/libfdt/%) +ppc_ARCH_REUSE_INITRD = + +ppc_CPPFLAGS = -I$(srcdir)/kexec/libfdt + +ppc_KEXEC_SRCS += $(libfdt_SRCS) $(ppc_libfdt_SRCS) + +ppc_ASFLAGS = -Wa,--noexecstack + +dist += kexec/arch/ppc/Makefile $(ppc_KEXEC_SRCS) \ + kexec/arch/ppc/crashdump-powerpc.h kexec/arch/ppc/fixup_dtb.h \ + kexec/arch/ppc/kexec-ppc.h kexec/arch/ppc/ops.h \ + kexec/arch/ppc/ppc_asm.h \ + kexec/arch/ppc/include/page.h kexec/arch/ppc/include/types.h \ + kexec/arch/ppc/include/arch/options.h diff --git a/kexec/arch/ppc/crashdump-powerpc.c b/kexec/arch/ppc/crashdump-powerpc.c new file mode 100644 index 0000000..15e8531 --- /dev/null +++ b/kexec/arch/ppc/crashdump-powerpc.c @@ -0,0 +1,433 @@ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <limits.h> +#include <elf.h> +#include <dirent.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include "../../kexec.h" +#include "../../kexec-elf.h" +#include "../../kexec-syscall.h" +#include "../../crashdump.h" +#include "kexec-ppc.h" +#include "crashdump-powerpc.h" + +#define DEVTREE_CRASHKERNEL_BASE "/proc/device-tree/chosen/linux,crashkernel-base" +#define DEVTREE_CRASHKERNEL_SIZE "/proc/device-tree/chosen/linux,crashkernel-size" + +#ifdef CONFIG_PPC64 +static struct crash_elf_info elf_info64 = { +class: ELFCLASS64, +data: ELFDATA2MSB, +machine: EM_PPC64, +page_offset: PAGE_OFFSET, +lowmem_limit: MAXMEM, +}; +#endif +static struct crash_elf_info elf_info32 = { +class: ELFCLASS32, +data: ELFDATA2MSB, +#ifdef CONFIG_PPC64 +machine: EM_PPC64, +#else +machine: EM_PPC, +#endif +page_offset: PAGE_OFFSET, +lowmem_limit: MAXMEM, +}; + +/* Stores a sorted list of RAM memory ranges for which to create elf headers. + * A separate program header is created for backup region + */ +static struct memory_range *crash_memory_range; +static int crash_nr_memory_ranges; + +/* Define a variable to replace the CRASH_MAX_MEMORY_RANGES macro */ +static int crash_max_memory_ranges; + +/* + * Used to save various memory ranges/regions needed for the captured + * kernel to boot. (lime memmap= option in other archs) + */ +mem_rgns_t usablemem_rgns = {0, NULL}; + +/* Append a segment to crash_memory_range, splitting it into two if + * it contains both lowmem and highmem */ +static void add_crash_memory_range(unsigned long long start, + unsigned long long end) +{ +#ifndef CONFIG_PPC64 + if (start < elf_info32.lowmem_limit && end > elf_info32.lowmem_limit) { + add_crash_memory_range(start, elf_info32.lowmem_limit); + add_crash_memory_range(elf_info32.lowmem_limit, end); + return; + } +#endif + + if (crash_nr_memory_ranges < crash_max_memory_ranges) { + crash_memory_range[crash_nr_memory_ranges].start = start; + crash_memory_range[crash_nr_memory_ranges].end = end; + crash_memory_range[crash_nr_memory_ranges].type = RANGE_RAM; + } + + crash_nr_memory_ranges++; +} + + +/* Reads the appropriate file and retrieves the SYSTEM RAM regions for whom to + * create Elf headers. Keeping it separate from get_memory_ranges() as + * requirements are different in the case of normal kexec and crashdumps. + * + * Normal kexec needs to look at all of available physical memory irrespective + * of the fact how much of it is being used by currently running kernel. + * Crashdumps need to have access to memory regions actually being used by + * running kernel. Expecting a different file/data structure than /proc/iomem + * to look into down the line. May be something like /proc/kernelmem or may + * be zone data structures exported from kernel. + */ +static int get_crash_memory_ranges(struct memory_range **range, int *ranges) +{ + + char device_tree[256] = "/proc/device-tree/"; + char fname[256]; + DIR *dir, *dmem; + int fd; + struct dirent *dentry, *mentry; + int n, crash_rng_len = 0; + unsigned long long start, end, cstart, cend; + + crash_max_memory_ranges = max_memory_ranges + 6; + crash_rng_len = sizeof(struct memory_range) * crash_max_memory_ranges; + + crash_memory_range = (struct memory_range *) malloc(crash_rng_len); + if (!crash_memory_range) { + fprintf(stderr, "Allocation for crash memory range failed\n"); + return -1; + } + memset(crash_memory_range, 0, crash_rng_len); + crash_nr_memory_ranges = 0; + +#ifndef CONFIG_BOOKE + /* create a separate program header for the backup region */ + add_crash_memory_range(BACKUP_SRC_START, BACKUP_SRC_END + 1); +#endif + + dir = opendir(device_tree); + if (!dir) { + perror(device_tree); + goto err; + } + while ((dentry = readdir(dir)) != NULL) { + if (strncmp(dentry->d_name, "memory@", 7) + && strcmp(dentry->d_name, "memory")) + continue; + strcpy(fname, device_tree); + strcat(fname, dentry->d_name); + dmem = opendir(fname); + if (!dmem) { + perror(fname); + closedir(dir); + goto err; + } + while ((mentry = readdir(dmem)) != NULL) { + if (strcmp(mentry->d_name, "reg")) + continue; + strcat(fname, "/reg"); + fd = open(fname, O_RDONLY); + if (fd < 0) { + perror(fname); + closedir(dmem); + closedir(dir); + goto err; + } + n = read_memory_region_limits(fd, &start, &end); + /* We are done with fd, close it. */ + close(fd); + if (n != 0) { + closedir(dmem); + closedir(dir); + goto err; + } +#ifndef CONFIG_BOOKE + if (start == 0 && end >= (BACKUP_SRC_END + 1)) + start = BACKUP_SRC_END + 1; +#endif + + /* + * Exclude the region that lies within crashkernel. + * If memory limit is set then exclude memory region + * above it. + */ + + if (memory_limit) { + if (start >= memory_limit) + continue; + if (end > memory_limit) + end = memory_limit; + } + + /* + * Exclure region used by crash kernel + */ + cstart = crash_base; + cend = crash_base + crash_size; + + if (cstart >= end || cend <= start) + add_crash_memory_range(start, end); + else { + if (start < cstart) + add_crash_memory_range(start, cstart); + if (cend < end) + add_crash_memory_range(cend, end); + } + } + closedir(dmem); + } + closedir(dir); + + /* + * If RTAS region is overlapped with crashkernel, need to create ELF + * Program header for the overlapped memory. + */ + if (crash_base < rtas_base + rtas_size && + rtas_base < crash_base + crash_size) { + cstart = rtas_base; + cend = rtas_base + rtas_size; + if (cstart < crash_base) + cstart = crash_base; + if (cend > crash_base + crash_size) + cend = crash_base + crash_size; + add_crash_memory_range(cstart, cend); + } + + if (crash_nr_memory_ranges >= crash_max_memory_ranges) { + fprintf(stderr, + "Error: Number of crash memory ranges" + " excedeed the max limit\n"); + goto err; + } + + *range = crash_memory_range; + *ranges = crash_nr_memory_ranges; + + int j; + dbgprintf("CRASH MEMORY RANGES\n"); + for (j = 0; j < *ranges; j++) { + start = crash_memory_range[j].start; + end = crash_memory_range[j].end; + dbgprintf("%016Lx-%016Lx\n", start, end); + } + + return 0; + +err: + if (crash_memory_range) + free(crash_memory_range); + return -1; +} + +/* Converts unsigned long to ascii string. */ +static void ulltoa(unsigned long long i, char *str) +{ + int j = 0, k; + char tmp; + + do { + str[j++] = i % 10 + '0'; + } while ((i /= 10) > 0); + str[j] = '\0'; + + /* Reverse the string. */ + for (j = 0, k = strlen(str) - 1; j < k; j++, k--) { + tmp = str[k]; + str[k] = str[j]; + str[j] = tmp; + } +} + +/* Append str to cmdline */ +static void add_cmdline(char *cmdline, char *str) +{ + int cmdline_size; + int cmdlen = strlen(cmdline) + strlen(str); + + cmdline_size = COMMAND_LINE_SIZE; + if (cmdlen > (cmdline_size - 1)) + die("Command line overflow\n"); + strcat(cmdline, str); +} + +static int add_cmdline_param(char *cmdline, unsigned long long addr, + char *cmdstr, char *byte) +{ + int align = 1024; + char str[COMMAND_LINE_SIZE], *ptr; + + /* Passing in =xxxK / =xxxM format. Saves space required in cmdline.*/ + switch (byte[0]) { + case 'K': + if (addr%align) + return -1; + addr = addr/align; + break; + case 'M': + addr = addr/(align *align); + break; + } + ptr = str; + strcpy(str, cmdstr); + ptr += strlen(str); + ulltoa(addr, ptr); + strcat(str, byte); + + add_cmdline(cmdline, str); + + dbgprintf("Command line after adding elfcorehdr: %s\n", cmdline); + + return 0; +} + +/* Loads additional segments in case of a panic kernel is being loaded. + * One segment for backup region, another segment for storing elf headers + * for crash memory image. + */ +int load_crashdump_segments(struct kexec_info *info, char *mod_cmdline, + unsigned long max_addr, unsigned long min_base) +{ + void *tmp; + unsigned long sz, elfcorehdr; + int nr_ranges, align = 1024, i; + unsigned long long end; + struct memory_range *mem_range; + + if (get_crash_memory_ranges(&mem_range, &nr_ranges) < 0) + return -1; + + info->backup_src_start = BACKUP_SRC_START; + info->backup_src_size = BACKUP_SRC_SIZE; +#ifndef CONFIG_BOOKE + /* Create a backup region segment to store backup data*/ + sz = _ALIGN(BACKUP_SRC_SIZE, align); + tmp = xmalloc(sz); + memset(tmp, 0, sz); + info->backup_start = add_buffer(info, tmp, sz, sz, align, + 0, max_addr, 1); + reserve(info->backup_start, sz); +#endif + + /* On powerpc memory ranges in device-tree is denoted as start + * and size rather than start and end, as is the case with + * other architectures like i386 . Because of this when loading + * the memory ranges in crashdump-elf.c the filesz calculation + * [ end - start + 1 ] goes for a toss. + * + * To be in sync with other archs adjust the end value for + * every crash memory range before calling the generic function + */ + + for (i = 0; i < nr_ranges; i++) { + end = crash_memory_range[i].end - 1; + crash_memory_range[i].end = end; + } + + +#ifdef CONFIG_PPC64 + /* Create elf header segment and store crash image data. */ + if (arch_options.core_header_type == CORE_TYPE_ELF64) { + if (crash_create_elf64_headers(info, &elf_info64, + crash_memory_range, nr_ranges, &tmp, + &sz, ELF_CORE_HEADER_ALIGN) < 0) + return -1; + } else if (crash_create_elf32_headers(info, &elf_info32, + crash_memory_range, nr_ranges, &tmp, &sz, + ELF_CORE_HEADER_ALIGN) < 0) + return -1; +#else + if (crash_create_elf32_headers(info, &elf_info32, crash_memory_range, + nr_ranges, &tmp, &sz, ELF_CORE_HEADER_ALIGN) + < 0) + return -1; +#endif + + elfcorehdr = add_buffer(info, tmp, sz, sz, align, + min_base, max_addr, 1); + reserve(elfcorehdr, sz); + /* modify and store the cmdline in a global array. This is later + * read by flatten_device_tree and modified if required + */ + add_cmdline_param(mod_cmdline, elfcorehdr, " elfcorehdr=", "K"); + add_cmdline(mod_cmdline, " maxcpus=1"); + return 0; +} + +/* + * Used to save various memory regions needed for the captured kernel. + */ + +void add_usable_mem_rgns(unsigned long long base, unsigned long long size) +{ + int i; + unsigned long long end = base + size; + unsigned long long ustart, uend; + + base = _ALIGN_DOWN(base, getpagesize()); + end = _ALIGN_UP(end, getpagesize()); + + for (i = 0; i < usablemem_rgns.size; i++) { + ustart = usablemem_rgns.ranges[i].start; + uend = usablemem_rgns.ranges[i].end; + if (base < uend && end > ustart) { + if ((base >= ustart) && (end <= uend)) + return; + if (base < ustart && end > uend) { + usablemem_rgns.ranges[i].start = base; + usablemem_rgns.ranges[i].end = end; + return; + } else if (base < ustart) { + usablemem_rgns.ranges[i].start = base; + return; + } else if (end > uend) { + usablemem_rgns.ranges[i].end = end; + return; + } + } + } + usablemem_rgns.ranges[usablemem_rgns.size].start = base; + usablemem_rgns.ranges[usablemem_rgns.size++].end = end; + + dbgprintf("usable memory rgns size:%u base:%llx size:%llx\n", + usablemem_rgns.size, base, size); +} + +int get_crash_kernel_load_range(uint64_t *start, uint64_t *end) +{ + unsigned long long value; + + if (!get_devtree_value(DEVTREE_CRASHKERNEL_BASE, &value)) + *start = value; + else + return -1; + + if (!get_devtree_value(DEVTREE_CRASHKERNEL_SIZE, &value)) + *end = *start + value - 1; + else + return -1; + + return 0; +} + +int is_crashkernel_mem_reserved(void) +{ + int fd; + + fd = open(DEVTREE_CRASHKERNEL_BASE, O_RDONLY); + if (fd < 0) + return 0; + close(fd); + return 1; +} + diff --git a/kexec/arch/ppc/crashdump-powerpc.h b/kexec/arch/ppc/crashdump-powerpc.h new file mode 100644 index 0000000..97b5095 --- /dev/null +++ b/kexec/arch/ppc/crashdump-powerpc.h @@ -0,0 +1,45 @@ +#ifndef CRASHDUMP_POWERPC_H +#define CRASHDUMP_POWERPC_H + +struct kexec_info; +int load_crashdump_segments(struct kexec_info *info, char *mod_cmdline, + unsigned long max_addr, unsigned long min_base); +void add_usable_mem_rgns(unsigned long long base, unsigned long long size); + +extern struct arch_options_t arch_options; + +#ifdef CONFIG_PPC64 +#define PAGE_OFFSET 0xC000000000000000UL +#define VMALLOCBASE 0xD000000000000000UL +#define MAXMEM (-KERNELBASE-VMALLOCBASE) +#else +#define PAGE_OFFSET 0xC0000000 +#define MAXMEM 0x30000000 /* Use CONFIG_LOWMEM_SIZE from kernel */ +#endif + +#define KERNELBASE PAGE_OFFSET +#define __pa(x) ((unsigned long)(x)-PAGE_OFFSET) + +#ifdef CONFIG_BOOKE +/* We don't need backup region in Book E */ +#define BACKUP_SRC_START 0x0000 +#define BACKUP_SRC_END 0x0000 +#define BACKUP_SRC_SIZE 0x0000 +#else +/* Backup Region, First 64K of System RAM. */ +#define BACKUP_SRC_START 0x0000 +#define BACKUP_SRC_END 0xffff +#define BACKUP_SRC_SIZE (BACKUP_SRC_END - BACKUP_SRC_START + 1) +#endif + +#define KDUMP_BACKUP_LIMIT BACKUP_SRC_SIZE + +extern unsigned long long crash_base; +extern unsigned long long crash_size; +extern unsigned int rtas_base; +extern unsigned int rtas_size; +extern uint64_t opal_base; +extern uint64_t opal_size; +extern uint64_t memory_limit; + +#endif /* CRASHDUMP_POWERPC_H */ diff --git a/kexec/arch/ppc/fixup_dtb.c b/kexec/arch/ppc/fixup_dtb.c new file mode 100644 index 0000000..92a0bfd --- /dev/null +++ b/kexec/arch/ppc/fixup_dtb.c @@ -0,0 +1,408 @@ +#define _GNU_SOURCE +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include "../../kexec.h" +#include "../../kexec-syscall.h" +#include <libfdt.h> +#include "ops.h" +#include "page.h" +#include "fixup_dtb.h" +#include "kexec-ppc.h" + +const char proc_dts[] = "/proc/device-tree"; + +static void print_fdt_reserve_regions(char *blob_buf) +{ + int i, num; + + if (!kexec_debug) + return; + /* Print out a summary of the final reserve regions */ + num = fdt_num_mem_rsv(blob_buf); + dbgprintf ("reserve regions: %d\n", num); + for (i = 0; i < num; i++) { + uint64_t offset, size; + + if (fdt_get_mem_rsv(blob_buf, i, &offset, &size) == 0) { + dbgprintf("%d: offset: %llx, size: %llx\n", i, offset, size); + } else { + dbgprintf("Error retreiving reserved region\n"); + } + } +} + + +static void fixup_nodes(char *nodes[]) +{ + int index = 0; + char *fname; + char *prop_name; + char *node_name; + void *node; + int len; + char *content; + off_t content_size; + int ret; + + while (nodes[index]) { + + len = asprintf(&fname, "%s%s", proc_dts, nodes[index]); + if (len < 0) + die("asprintf() failed\n"); + + content = slurp_file(fname, &content_size); + if (!content) { + die("Can't open %s: %s\n", fname, strerror(errno)); + } + + prop_name = fname + len; + while (*prop_name != '/') + prop_name--; + + *prop_name = '\0'; + prop_name++; + + node_name = fname + sizeof(proc_dts) - 1; + + node = finddevice(node_name); + if (!node) + node = create_node(NULL, node_name + 1); + + ret = setprop(node, prop_name, content, content_size); + if (ret < 0) + die("setprop of %s/%s size: %ld failed: %s\n", + node_name, prop_name, content_size, + fdt_strerror(ret)); + + free(content); + free(fname); + index++; + }; +} + +/* + * command line priority: + * - use the supplied command line + * - if none available use the command line from .dtb + * - if not available use the current command line + */ +static void fixup_cmdline(const char *cmdline) +{ + void *chosen; + char *fixup_cmd_node[] = { + "/chosen/bootargs", + NULL, + }; + + chosen = finddevice("/chosen"); + + if (!cmdline) { + if (!chosen) + fixup_nodes(fixup_cmd_node); + } else { + if (!chosen) + chosen = create_node(NULL, "chosen"); + setprop_str(chosen, "bootargs", cmdline); + } + return; +} + +#define EXPAND_GRANULARITY 1024 + +static char *expand_buf(int minexpand, char *blob_buf, off_t *blob_size) +{ + int size = fdt_totalsize(blob_buf); + int rc; + + size = _ALIGN(size + minexpand, EXPAND_GRANULARITY); + blob_buf = realloc(blob_buf, size); + if (!blob_buf) + die("Couldn't find %d bytes to expand device tree\n\r", size); + rc = fdt_open_into(blob_buf, blob_buf, size); + if (rc != 0) + die("Couldn't expand fdt into new buffer: %s\n\r", + fdt_strerror(rc)); + + *blob_size = fdt_totalsize(blob_buf); + + return blob_buf; +} + +static void fixup_reserve_regions(struct kexec_info *info, char *blob_buf) +{ + int ret, i; + int nodeoffset; + u64 val = 0; + + /* If this is a KEXEC kernel we add all regions since they will + * all need to be saved */ + if (info->kexec_flags & KEXEC_ON_CRASH) { + for (i = 0; i < info->nr_segments; i++) { + uint64_t address = (unsigned long)info->segment[i].mem; + uint64_t size = info->segment[i].memsz; + + while ((i+1) < info->nr_segments && + (address + size == (unsigned long)info->segment[i+1].mem)) { + size += info->segment[++i].memsz; + } + + ret = fdt_add_mem_rsv(blob_buf, address, size); + if (ret) { + printf("%s: Error adding memory range to memreserve!\n", + fdt_strerror(ret)); + goto out; + } + } + } else if (ramdisk || reuse_initrd) { + /* Otherwise we just add back the ramdisk and the device tree + * is already in the list */ + ret = fdt_add_mem_rsv(blob_buf, ramdisk_base, ramdisk_size); + if (ret) { + printf("%s: Unable to add new reserved memory for initrd flat device tree\n", + fdt_strerror(ret)); + goto out; + } + } + +#if 0 + /* XXX: Do not reserve spin-table for CPUs. */ + + /* Add reserve regions for cpu-release-addr */ + nodeoffset = fdt_node_offset_by_prop_value(blob_buf, -1, "device_type", "cpu", 4); + while (nodeoffset != -FDT_ERR_NOTFOUND) { + const void *buf; + int sz, ret; + u64 tmp; + + buf = fdt_getprop(blob_buf, nodeoffset, "cpu-release-addr", &sz); + + if (buf) { + if (sz == 4) { + tmp = *(u32 *)buf; + } else if (sz == 8) { + tmp = *(u64 *)buf; + } + + /* crude check to see if last value is repeated */ + if (_ALIGN_DOWN(tmp, PAGE_SIZE) != _ALIGN_DOWN(val, PAGE_SIZE)) { + val = tmp; + ret = fdt_add_mem_rsv(blob_buf, _ALIGN_DOWN(val, PAGE_SIZE), PAGE_SIZE); + if (ret) + printf("%s: Unable to add reserve for cpu-release-addr!\n", + fdt_strerror(ret)); + } + } + + nodeoffset = fdt_node_offset_by_prop_value(blob_buf, nodeoffset, + "device_type", "cpu", 4); + } +#endif + +out: + print_fdt_reserve_regions(blob_buf); +} + +static void fixup_memory(struct kexec_info *info, char *blob_buf) +{ + if (info->kexec_flags & KEXEC_ON_CRASH) { + int nodeoffset, len = 0; + u8 tmp[16]; + const unsigned long *addrcell, *sizecell; + + nodeoffset = fdt_path_offset(blob_buf, "/memory"); + + if (nodeoffset < 0) { + printf("Error searching for memory node!\n"); + return; + } + + addrcell = fdt_getprop(blob_buf, 0, "#address-cells", NULL); + /* use shifts and mask to ensure endianness */ + if ((addrcell) && (*addrcell == 2)) { + tmp[0] = (crash_base >> 56) & 0xff; + tmp[1] = (crash_base >> 48) & 0xff; + tmp[2] = (crash_base >> 40) & 0xff; + tmp[3] = (crash_base >> 32) & 0xff; + tmp[4] = (crash_base >> 24) & 0xff; + tmp[5] = (crash_base >> 16) & 0xff; + tmp[6] = (crash_base >> 8) & 0xff; + tmp[7] = (crash_base ) & 0xff; + len = 8; + } else { + tmp[0] = (crash_base >> 24) & 0xff; + tmp[1] = (crash_base >> 16) & 0xff; + tmp[2] = (crash_base >> 8) & 0xff; + tmp[3] = (crash_base ) & 0xff; + len = 4; + } + + sizecell = fdt_getprop(blob_buf, 0, "#size-cells", NULL); + /* use shifts and mask to ensure endianness */ + if ((sizecell) && (*sizecell == 2)) { + tmp[0+len] = (crash_size >> 56) & 0xff; + tmp[1+len] = (crash_size >> 48) & 0xff; + tmp[2+len] = (crash_size >> 40) & 0xff; + tmp[3+len] = (crash_size >> 32) & 0xff; + tmp[4+len] = (crash_size >> 24) & 0xff; + tmp[5+len] = (crash_size >> 16) & 0xff; + tmp[6+len] = (crash_size >> 8) & 0xff; + tmp[7+len] = (crash_size ) & 0xff; + len += 8; + } else { + tmp[0+len] = (crash_size >> 24) & 0xff; + tmp[1+len] = (crash_size >> 16) & 0xff; + tmp[2+len] = (crash_size >> 8) & 0xff; + tmp[3+len] = (crash_size ) & 0xff; + len += 4; + } + + if (fdt_setprop(blob_buf, nodeoffset, "reg", tmp, len) != 0) { + printf ("Error setting memory node!\n"); + } + + fdt_delprop(blob_buf, nodeoffset, "linux,usable-memory"); + } +} + +/* removes crashkernel nodes if they exist and we are *rebooting* + * into a crashkernel. These nodes should not exist after we + * crash and reboot into a new kernel + */ +static void fixup_crashkernel(struct kexec_info *info, char *blob_buf) +{ + int nodeoffset; + + nodeoffset = fdt_path_offset(blob_buf, "/chosen"); + + if (info->kexec_flags & KEXEC_ON_CRASH) { + if (nodeoffset < 0) { + printf("fdt_crashkernel: %s\n", fdt_strerror(nodeoffset)); + return; + } + + fdt_delprop(blob_buf, nodeoffset, "linux,crashkernel-base"); + fdt_delprop(blob_buf, nodeoffset, "linux,crashkernel-size"); + } +} +/* remove the old chosen nodes if they exist and add correct chosen + * nodes if we have an initd + */ +static void fixup_initrd(char *blob_buf) +{ + int err, nodeoffset; + unsigned long tmp; + + nodeoffset = fdt_path_offset(blob_buf, "/chosen"); + + if (nodeoffset < 0) { + printf("fdt_initrd: %s\n", fdt_strerror(nodeoffset)); + return; + } + + fdt_delprop(blob_buf, nodeoffset, "linux,initrd-start"); + fdt_delprop(blob_buf, nodeoffset, "linux,initrd-end"); + + if ((reuse_initrd || ramdisk) && + ((ramdisk_base != 0) && (ramdisk_size != 0))) { + tmp = ramdisk_base; + err = fdt_setprop(blob_buf, nodeoffset, + "linux,initrd-start", &tmp, sizeof(tmp)); + if (err < 0) { + printf("WARNING: " + "could not set linux,initrd-start %s.\n", + fdt_strerror(err)); + return; + } + + tmp = ramdisk_base + ramdisk_size; + err = fdt_setprop(blob_buf, nodeoffset, + "linux,initrd-end", &tmp, sizeof(tmp)); + if (err < 0) { + printf("WARNING: could not set linux,initrd-end %s.\n", + fdt_strerror(err)); + return; + } + } +} + +char *fixup_dtb_init(struct kexec_info *info, char *blob_buf, off_t *blob_size, + unsigned long hole_addr, unsigned long *dtb_addr) +{ + int ret, i, num = fdt_num_mem_rsv(blob_buf); + + fdt_init(blob_buf); + + /* Remove the existing reserve regions as they will no longer + * be valid after we reboot */ + for (i = num - 1; i >= 0; i--) { + ret = fdt_del_mem_rsv(blob_buf, i); + if (ret) { + printf("%s: Error deleting memory reserve region %d from device tree!\n", + fdt_strerror(ret), i); + } + } + + /* Pack the FDT first, so we don't grow excessively if there is already free space */ + ret = fdt_pack(blob_buf); + if (ret) + printf("%s: Unable to pack flat device tree\n", fdt_strerror(ret)); + + /* info->nr_segments just a guide, will grow by at least EXPAND_GRANULARITY */ + blob_buf = expand_buf(info->nr_segments * sizeof(struct fdt_reserve_entry), + blob_buf, blob_size); + + /* add reserve region for *THIS* fdt */ + *dtb_addr = locate_hole(info, *blob_size, 0, + hole_addr, hole_addr+KERNEL_ACCESS_TOP, -1); + ret = fdt_add_mem_rsv(blob_buf, *dtb_addr, PAGE_ALIGN(*blob_size)); + if (ret) { + printf("%s: Unable to add new reserved memory for the flat device tree\n", + fdt_strerror(ret)); + } + + return blob_buf; +} + +static void save_fixed_up_dtb(char *blob_buf, off_t blob_size) +{ + FILE *fp; + + if (!kexec_debug) + return; + fp = fopen("debug.dtb", "w"); + if (fp) { + if ( blob_size == fwrite(blob_buf, sizeof(char), blob_size, fp)) { + dbgprintf("debug.dtb written\n"); + } else { + dbgprintf("Unable to write debug.dtb\n"); + } + + fclose(fp); + } else { + dbgprintf("Unable to dump flat device tree to debug.dtb\n"); + } +} + +char *fixup_dtb_finalize(struct kexec_info *info, char *blob_buf, off_t *blob_size, + char *nodes[], char *cmdline) +{ + fixup_nodes(nodes); + fixup_cmdline(cmdline); + fixup_reserve_regions(info, blob_buf); + fixup_memory(info, blob_buf); + fixup_initrd(blob_buf); + fixup_crashkernel(info, blob_buf); + + blob_buf = (char *)dt_ops.finalize(); + *blob_size = fdt_totalsize(blob_buf); + + save_fixed_up_dtb(blob_buf, *blob_size); + + return blob_buf; +} diff --git a/kexec/arch/ppc/fixup_dtb.h b/kexec/arch/ppc/fixup_dtb.h new file mode 100644 index 0000000..b706a5a --- /dev/null +++ b/kexec/arch/ppc/fixup_dtb.h @@ -0,0 +1,10 @@ +#ifndef __FIXUP_DTB_H +#define __FIXUP_DTB_H + +char *fixup_dtb_init(struct kexec_info *info, char *blob_buf, off_t *blob_size, + unsigned long hole_addr, unsigned long *dtb_addr); + +char *fixup_dtb_finalize(struct kexec_info *info, char *blob_buf, off_t *blob_size, + char *nodes[], char *cmdline); + +#endif diff --git a/kexec/arch/ppc/fs2dt.c b/kexec/arch/ppc/fs2dt.c new file mode 100644 index 0000000..fed499b --- /dev/null +++ b/kexec/arch/ppc/fs2dt.c @@ -0,0 +1,471 @@ +/* + * fs2dt: creates a flattened device-tree + * + * Copyright (C) 2004,2005 Milton D Miller II, IBM Corporation + * Copyright (C) 2005 R Sharada (sharada@in.ibm.com), IBM Corporation + * + * 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 (version 2 of the License). + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <sys/types.h> +#include <sys/stat.h> + +#include <fcntl.h> +#include <dirent.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <stdio.h> +#include "../../kexec.h" +#include "kexec-ppc.h" +#include "types.h" + +#define MAXPATH 1024 /* max path name length */ +#define NAMESPACE 16384 /* max bytes for property names */ +#define TREEWORDS 65536 /* max 32 bit words for properties */ +#define MEMRESERVE 256 /* max number of reserved memory blks */ +#define MAX_MEMORY_RANGES 1024 + +static char pathname[MAXPATH]; +static char propnames[NAMESPACE] = { 0 }; +static unsigned dtstruct[TREEWORDS], *dt; +static unsigned long long mem_rsrv[2*MEMRESERVE] = { 0, 0 }; + +static int crash_param; +static char local_cmdline[COMMAND_LINE_SIZE] = { "" }; +static unsigned *dt_len; /* changed len of modified cmdline + in flat device-tree */ +static struct bootblock bb[1]; + +void reserve(unsigned long long where, unsigned long long length) +{ + size_t offset; + + for (offset = 0; mem_rsrv[offset + 1]; offset += 2) + ; + + if (offset + 4 >= 2 * MEMRESERVE) + die("unrecoverable error: exhasuted reservation meta data\n"); + + mem_rsrv[offset] = where; + mem_rsrv[offset + 1] = length; + mem_rsrv[offset + 3] = 0; /* N.B: don't care about offset + 2 */ +} + +/* look for properties we need to reserve memory space for */ +static void checkprop(char *name, unsigned *data, int len) +{ + static unsigned long long base, size, end; + + if ((data == NULL) && (base || size || end)) + die("unrecoverable error: no property data"); + else if (!strcmp(name, "linux,rtas-base")) + base = *data; + else if (!strcmp(name, "linux,tce-base")) + base = *(unsigned long long *) data; + else if (!strcmp(name, "rtas-size") || + !strcmp(name, "linux,tce-size")) + size = *data; + else if (reuse_initrd && !strcmp(name, "linux,initrd-start")) + if (len == 8) + base = *(unsigned long long *) data; + else + base = *data; + else if (reuse_initrd && !strcmp(name, "linux,initrd-end")) + end = *(unsigned long long *) data; + + if (size && end) + die("unrecoverable error: size and end set at same time\n"); + if (base && size) { + reserve(base, size); + base = 0; + size = 0; + } + if (base && end) { + reserve(base, end-base); + base = 0; + end = 0; + } +} + +/* + * return the property index for a property name, creating a new one + * if needed. + */ +static unsigned propnum(const char *name) +{ + unsigned offset = 0; + + while (propnames[offset]) + if (strcmp(name, propnames+offset)) + offset += strlen(propnames+offset)+1; + else + return offset; + + if (NAMESPACE - offset < strlen(name) + 1) + die("unrecoverable error: propnames overrun\n"); + + strcpy(propnames+offset, name); + + return offset; +} + +static void add_usable_mem_property(int fd, int len) +{ + char fname[MAXPATH], *bname; + unsigned long buf[2]; + unsigned long ranges[2*MAX_MEMORY_RANGES]; + unsigned long long base, end, loc_base, loc_end; + int range, rlen = 0; + + strcpy(fname, pathname); + bname = strrchr(fname, '/'); + bname[0] = '\0'; + bname = strrchr(fname, '/'); + if (strncmp(bname, "/memory@", 8) && strcmp(bname, "/memory")) + return; + + if (lseek(fd, 0, SEEK_SET) < 0) + die("unrecoverable error: error seeking in \"%s\": %s\n", + pathname, strerror(errno)); + if (read_memory_region_limits(fd, &base, &end) != 0) + die("unrecoverable error: error parsing memory/reg limits\n"); + + for (range = 0; range < usablemem_rgns.size; range++) { + loc_base = usablemem_rgns.ranges[range].start; + loc_end = usablemem_rgns.ranges[range].end; + if (loc_base >= base && loc_end <= end) { + ranges[rlen++] = loc_base; + ranges[rlen++] = loc_end - loc_base; + } else if (base < loc_end && end > loc_base) { + if (loc_base < base) + loc_base = base; + if (loc_end > end) + loc_end = end; + ranges[rlen++] = loc_base; + ranges[rlen++] = loc_end - loc_base; + } + } + + if (!rlen) { + /* + * User did not pass any ranges for thsi region. Hence, write + * (0,0) duple in linux,usable-memory property such that + * this region will be ignored. + */ + ranges[rlen++] = 0; + ranges[rlen++] = 0; + } + + rlen = rlen * sizeof(unsigned long); + /* + * No add linux,usable-memory property. + */ + *dt++ = 3; + *dt++ = rlen; + *dt++ = propnum("linux,usable-memory"); + memcpy(dt, &ranges, rlen); + dt += (rlen + 3)/4; +} + +/* put all properties (files) in the property structure */ +static void putprops(char *fn, struct dirent **nlist, int numlist) +{ + struct dirent *dp; + int i = 0, fd, len; + struct stat statbuf; + + for (i = 0; i < numlist; i++) { + dp = nlist[i]; + strcpy(fn, dp->d_name); + + if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) + continue; + + if (lstat(pathname, &statbuf)) + die("unrecoverable error: could not stat \"%s\": %s\n", + pathname, strerror(errno)); + + if (!crash_param && !strcmp(fn, "linux,crashkernel-base")) + continue; + + if (!crash_param && !strcmp(fn, "linux,crashkernel-size")) + continue; + + /* + * This property will be created for each node during kexec + * boot. So, ignore it. + */ + if (!strcmp(dp->d_name, "linux,pci-domain") || + !strcmp(dp->d_name, "linux,htab-base") || + !strcmp(dp->d_name, "linux,htab-size") || + !strcmp(dp->d_name, "linux,kernel-end") || + !strcmp(dp->d_name, "linux,usable-memory")) + continue; + + /* This property will be created/modified later in putnode() + * So ignore it, unless we are reusing the initrd. + */ + if ((!strcmp(dp->d_name, "linux,initrd-start") || + !strcmp(dp->d_name, "linux,initrd-end")) && + !reuse_initrd) + continue; + + if (!S_ISREG(statbuf.st_mode)) + continue; + + len = statbuf.st_size; + + *dt++ = 3; + dt_len = dt; + *dt++ = len; + *dt++ = propnum(fn); + + fd = open(pathname, O_RDONLY); + if (fd == -1) + die("unrecoverable error: could not open \"%s\": %s\n", + pathname, strerror(errno)); + + if (read(fd, dt, len) != len) + die("unrecoverable error: could not read \"%s\": %s\n", + pathname, strerror(errno)); + + checkprop(fn, dt, len); + + /* Get the cmdline from the device-tree and modify it */ + if (!strcmp(dp->d_name, "bootargs")) { + int cmd_len; + char temp_cmdline[COMMAND_LINE_SIZE] = { "" }; + char *param = NULL; + cmd_len = strlen(local_cmdline); + if (cmd_len != 0) { + param = strstr(local_cmdline, "crashkernel="); + if (param) + crash_param = 1; + param = strstr(local_cmdline, "root="); + } + if (!param) { + char *old_param; + memcpy(temp_cmdline, dt, len); + param = strstr(temp_cmdline, "root="); + if (param) { + old_param = strtok(param, " "); + if (cmd_len != 0) + strcat(local_cmdline, " "); + strcat(local_cmdline, old_param); + } + } + strcat(local_cmdline, " "); + cmd_len = strlen(local_cmdline); + cmd_len = cmd_len + 1; + memcpy(dt, local_cmdline, cmd_len); + len = cmd_len; + *dt_len = cmd_len; + + dbgprintf("Modified cmdline:%s\n", local_cmdline); + + } + + dt += (len + 3)/4; + if (!strcmp(dp->d_name, "reg") && usablemem_rgns.size) + add_usable_mem_property(fd, len); + close(fd); + } + + fn[0] = '\0'; + checkprop(pathname, NULL, 0); +} + +/* + * Compare function used to sort the device-tree directories + * This function will be passed to scandir. + */ +static int comparefunc(const void *dentry1, const void *dentry2) +{ + char *str1 = (*(struct dirent **)dentry1)->d_name; + char *str2 = (*(struct dirent **)dentry2)->d_name; + char *p1, *p2; + int res = 0, max_len; + + /* + * strcmp scans from left to right and fails to idetify for some + * strings such as memory@10000000 and memory@f000000. + * Therefore, we get the wrong sorted order like memory@10000000 and + * memory@f000000. + */ + if ((p1 = strchr(str1, '@')) && (p2 = strchr(str2, '@'))) { + max_len = max(p1 - str1, p2 - str2); + if ((res = strncmp(str1, str2, max_len)) == 0) { + /* prefix is equal - compare part after '@' by length */ + p1++; p2++; + res = strlen(p1) - strlen(p2); + if (res == 0) + /* equal length, compare by strcmp() */ + res = strcmp(p1,p2); + } + } else { + res = strcmp(str1, str2); + } + + return res; +} + +/* + * put a node (directory) in the property structure. first properties + * then children. + */ +static void putnode(void) +{ + char *dn; + struct dirent *dp; + char *basename; + struct dirent **namelist; + int numlist, i; + struct stat statbuf; + + numlist = scandir(pathname, &namelist, 0, comparefunc); + if (numlist < 0) + die("unrecoverable error: could not scan \"%s\": %s\n", + pathname, strerror(errno)); + if (numlist == 0) + die("unrecoverable error: no directory entries in \"%s\"", + pathname); + + basename = strrchr(pathname, '/') + 1; + + *dt++ = 1; + strcpy((void *)dt, *basename ? basename : ""); + dt += strlen((void *)dt) / sizeof(unsigned) + 1; + + strcat(pathname, "/"); + dn = pathname + strlen(pathname); + + putprops(dn, namelist, numlist); + + /* + * Add initrd entries to the second kernel + * if + * a) a ramdisk is specified in cmdline + * OR + * b) reuseinitrd is specified and a initrd is + * used by the kernel. + * + */ + if ((ramdisk || (initrd_base && reuse_initrd)) + && !strcmp(basename, "chosen/")) { + int len = 8; + unsigned long long initrd_end; + *dt++ = 3; + *dt++ = len; + *dt++ = propnum("linux,initrd-start"); + + memcpy(dt, &initrd_base, len); + dt += (len + 3)/4; + + len = 8; + *dt++ = 3; + *dt++ = len; + *dt++ = propnum("linux,initrd-end"); + + initrd_end = initrd_base + initrd_size; + + memcpy(dt, &initrd_end, len); + dt += (len + 3)/4; + /* reserve the existing initrd image in case of reuse_initrd */ + if (initrd_base && initrd_size && reuse_initrd) + reserve(initrd_base, initrd_size); + } + + for (i = 0; i < numlist; i++) { + dp = namelist[i]; + strcpy(dn, dp->d_name); + free(namelist[i]); + + if (!strcmp(dn, ".") || !strcmp(dn, "..")) + continue; + + if (lstat(pathname, &statbuf)) + die("unrecoverable error: could not stat \"%s\": %s\n", + pathname, strerror(errno)); + + if (S_ISDIR(statbuf.st_mode)) + putnode(); + } + + *dt++ = 2; + dn[-1] = '\0'; + free(namelist); +} + +int create_flatten_tree(struct kexec_info *info, unsigned char **bufp, + unsigned long *sizep, char *cmdline) +{ + unsigned long len; + unsigned long tlen; + unsigned char *buf; + unsigned long me; + + me = 0; + + strcpy(pathname, "/proc/device-tree/"); + + dt = dtstruct; + + if (cmdline) + strcpy(local_cmdline, cmdline); + + putnode(); + *dt++ = 9; + + len = _ALIGN(sizeof(bb[0]), 8); + + bb->off_mem_rsvmap = len; + + for (len = 1; mem_rsrv[len]; len += 2) + ; + len += 3; + len *= sizeof(mem_rsrv[0]); + + bb->off_dt_struct = bb->off_mem_rsvmap + len; + + len = dt - dtstruct; + len *= sizeof(unsigned); + bb->dt_struct_size = len; + bb->off_dt_strings = bb->off_dt_struct + len; + + len = propnum(""); + bb->dt_strings_size = len; + len = _ALIGN(len, 4); + bb->totalsize = bb->off_dt_strings + len; + + bb->magic = 0xd00dfeed; + bb->version = 17; + bb->last_comp_version = 16; + + reserve(me, bb->totalsize); /* patched later in kexec_load */ + + buf = (unsigned char *) malloc(bb->totalsize); + *bufp = buf; + memcpy(buf, bb, bb->off_mem_rsvmap); + tlen = bb->off_mem_rsvmap; + memcpy(buf+tlen, mem_rsrv, bb->off_dt_struct - bb->off_mem_rsvmap); + tlen = tlen + (bb->off_dt_struct - bb->off_mem_rsvmap); + memcpy(buf+tlen, dtstruct, bb->off_dt_strings - bb->off_dt_struct); + tlen = tlen + (bb->off_dt_strings - bb->off_dt_struct); + memcpy(buf+tlen, propnames, bb->totalsize - bb->off_dt_strings); + tlen = tlen + bb->totalsize - bb->off_dt_strings; + *sizep = tlen; + return 0; +} diff --git a/kexec/arch/ppc/include/arch/options.h b/kexec/arch/ppc/include/arch/options.h new file mode 100644 index 0000000..b2176ab --- /dev/null +++ b/kexec/arch/ppc/include/arch/options.h @@ -0,0 +1,47 @@ +#ifndef KEXEC_ARCH_PPC_OPTIONS_H +#define KEXEC_ARCH_PPC_OPTIONS_H + +#define OPT_ARCH_MAX (OPT_MAX+0) + +/* All 'local' loader options: */ +#define OPT_APPEND (OPT_ARCH_MAX+0) +#define OPT_GAMECUBE (OPT_ARCH_MAX+1) +#define OPT_DTB (OPT_ARCH_MAX+2) +#define OPT_NODES (OPT_ARCH_MAX+3) +#define OPT_RAMDISK (OPT_ARCH_MAX+4) + +/* Options relevant to the architecture (excluding loader-specific ones), + * in this case none: + */ +#define KEXEC_ARCH_OPTIONS \ + KEXEC_OPTIONS \ + +#define KEXEC_ARCH_OPT_STR KEXEC_OPT_STR "" + +/* The following two #defines list ALL of the options added by all of the + * architecture's loaders. + * o main() uses this complete list to scan for its options, ignoring + * arch-specific/loader-specific ones. + * o Then, arch_process_options() uses this complete list to scan for its + * options, ignoring general/loader-specific ones. + * o Then, the file_type[n].load re-scans for options, using + * KEXEC_ARCH_OPTIONS plus its loader-specific options subset. + * Any unrecognised options cause an error here. + * + * This is done so that main()'s/arch_process_options()'s getopt_long() calls + * don't choose a kernel filename from random arguments to options they don't + * recognise -- as they now recognise (if not act upon) all possible options. + */ +#define KEXEC_ALL_OPTIONS \ + KEXEC_ARCH_OPTIONS \ + {"command-line", 1, 0, OPT_APPEND},\ + {"append", 1, 0, OPT_APPEND},\ + {"ramdisk", 1, 0, OPT_APPEND},\ + {"initrd", 1, 0, OPT_APPEND},\ + {"gamecube", 1, 0, OPT_GAMECUBE},\ + {"dtb", 1, 0, OPT_DTB},\ + {"reuse-node", 1, 0, OPT_NODES}, + +#define KEXEC_ALL_OPT_STR KEXEC_OPT_STR + +#endif /* KEXEC_ARCH_PPC_OPTIONS_H */ diff --git a/kexec/arch/ppc/include/page.h b/kexec/arch/ppc/include/page.h new file mode 100644 index 0000000..65877bc --- /dev/null +++ b/kexec/arch/ppc/include/page.h @@ -0,0 +1,27 @@ +#ifndef _PPC_BOOT_PAGE_H +#define _PPC_BOOT_PAGE_H +/* + * Copyright (C) 2001 PPC64 Team, IBM Corp + * + * 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. + */ + +#ifdef __ASSEMBLY__ +#define ASM_CONST(x) x +#else +#define __ASM_CONST(x) x##UL +#define ASM_CONST(x) __ASM_CONST(x) +#endif + +/* PAGE_SHIFT determines the page size */ +#define PAGE_SHIFT 12 +#define PAGE_SIZE (ASM_CONST(1) << PAGE_SHIFT) +#define PAGE_MASK (~(PAGE_SIZE-1)) + +/* to align the pointer to the (next) page boundary */ +#define PAGE_ALIGN(addr) (((addr)+((PAGE_SIZE)-1))&(~((PAGE_SIZE)-1))) + +#endif /* _PPC_BOOT_PAGE_H */ diff --git a/kexec/arch/ppc/include/types.h b/kexec/arch/ppc/include/types.h new file mode 100644 index 0000000..31393d1 --- /dev/null +++ b/kexec/arch/ppc/include/types.h @@ -0,0 +1,27 @@ +#ifndef _TYPES_H_ +#define _TYPES_H_ + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long long u64; +typedef signed char s8; +typedef short s16; +typedef int s32; +typedef long long s64; + +#define min(x,y) ({ \ + typeof(x) _x = (x); \ + typeof(y) _y = (y); \ + (void) (&_x == &_y); \ + _x < _y ? _x : _y; }) + +#define max(x,y) ({ \ + typeof(x) _x = (x); \ + typeof(y) _y = (y); \ + (void) (&_x == &_y); \ + _x > _y ? _x : _y; }) + +#endif /* _TYPES_H_ */ diff --git a/kexec/arch/ppc/kexec-dol-ppc.c b/kexec/arch/ppc/kexec-dol-ppc.c new file mode 100644 index 0000000..800c072 --- /dev/null +++ b/kexec/arch/ppc/kexec-dol-ppc.c @@ -0,0 +1,468 @@ +/* + * kexec-dol-ppc.c - kexec DOL executable loader for the PowerPC + * Copyright (C) 2004 Albert Herranz + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#define _GNU_SOURCE +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <getopt.h> +#include <elf.h> +#include <boot/elf_boot.h> +#include <ip_checksum.h> +#include "../../kexec.h" +#include "kexec-ppc.h" +#include <arch/options.h> + +static int debug = 0; + +/* + * I've found out there DOLs with unaligned and/or overlapping sections. + * I assume that sizes of sections can be wrong on these DOLs so I trust + * better start of sections. + * In order to load DOLs, I first extend sections to page aligned boundaries + * and then merge overlapping sections starting from lower addresses. + * -- Albert Herranz + */ + +/* DOL related stuff */ + +#define DOL_HEADER_SIZE 0x100 + +#define DOL_SECT_MAX_TEXT 7 /* text sections */ +#define DOL_SECT_MAX_DATA 11 /* data sections */ +#define DOL_MAX_SECT (DOL_SECT_MAX_TEXT+DOL_SECT_MAX_DATA) + +/* this is the DOL executable header */ +typedef struct { + uint32_t offset_text[DOL_SECT_MAX_TEXT]; /* in the file */ + uint32_t offset_data[DOL_SECT_MAX_DATA]; + uint32_t address_text[DOL_SECT_MAX_TEXT]; /* in memory */ + uint32_t address_data[DOL_SECT_MAX_DATA]; + uint32_t size_text[DOL_SECT_MAX_TEXT]; + uint32_t size_data[DOL_SECT_MAX_DATA]; + uint32_t address_bss; + uint32_t size_bss; + uint32_t entry_point; +} dol_header; + +#define dol_sect_offset(hptr, index) \ + ((index >= DOL_SECT_MAX_TEXT)? \ + hptr->offset_data[index - DOL_SECT_MAX_TEXT] \ + :hptr->offset_text[index]) +#define dol_sect_address(hptr, index) \ + ((index >= DOL_SECT_MAX_TEXT)? \ + hptr->address_data[index - DOL_SECT_MAX_TEXT] \ + :hptr->address_text[index]) +#define dol_sect_size(hptr, index) \ + ((index >= DOL_SECT_MAX_TEXT)? \ + hptr->size_data[index - DOL_SECT_MAX_TEXT] \ + :hptr->size_text[index]) +#define dol_sect_type(index) \ + ((index >= DOL_SECT_MAX_TEXT) ? "data" : "text") + +typedef struct { + uint32_t sects_bitmap; + uint32_t start; + uint32_t size; +} dol_segment; + +#define dol_seg_end(s1) \ + (s1->start + s1->size) +#define dol_seg_after_sect(s1, s2) \ + (s1->start >= dol_seg_end(s2)) +#define dol_seg_overlaps(s1, s2) \ + (!(dol_seg_after_sect(s1,s2) || dol_seg_after_sect(s2,s1))) + +/* same as in asm/page.h */ +#define PAGE_SHIFT 12 +#define PAGE_SIZE (1UL << PAGE_SHIFT) +#define PAGE_MASK (~((1 << PAGE_SHIFT) - 1)) +#define PAGE_ALIGN(addr) _ALIGN(addr, PAGE_SIZE) + +#define MAX_COMMAND_LINE 256 + +#define UPSZ(X) _ALIGN_UP(sizeof(X), 4) +static struct boot_notes { + Elf_Bhdr hdr; + Elf_Nhdr bl_hdr; + unsigned char bl_desc[UPSZ(BOOTLOADER)]; + Elf_Nhdr blv_hdr; + unsigned char blv_desc[UPSZ(BOOTLOADER_VERSION)]; + Elf_Nhdr cmd_hdr; + unsigned char command_line[0]; +} elf_boot_notes = { + .hdr = { + .b_signature = 0x0E1FB007, + .b_size = sizeof(elf_boot_notes), + .b_checksum = 0, + .b_records = 3, + }, + .bl_hdr = { + .n_namesz = 0, + .n_descsz = sizeof(BOOTLOADER), + .n_type = EBN_BOOTLOADER_NAME, + }, + .bl_desc = BOOTLOADER, + .blv_hdr = { + .n_namesz = 0, + .n_descsz = sizeof(BOOTLOADER_VERSION), + .n_type = EBN_BOOTLOADER_VERSION, + }, + .blv_desc = BOOTLOADER_VERSION, + .cmd_hdr = { + .n_namesz = 0, + .n_descsz = 0, + .n_type = EBN_COMMAND_LINE, + }, +}; + +void print_sects_bitmap(dol_segment * seg) +{ + int i, first_seen; + + printf("\t" "sects_bitmap"); + first_seen = 0; + for (i = 0; i < DOL_MAX_SECT; i++) { + if ((seg->sects_bitmap & (1 << i)) == 0) + continue; + printf("%c%d", (first_seen ? ',' : '='), i); + first_seen = 1; + } + printf("\n"); +} + +void print_dol_segment(dol_segment * seg) +{ + printf("dol segment:\n"); + printf("\t" "start=%08lx, size=%ld (%08lx)\n", + (unsigned long)seg->start, (unsigned long)seg->size, + (unsigned long)seg->size); + printf("\t" "end=%08lx\n", (unsigned long)dol_seg_end(seg)); + print_sects_bitmap(seg); +} + +int load_dol_segments(dol_segment * seg, int max_segs, dol_header * h) +{ + int i, n, remaining; + unsigned int start, size; + unsigned long adj1, adj2, end1; + + n = 0; + remaining = max_segs; + for (i = 0; i < DOL_MAX_SECT && remaining > 0; i++) { + /* zero here means the section is not in use */ + if (dol_sect_size(h, i) == 0) + continue; + + /* we initially map 1 seg to 1 sect */ + seg->sects_bitmap = (1 << i); + + start = dol_sect_address(h, i); + size = dol_sect_size(h, i); + + /* page align the segment */ + seg->start = start & PAGE_MASK; + end1 = start + size; + adj1 = start - seg->start; + adj2 = PAGE_ALIGN(end1) - end1; + seg->size = adj1 + size + adj2; + + //print_dol_segment(seg); + + seg++; + remaining--; + n++; + } + return n; +} + +void fix_dol_segments_overlaps(dol_segment * seg, int max_segs) +{ + int i, j; + dol_segment *p, *pp; + long extra_length; + + /* look for overlapping segments and fix them */ + for (i = 0; i < max_segs; i++) { + p = seg + i; /* segment p */ + + /* not really a segment */ + if (p->size == 0) + continue; + + /* check if overlaps any previous segments */ + for (j = 0; j < i; j++) { + pp = seg + j; /* segment pp */ + + /* not a segment or no overlap */ + if (pp->size == 0 || !dol_seg_overlaps(p, pp)) + continue; + + /* merge the two segments */ + if (pp->start < p->start) { + /* extend pp to include p and delete p */ + extra_length = dol_seg_end(p) - dol_seg_end(pp); + if (extra_length > 0) { + pp->size += extra_length; + } + pp->sects_bitmap |= p->sects_bitmap; + p->size = p->start = p->sects_bitmap = 0; + + /* restart the loop because p was deleted */ + i = 0; + break; + } else { + /* extend p to include pp and delete pp */ + extra_length = dol_seg_end(pp) - dol_seg_end(p); + if (extra_length > 0) { + p->size += extra_length; + } + p->sects_bitmap |= pp->sects_bitmap; + pp->size = pp->start = pp->sects_bitmap = 0; + } + } + } +} + +int dol_ppc_probe(const char *buf, off_t dol_length) +{ + dol_header header, *h; + int i, valid = 0; + + /* the DOL file should be at least as long as the DOL header */ + if (dol_length < DOL_HEADER_SIZE) { + if (debug) { + fprintf(stderr, "Not a DOL file, too short.\n"); + } + return -1; + } + + /* read the DOL header */ + memcpy(&header, buf, sizeof(header)); + h = &header; + + /* now perform some sanity checks */ + for (i = 0; i < DOL_MAX_SECT; i++) { + /* DOL segment MAY NOT be physically stored in the header */ + if ((dol_sect_offset(h, i) != 0) + && (dol_sect_offset(h, i) < DOL_HEADER_SIZE)) { + if (debug) { + fprintf(stderr, + "%s segment offset within DOL header\n", + dol_sect_type(i)); + } + return -1; + } + + /* end of physical storage must be within file */ + if ((uintmax_t)(dol_sect_offset(h, i) + dol_sect_size(h, i)) > + (uintmax_t)dol_length) { + if (debug) { + fprintf(stderr, + "%s segment past DOL file size\n", + dol_sect_type(i)); + } + return -1; + } + + /* we only should accept DOLs with segments above 2GB */ + if (dol_sect_address(h, i) != 0 + && !(dol_sect_address(h, i) & 0x80000000)) { + fprintf(stderr, "warning, %s segment below 2GB\n", + dol_sect_type(i)); + } + + if (i < DOL_SECT_MAX_TEXT) { + /* remember that entrypoint was in a code segment */ + if (h->entry_point >= dol_sect_address(h, i) + && h->entry_point < dol_sect_address(h, i) + + dol_sect_size(h, i)) + valid = 1; + } + } + + /* if there is a BSS segment it must^H^H^H^Hshould be above 2GB, too */ + if (h->address_bss != 0 && !(h->address_bss & 0x80000000)) { + fprintf(stderr, "warning, BSS segment below 2GB\n"); + } + + /* if entrypoint is not within a code segment reject this file */ + if (!valid) { + if (debug) { + fprintf(stderr, "Entry point out of text segment\n"); + } + return -1; + } + + /* I've got a dol */ + return 0; +} + +void dol_ppc_usage(void) +{ + printf + (" --command-line=STRING Set the kernel command line to STRING.\n" + " --append=STRING Set the kernel command line to STRING.\n"); + +} + +int dol_ppc_load(int argc, char **argv, const char *buf, off_t UNUSED(len), + struct kexec_info *info) +{ + dol_header header, *h; + unsigned long entry; + char *arg_buf; + size_t arg_bytes; + unsigned long arg_base; + struct boot_notes *notes; + size_t note_bytes; + const char *command_line; + int command_line_len; + unsigned long mstart; + dol_segment dol_segs[DOL_MAX_SECT]; + unsigned int sects_bitmap; + unsigned long lowest_start; + int i, j, k; + int opt; + + /* See options.h -- add any more there, too. */ + static const struct option options[] = { + KEXEC_ARCH_OPTIONS + {"command-line", 1, 0, OPT_APPEND}, + {"append", 1, 0, OPT_APPEND}, + {0, 0, 0, 0}, + }; + static const char short_options[] = KEXEC_ARCH_OPT_STR; + + /* + * Parse the command line arguments + */ + command_line = 0; + while ((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) { + switch (opt) { + default: + /* Ignore core options */ + if (opt < OPT_ARCH_MAX) { + break; + } + case OPT_APPEND: + command_line = optarg; + break; + } + } + command_line_len = 0; + if (command_line) { + command_line_len = strlen(command_line) + 1; + } + + /* read the DOL header */ + memcpy(&header, buf, sizeof(header)); + h = &header; + + /* set entry point */ + entry = h->entry_point; + + /* convert the DOL sections into suitable page aligned segments */ + memset(dol_segs, 0, sizeof(dol_segs)); + + load_dol_segments(dol_segs, DOL_MAX_SECT, h); + fix_dol_segments_overlaps(dol_segs, DOL_MAX_SECT); + + /* load rest of segments */ + for (i = 0; i < DOL_MAX_SECT; i++) { + unsigned char *seg_buf; + /* not a segment */ + if (dol_segs[i].size == 0) + continue; + + //print_dol_segment(&dol_segs[i]); + + /* prepare segment */ + seg_buf = xmalloc(dol_segs[i].size); + mstart = dol_segs[i].start; + if (mstart & 0xf0000000) { + /* + * GameCube DOLs expect memory mapped this way: + * + * 80000000 - 817fffff 24MB RAM, cached + * c0000000 - c17fffff 24MB RAM, not cached + * + * kexec, instead, needs physical memory layout, so + * we clear the upper bits of the address. + * (2 bits should be enough, indeed) + */ + mstart &= ~0xf0000000; /* clear bits 0-3, ibm syntax */ + } + add_segment(info, + seg_buf, dol_segs[i].size, + mstart, dol_segs[i].size); + + + /* load sections into segment memory, according to bitmap */ + sects_bitmap = 0; + while (sects_bitmap != dol_segs[i].sects_bitmap) { + unsigned char *sec_buf; + /* find lowest start address for section */ + lowest_start = 0xffffffff; + for (j = -1, k = 0; k < DOL_MAX_SECT; k++) { + /* continue if section is already done */ + if ((sects_bitmap & (1 << k)) != 0) + continue; + /* do nothing for non sections */ + if ((dol_segs[i].sects_bitmap & (1 << k)) == 0) + continue; + /* found new candidate */ + if (dol_sect_address(h, k) < lowest_start) { + lowest_start = dol_sect_address(h, k); + j = k; + } + } + /* mark section as being loaded */ + sects_bitmap |= (1 << j); + + /* read it from file to the right place */ + sec_buf = seg_buf + + (dol_sect_address(h, j) - dol_segs[i].start); + memcpy(sec_buf, buf + dol_sect_offset(h, j), + dol_sect_size(h, j)); + } + } + + /* build the setup glue and argument segment (segment 0) */ + note_bytes = sizeof(elf_boot_notes) + _ALIGN(command_line_len, 4); + arg_bytes = note_bytes + _ALIGN(setup_dol_size, 4); + + arg_buf = xmalloc(arg_bytes); + arg_base = add_buffer(info, + arg_buf, arg_bytes, arg_bytes, 4, 0, 0xFFFFFFFFUL, 1); + + notes = (struct boot_notes *)(arg_buf + _ALIGN(setup_dol_size, 4)); + + notes->hdr.b_size = note_bytes; + notes->cmd_hdr.n_descsz = command_line_len; + notes->hdr.b_checksum = compute_ip_checksum(notes, note_bytes); + + setup_dol_regs.spr8 = entry; /* Link Register */ + + memcpy(arg_buf, setup_dol_start, setup_dol_size); + memcpy(notes, &elf_boot_notes, sizeof(elf_boot_notes)); + memcpy(notes->command_line, command_line, command_line_len); + + if (debug) { + fprintf(stdout, "entry = %p\n", (void *)arg_base); + print_segments(stdout, info); + } + + info->entry = (void *)arg_base; + return 0; +} diff --git a/kexec/arch/ppc/kexec-elf-ppc.c b/kexec/arch/ppc/kexec-elf-ppc.c new file mode 100644 index 0000000..4a4886e --- /dev/null +++ b/kexec/arch/ppc/kexec-elf-ppc.c @@ -0,0 +1,458 @@ +/* + * kexec-elf-ppc.c - kexec Elf loader for the PowerPC + * Copyright (C) 2004 Albert Herranz + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#define _GNU_SOURCE +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <limits.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <getopt.h> +#include <elf.h> +#include <boot/elf_boot.h> +#include <ip_checksum.h> +#include "../../kexec.h" +#include "../../kexec-elf.h" +#include "kexec-ppc.h" +#include <arch/options.h> +#include "../../kexec-syscall.h" +#include "crashdump-powerpc.h" + +#include "config.h" +#include "fixup_dtb.h" + +static const int probe_debug = 0; + +unsigned char reuse_initrd; +int create_flatten_tree(struct kexec_info *, unsigned char **, unsigned long *, + char *); + +#define UPSZ(X) _ALIGN_UP(sizeof(X), 4); +#ifdef WITH_GAMECUBE +static struct boot_notes { + Elf_Bhdr hdr; + Elf_Nhdr bl_hdr; + unsigned char bl_desc[UPSZ(BOOTLOADER)]; + Elf_Nhdr blv_hdr; + unsigned char blv_desc[UPSZ(BOOTLOADER_VERSION)]; + Elf_Nhdr cmd_hdr; + unsigned char command_line[0]; +} elf_boot_notes = { + .hdr = { + .b_signature = 0x0E1FB007, + .b_size = sizeof(elf_boot_notes), + .b_checksum = 0, + .b_records = 3, + }, + .bl_hdr = { + .n_namesz = 0, + .n_descsz = sizeof(BOOTLOADER), + .n_type = EBN_BOOTLOADER_NAME, + }, + .bl_desc = BOOTLOADER, + .blv_hdr = { + .n_namesz = 0, + .n_descsz = sizeof(BOOTLOADER_VERSION), + .n_type = EBN_BOOTLOADER_VERSION, + }, + .blv_desc = BOOTLOADER_VERSION, + .cmd_hdr = { + .n_namesz = 0, + .n_descsz = 0, + .n_type = EBN_COMMAND_LINE, + }, +}; +#endif + +int elf_ppc_probe(const char *buf, off_t len) +{ + + struct mem_ehdr ehdr; + int result; + result = build_elf_exec_info(buf, len, &ehdr, 0); + if (result < 0) { + goto out; + } + + /* Verify the architecuture specific bits */ + if (ehdr.e_machine != EM_PPC) { + /* for a different architecture */ + if (probe_debug) { + fprintf(stderr, "Not for this architecture.\n"); + } + result = -1; + goto out; + } + result = 0; + out: + free_elf_info(&ehdr); + return result; +} + +#ifdef WITH_GAMECUBE +static void gamecube_hack_addresses(struct mem_ehdr *ehdr) +{ + struct mem_phdr *phdr, *phdr_end; + phdr_end = ehdr->e_phdr + ehdr->e_phnum; + for(phdr = ehdr->e_phdr; phdr != phdr_end; phdr++) { + /* + * GameCube ELF kernel is linked with memory mapped + * this way (to easily transform it into a DOL + * suitable for being loaded with psoload): + * + * 80000000 - 817fffff 24MB RAM, cached + * c0000000 - c17fffff 24MB RAM, not cached + * + * kexec, instead, needs physical memory layout, so + * we clear the upper bits of the address. + * (2 bits should be enough, indeed) + */ + phdr->p_paddr &= ~0xf0000000; /* clear bits 0-3, ibm syntax */ + } +} +#endif + +/* See options.h -- add any more there, too. */ +static const struct option options[] = { + KEXEC_ARCH_OPTIONS + {"command-line", 1, 0, OPT_APPEND}, + {"append", 1, 0, OPT_APPEND}, + {"ramdisk", 1, 0, OPT_RAMDISK}, + {"initrd", 1, 0, OPT_RAMDISK}, + {"gamecube", 1, 0, OPT_GAMECUBE}, + {"dtb", 1, 0, OPT_DTB}, + {"reuse-node", 1, 0, OPT_NODES}, + {0, 0, 0, 0}, +}; +static const char short_options[] = KEXEC_ARCH_OPT_STR; + +void elf_ppc_usage(void) +{ + printf( + " --command-line=STRING Set the kernel command line to STRING.\n" + " --append=STRING Set the kernel command line to STRING.\n" + " --ramdisk=<filename> Initial RAM disk.\n" + " --initrd=<filename> same as --ramdisk\n" + " --gamecube=1|0 Enable/disable support for ELFs with changed\n" + " addresses suitable for the GameCube.\n" + " --dtb=<filename> Specify device tree blob file.\n" + " --reuse-node=node Specify nodes which should be taken from /proc/device-tree.\n" + " Can be set multiple times.\n" + ); +} + +int elf_ppc_load(int argc, char **argv, const char *buf, off_t len, + struct kexec_info *info) +{ + struct mem_ehdr ehdr; + char *command_line, *crash_cmdline, *cmdline_buf; + char *tmp_cmdline; + int command_line_len, crash_cmdline_len; + char *dtb; + int result; + char *error_msg; + unsigned long max_addr, hole_addr; + struct mem_phdr *phdr; + size_t size; +#ifdef CONFIG_PPC64 + unsigned long toc_addr; +#endif +#ifdef WITH_GAMECUBE + int target_is_gamecube = 1; + char *arg_buf; + size_t arg_bytes; + unsigned long arg_base; + struct boot_notes *notes; + size_t note_bytes; + unsigned char *setup_start; + uint32_t setup_size; +#else + char *seg_buf = NULL; + off_t seg_size = 0; + int target_is_gamecube = 0; + unsigned int addr; + unsigned long dtb_addr; + unsigned long dtb_addr_actual; +#endif + unsigned long kernel_addr; +#define FIXUP_ENTRYS (20) + char *fixup_nodes[FIXUP_ENTRYS + 1]; + int cur_fixup = 0; + int opt; + char *blob_buf = NULL; + off_t blob_size = 0; + + command_line = tmp_cmdline = NULL; + dtb = NULL; + max_addr = LONG_MAX; + hole_addr = 0; + kernel_addr = 0; + ramdisk = 0; + result = 0; + error_msg = NULL; + + while ((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) { + switch (opt) { + default: + /* Ignore core options */ + if (opt < OPT_ARCH_MAX) { + break; + } + case OPT_APPEND: + tmp_cmdline = optarg; + break; + case OPT_RAMDISK: + ramdisk = optarg; + break; + case OPT_GAMECUBE: + target_is_gamecube = atoi(optarg); + break; + + case OPT_DTB: + dtb = optarg; + break; + + case OPT_NODES: + if (cur_fixup >= FIXUP_ENTRYS) { + die("The number of entries for the fixup is too large\n"); + } + fixup_nodes[cur_fixup] = optarg; + cur_fixup++; + break; + } + } + + if (ramdisk && reuse_initrd) + die("Can't specify --ramdisk or --initrd with --reuseinitrd\n"); + + command_line_len = 0; + if (tmp_cmdline) { + command_line = tmp_cmdline; + } else { + command_line = get_command_line(); + } + command_line_len = strlen(command_line); + + fixup_nodes[cur_fixup] = NULL; + + /* Parse the Elf file */ + result = build_elf_exec_info(buf, len, &ehdr, 0); + if (result < 0) { + goto out; + } + +#ifdef WITH_GAMECUBE + if (target_is_gamecube) { + gamecube_hack_addresses(&ehdr); + } +#endif + + /* Load the Elf data. Physical load addresses in elf64 header do not + * show up correctly. Use user supplied address for now to patch the + * elf header + */ + + phdr = &ehdr.e_phdr[0]; + size = phdr->p_filesz; + if (size > phdr->p_memsz) + size = phdr->p_memsz; + + kernel_addr = locate_hole(info, size, 0, 0, max_addr, 1); +#ifdef CONFIG_PPC64 + ehdr.e_phdr[0].p_paddr = (Elf64_Addr)kernel_addr; +#else + ehdr.e_phdr[0].p_paddr = kernel_addr; +#endif + + /* Load the Elf data */ + result = elf_exec_load(&ehdr, info); + if (result < 0) { + goto out; + } + + /* + * Need to append some command line parameters internally in case of + * taking crash dumps. Additional segments need to be created. + */ + if (info->kexec_flags & KEXEC_ON_CRASH) { + crash_cmdline = xmalloc(COMMAND_LINE_SIZE); + memset((void *)crash_cmdline, 0, COMMAND_LINE_SIZE); + result = load_crashdump_segments(info, crash_cmdline, + max_addr, 0); + if (result < 0) { + result = -1; + goto out; + } + crash_cmdline_len = strlen(crash_cmdline); + } else { + crash_cmdline = NULL; + crash_cmdline_len = 0; + } + + /* + * In case of a toy we take the hardcoded things and an easy setup via + * one of the assembly startups. Every thing else should be grown up + * and go through the purgatory. + */ +#ifdef WITH_GAMECUBE + if (target_is_gamecube) { + setup_start = setup_dol_start; + setup_size = setup_dol_size; + setup_dol_regs.spr8 = ehdr.e_entry; /* Link Register */ + } else { + setup_start = setup_simple_start; + setup_size = setup_simple_size; + setup_simple_regs.spr8 = ehdr.e_entry; /* Link Register */ + } + note_bytes = sizeof(elf_boot_notes) + _ALIGN(command_line_len, 4); + arg_bytes = note_bytes + _ALIGN(setup_size, 4); + + arg_buf = xmalloc(arg_bytes); + arg_base = add_buffer(info, + arg_buf, arg_bytes, arg_bytes, 4, 0, elf_max_addr(&ehdr), 1); + + notes = (struct boot_notes *)(arg_buf + _ALIGN(setup_size, 4)); + + memcpy(arg_buf, setup_start, setup_size); + memcpy(notes, &elf_boot_notes, sizeof(elf_boot_notes)); + memcpy(notes->command_line, command_line, command_line_len); + notes->hdr.b_size = note_bytes; + notes->cmd_hdr.n_descsz = command_line_len; + notes->hdr.b_checksum = compute_ip_checksum(notes, note_bytes); + + info->entry = (void *)arg_base; +#else + if (crash_cmdline_len + command_line_len + 1 > COMMAND_LINE_SIZE) { + printf("Kernel command line exceeds size\n"); + return -1; + } + + cmdline_buf = xmalloc(COMMAND_LINE_SIZE); + memset((void *)cmdline_buf, 0, COMMAND_LINE_SIZE); + if (command_line) + strncat(cmdline_buf, command_line, command_line_len); + if (crash_cmdline) + strncat(cmdline_buf, crash_cmdline, + sizeof(crash_cmdline) - + strlen(crash_cmdline) - 1); + + elf_rel_build_load(info, &info->rhdr, (const char *)purgatory, + purgatory_size, 0, elf_max_addr(&ehdr), 1, 0); + + /* Here we need to initialize the device tree, and find out where + * it is going to live so we can place it directly after the + * kernel image */ + if (dtb) { + /* Grab device tree from buffer */ + blob_buf = slurp_file(dtb, &blob_size); + } else { + create_flatten_tree(info, (unsigned char **)&blob_buf, + (unsigned long *)&blob_size, cmdline_buf); + } + if (!blob_buf || !blob_size) { + error_msg = "Device tree seems to be an empty file.\n"; + goto out2; + } + + /* initial fixup for device tree */ + blob_buf = fixup_dtb_init(info, blob_buf, &blob_size, kernel_addr, &dtb_addr); + + if (ramdisk) { + seg_buf = slurp_ramdisk_ppc(ramdisk, &seg_size); + /* load the ramdisk *above* the device tree */ + hole_addr = add_buffer(info, seg_buf, seg_size, seg_size, + 0, dtb_addr + blob_size + 1, max_addr, -1); + ramdisk_base = hole_addr; + ramdisk_size = seg_size; + } + if (reuse_initrd) { + ramdisk_base = initrd_base; + ramdisk_size = initrd_size; + } + + if (info->kexec_flags & KEXEC_ON_CRASH && ramdisk_base != 0) { + if ( (ramdisk_base < crash_base) || + (ramdisk_base > crash_base + crash_size) ) { + printf("WARNING: ramdisk is above crashkernel region!\n"); + } + else if (ramdisk_base + ramdisk_size > crash_base + crash_size) { + printf("WARNING: ramdisk overflows crashkernel region!\n"); + } + } + + /* Perform final fixup on devie tree, i.e. everything beside what + * was done above */ + fixup_dtb_finalize(info, blob_buf, &blob_size, fixup_nodes, + cmdline_buf); + dtb_addr_actual = add_buffer(info, blob_buf, blob_size, blob_size, 0, dtb_addr, + kernel_addr + KERNEL_ACCESS_TOP, 1); + if (dtb_addr_actual != dtb_addr) { + error_msg = "Error device tree not loadded to address it was expecting to be loaded too!\n"; + goto out2; + } + + /* + * set various variables for the purgatory. + * ehdr.e_entry is a virtual address. we know physical start + * address of the kernel (kernel_addr). Find the offset of + * e_entry from the virtual start address(e_phdr[0].p_vaddr) + * and calculate the actual physical address of the 'kernel entry'. + */ + addr = kernel_addr + (ehdr.e_entry - ehdr.e_phdr[0].p_vaddr); + elf_rel_set_symbol(&info->rhdr, "kernel", &addr, sizeof(addr)); + + addr = dtb_addr; + elf_rel_set_symbol(&info->rhdr, "dt_offset", + &addr, sizeof(addr)); + +#define PUL_STACK_SIZE (16 * 1024) + addr = locate_hole(info, PUL_STACK_SIZE, 0, 0, + elf_max_addr(&ehdr), 1); + addr += PUL_STACK_SIZE; + elf_rel_set_symbol(&info->rhdr, "stack", &addr, sizeof(addr)); +#undef PUL_STACK_SIZE + + /* + * Fixup ThreadPointer(r2) for purgatory. + * PPC32 ELF ABI expects : + * ThreadPointer (TP) = TCB + 0x7000 + * We manually allocate a TCB space and set the TP + * accordingly. + */ +#define TCB_SIZE 1024 +#define TCB_TP_OFFSET 0x7000 /* PPC32 ELF ABI */ + + addr = locate_hole(info, TCB_SIZE, 0, 0, + ((unsigned long)elf_max_addr(&ehdr) - TCB_TP_OFFSET), + 1); + addr += TCB_SIZE + TCB_TP_OFFSET; + elf_rel_set_symbol(&info->rhdr, "my_thread_ptr", &addr, sizeof(addr)); + +#undef TCB_SIZE +#undef TCB_TP_OFFSET + + addr = elf_rel_get_addr(&info->rhdr, "purgatory_start"); + info->entry = (void *)addr; + +out2: + free(cmdline_buf); +#endif +out: + free_elf_info(&ehdr); + free(crash_cmdline); + if (!tmp_cmdline) + free(command_line); + if (error_msg) + die("%s", error_msg); + + return result; +} diff --git a/kexec/arch/ppc/kexec-elf-rel-ppc.c b/kexec/arch/ppc/kexec-elf-rel-ppc.c new file mode 100644 index 0000000..1acbd86 --- /dev/null +++ b/kexec/arch/ppc/kexec-elf-rel-ppc.c @@ -0,0 +1,69 @@ +#include <stdio.h> +#include <elf.h> +#include "../../kexec.h" +#include "../../kexec-elf.h" + +int machine_verify_elf_rel(struct mem_ehdr *ehdr) +{ + if (ehdr->ei_data != ELFDATA2MSB) { + return 0; + } + if (ehdr->ei_class != ELFCLASS32) { + return 0; + } + if (ehdr->e_machine != EM_PPC) { + return 0; + } + return 1; +} + +void machine_apply_elf_rel(struct mem_ehdr *UNUSED(ehdr), + struct mem_sym *UNUSED(sym), unsigned long r_type, void *location, + unsigned long address, unsigned long value) +{ + switch(r_type) { + case R_PPC_ADDR32: + /* Simply set it */ + *(uint32_t *)location = value; + break; + + case R_PPC_ADDR16_LO: + /* Low half of the symbol */ + *(uint16_t *)location = value; + break; + + case R_PPC_ADDR16_HI: + *(uint16_t *)location = (value>>16) & 0xffff; + break; + + case R_PPC_ADDR16_HA: + /* Sign-adjusted lower 16 bits: PPC ELF ABI says: + (((x >> 16) + ((x & 0x8000) ? 1 : 0))) & 0xFFFF. + This is the same, only sane. + */ + *(uint16_t *)location = (value + 0x8000) >> 16; + break; + + case R_PPC_REL24: + if ((int)(value - address) < -0x02000000 + || (int)(value - address) >= 0x02000000) + { + die("Symbol more than 16MiB away"); + } + /* Only replace bits 2 through 26 */ + *(uint32_t *)location + = (*(uint32_t *)location & ~0x03fffffc) + | ((value - address) + & 0x03fffffc); + break; + + case R_PPC_REL32: + /* 32-bit relative jump. */ + *(uint32_t *)location = value - address; + break; + default: + die("Unknown rela relocation: %lu\n", r_type); + break; + } + return; +} diff --git a/kexec/arch/ppc/kexec-ppc.c b/kexec/arch/ppc/kexec-ppc.c new file mode 100644 index 0000000..03bec36 --- /dev/null +++ b/kexec/arch/ppc/kexec-ppc.c @@ -0,0 +1,968 @@ +/* + * kexec-ppc.c - kexec for the PowerPC + * Copyright (C) 2004, 2005 Albert Herranz + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#include <stddef.h> +#include <stdio.h> +#include <errno.h> +#include <stdint.h> +#include <string.h> +#include <getopt.h> +#include <sys/types.h> +#include <dirent.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> + +#include "../../kexec.h" +#include "../../kexec-syscall.h" +#include "kexec-ppc.h" +#include "crashdump-powerpc.h" +#include <arch/options.h> + +#include "config.h" + +unsigned long dt_address_cells = 0, dt_size_cells = 0; +uint64_t rmo_top; +uint64_t memory_limit; +unsigned long long crash_base = 0, crash_size = 0; +unsigned long long initrd_base = 0, initrd_size = 0; +unsigned long long ramdisk_base = 0, ramdisk_size = 0; +unsigned int rtas_base, rtas_size; +int max_memory_ranges; +const char *ramdisk; + +/* + * Reads the #address-cells and #size-cells on this platform. + * This is used to parse the memory/reg info from the device-tree + */ +int init_memory_region_info() +{ + size_t res = 0; + int fd; + char *file; + + file = "/proc/device-tree/#address-cells"; + fd = open(file, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "Unable to open %s\n", file); + return -1; + } + + res = read(fd, &dt_address_cells, sizeof(dt_address_cells)); + if (res != sizeof(dt_address_cells)) { + fprintf(stderr, "Error reading %s\n", file); + return -1; + } + close(fd); + + file = "/proc/device-tree/#size-cells"; + fd = open(file, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "Unable to open %s\n", file); + return -1; + } + + res = read(fd, &dt_size_cells, sizeof(dt_size_cells)); + if (res != sizeof(dt_size_cells)) { + fprintf(stderr, "Error reading %s\n", file); + return -1; + } + close(fd); + + /* Convert the sizes into bytes */ + dt_size_cells *= sizeof(unsigned long); + dt_address_cells *= sizeof(unsigned long); + + return 0; +} + +#define MAXBYTES 128 +/* + * Reads the memory region info from the device-tree node pointed + * by @fd and fills the *start, *end with the boundaries of the region + */ +int read_memory_region_limits(int fd, unsigned long long *start, + unsigned long long *end) +{ + char buf[MAXBYTES]; + unsigned long *p; + unsigned long nbytes = dt_address_cells + dt_size_cells; + + if (lseek(fd, 0, SEEK_SET) == -1) { + fprintf(stderr, "Error in file seek\n"); + return -1; + } + if (read(fd, buf, nbytes) != nbytes) { + fprintf(stderr, "Error reading the memory region info\n"); + return -1; + } + + p = (unsigned long*)buf; + if (dt_address_cells == sizeof(unsigned long)) { + *start = p[0]; + p++; + } else if (dt_address_cells == sizeof(unsigned long long)) { + *start = ((unsigned long long *)p)[0]; + p = (unsigned long long *)p + 1; + } else { + fprintf(stderr, "Unsupported value for #address-cells : %ld\n", + dt_address_cells); + return -1; + } + + if (dt_size_cells == sizeof(unsigned long)) + *end = *start + p[0]; + else if (dt_size_cells == sizeof(unsigned long long)) + *end = *start + ((unsigned long long *)p)[0]; + else { + fprintf(stderr, "Unsupported value for #size-cells : %ld\n", + dt_size_cells); + return -1; + } + + return 0; +} + +void arch_reuse_initrd(void) +{ + reuse_initrd = 1; +} + +#ifdef WITH_GAMECUBE +#define MAX_MEMORY_RANGES 64 +static struct memory_range memory_range[MAX_MEMORY_RANGES]; + +static int get_memory_ranges_gc(struct memory_range **range, int *ranges, + unsigned long UNUSED(kexec_flags)) +{ + int memory_ranges = 0; + + /* RAM - lowmem used by DOLs - framebuffer */ + memory_range[memory_ranges].start = 0x00003000; + memory_range[memory_ranges].end = 0x0174bfff; + memory_range[memory_ranges].type = RANGE_RAM; + memory_ranges++; + *range = memory_range; + *ranges = memory_ranges; + return 0; +} +#else +static int use_new_dtb; +static int nr_memory_ranges, nr_exclude_ranges; +static struct memory_range *exclude_range; +static struct memory_range *memory_range; +static struct memory_range *base_memory_range; +static uint64_t memory_max; + +/* + * Count the memory nodes under /proc/device-tree and populate the + * max_memory_ranges variable. This variable replaces MAX_MEMORY_RANGES + * macro used earlier. + */ +static int count_memory_ranges(void) +{ + char device_tree[256] = "/proc/device-tree/"; + struct dirent *dentry; + DIR *dir; + + if ((dir = opendir(device_tree)) == NULL) { + perror(device_tree); + return -1; + } + + while ((dentry = readdir(dir)) != NULL) { + if (strncmp(dentry->d_name, "memory@", 7) && + strcmp(dentry->d_name, "memory")) + continue; + max_memory_ranges++; + } + + /* need to add extra region for retained initrd */ + if (use_new_dtb) { + max_memory_ranges++; + } + + closedir(dir); + return 0; + +} + + static void cleanup_memory_ranges(void) + { + free(memory_range); + free(base_memory_range); + free(exclude_range); + } + +/* + * Allocate memory for various data structures used to hold + * values of different memory ranges + */ +static int alloc_memory_ranges(void) +{ + int memory_range_len; + + memory_range_len = sizeof(struct memory_range) * max_memory_ranges; + + memory_range = malloc(memory_range_len); + if (!memory_range) + return -1; + + base_memory_range = malloc(memory_range_len); + if (!base_memory_range) + goto err1; + + exclude_range = malloc(memory_range_len); + if (!exclude_range) + goto err1; + + memset(memory_range, 0, memory_range_len); + memset(base_memory_range, 0, memory_range_len); + memset(exclude_range, 0, memory_range_len); + return 0; + +err1: + fprintf(stderr, "memory range structure allocation failure\n"); + cleanup_memory_ranges(); + return -1; +} + +/* Sort the exclude ranges in memory */ +static int sort_ranges(void) +{ + int i, j; + uint64_t tstart, tend; + for (i = 0; i < nr_exclude_ranges - 1; i++) { + for (j = 0; j < nr_exclude_ranges - i - 1; j++) { + if (exclude_range[j].start > exclude_range[j+1].start) { + tstart = exclude_range[j].start; + tend = exclude_range[j].end; + exclude_range[j].start = exclude_range[j+1].start; + exclude_range[j].end = exclude_range[j+1].end; + exclude_range[j+1].start = tstart; + exclude_range[j+1].end = tend; + } + } + } + return 0; +} + +/* Sort the base ranges in memory - this is useful for ensuring that our + * ranges are in ascending order, even if device-tree read of memory nodes + * is done differently. Also, could be used for other range coalescing later + */ +static int sort_base_ranges(void) +{ + int i, j; + unsigned long long tstart, tend; + + for (i = 0; i < nr_memory_ranges - 1; i++) { + for (j = 0; j < nr_memory_ranges - i - 1; j++) { + if (base_memory_range[j].start > base_memory_range[j+1].start) { + tstart = base_memory_range[j].start; + tend = base_memory_range[j].end; + base_memory_range[j].start = base_memory_range[j+1].start; + base_memory_range[j].end = base_memory_range[j+1].end; + base_memory_range[j+1].start = tstart; + base_memory_range[j+1].end = tend; + } + } + } + return 0; +} + +static int realloc_memory_ranges(void) +{ + size_t memory_range_len; + + max_memory_ranges++; + memory_range_len = sizeof(struct memory_range) * max_memory_ranges; + + memory_range = (struct memory_range *) realloc(memory_range, + memory_range_len); + if (!memory_range) + goto err; + + base_memory_range = (struct memory_range *) realloc(base_memory_range, + memory_range_len); + if (!base_memory_range) + goto err; + + exclude_range = (struct memory_range *) realloc(exclude_range, + memory_range_len); + if (!exclude_range) + goto err; + + usablemem_rgns.ranges = (struct memory_range *) + realloc(usablemem_rgns.ranges, + memory_range_len); + if (!(usablemem_rgns.ranges)) + goto err; + + return 0; + +err: + fprintf(stderr, "memory range structure re-allocation failure\n"); + return -1; +} + +/* Get base memory ranges */ +static int get_base_ranges(void) +{ + int local_memory_ranges = 0; + char device_tree[256] = "/proc/device-tree/"; + char fname[256]; + char buf[MAXBYTES]; + DIR *dir, *dmem; + struct dirent *dentry, *mentry; + int n, fd; + + if ((dir = opendir(device_tree)) == NULL) { + perror(device_tree); + return -1; + } + while ((dentry = readdir(dir)) != NULL) { + if (strncmp(dentry->d_name, "memory@", 7) && + strcmp(dentry->d_name, "memory")) + continue; + strcpy(fname, device_tree); + strcat(fname, dentry->d_name); + if ((dmem = opendir(fname)) == NULL) { + perror(fname); + closedir(dir); + return -1; + } + while ((mentry = readdir(dmem)) != NULL) { + unsigned long long start, end; + + if (strcmp(mentry->d_name, "reg")) + continue; + strcat(fname, "/reg"); + if ((fd = open(fname, O_RDONLY)) < 0) { + perror(fname); + closedir(dmem); + closedir(dir); + return -1; + } + if (read_memory_region_limits(fd, &start, &end) != 0) { + close(fd); + closedir(dmem); + closedir(dir); + return -1; + } + if (local_memory_ranges >= max_memory_ranges) { + if (realloc_memory_ranges() < 0){ + close(fd); + break; + } + } + + base_memory_range[local_memory_ranges].start = start; + base_memory_range[local_memory_ranges].end = end; + base_memory_range[local_memory_ranges].type = RANGE_RAM; + local_memory_ranges++; + dbgprintf("%016llx-%016llx : %x\n", + base_memory_range[local_memory_ranges-1].start, + base_memory_range[local_memory_ranges-1].end, + base_memory_range[local_memory_ranges-1].type); + close(fd); + } + closedir(dmem); + } + closedir(dir); + nr_memory_ranges = local_memory_ranges; + sort_base_ranges(); + memory_max = base_memory_range[nr_memory_ranges - 1].end; + + dbgprintf("get base memory ranges:%d\n", nr_memory_ranges); + + return 0; +} + +static int read_kernel_memory_limit(char *fname, char *buf) +{ + FILE *file; + int n; + + if (!fname || !buf) + return -1; + + file = fopen(fname, "r"); + if (file == NULL) { + if (errno != ENOENT) { + perror(fname); + return -1; + } + errno = 0; + /* + * fall through. On older kernel this file + * is not present. Hence return success. + */ + } else { + /* Memory limit property is of u64 type. */ + if ((n = fread(&memory_limit, 1, sizeof(uint64_t), file)) < 0) { + perror(fname); + goto err_out; + } + if (n != sizeof(uint64_t)) { + fprintf(stderr, "%s node has invalid size: %d\n", + fname, n); + goto err_out; + } + fclose(file); + } + return 0; +err_out: + fclose(file); + return -1; +} + +/* Return 0 if fname/value valid, -1 otherwise */ +int get_devtree_value(const char *fname, unsigned long long *value) +{ + FILE *file; + char buf[MAXBYTES]; + int n = -1; + + if ((file = fopen(fname, "r"))) { + n = fread(buf, 1, MAXBYTES, file); + fclose(file); + } + + if (n == sizeof(uint32_t)) + *value = ((uint32_t *)buf)[0]; + else if (n == sizeof(uint64_t)) + *value = ((uint64_t *)buf)[0]; + else { + fprintf(stderr, "%s node has invalid size: %d\n", fname, n); + return -1; + } + + return 0; +} + +/* Get devtree details and create exclude_range array + * Also create usablemem_ranges for KEXEC_ON_CRASH + */ +static int get_devtree_details(unsigned long kexec_flags) +{ + uint64_t rmo_base; + unsigned long long tce_base; + unsigned int tce_size; + unsigned long long htab_base, htab_size; + unsigned long long kernel_end; + unsigned long long initrd_start, initrd_end; + char buf[MAXBYTES]; + char device_tree[256] = "/proc/device-tree/"; + char fname[256]; + DIR *dir, *cdir; + FILE *file; + struct dirent *dentry; + int n, i = 0; + + if ((dir = opendir(device_tree)) == NULL) { + perror(device_tree); + return -1; + } + + while ((dentry = readdir(dir)) != NULL) { + if (strncmp(dentry->d_name, "chosen", 6) && + strncmp(dentry->d_name, "memory@", 7) && + strncmp(dentry->d_name, "memory", 6) && + strncmp(dentry->d_name, "pci@", 4) && + strncmp(dentry->d_name, "rtas", 4)) + continue; + strcpy(fname, device_tree); + strcat(fname, dentry->d_name); + if ((cdir = opendir(fname)) == NULL) { + perror(fname); + goto error_opendir; + } + + if (strncmp(dentry->d_name, "chosen", 6) == 0) { + /* only reserve kernel region if we are doing a crash kernel */ + if (kexec_flags & KEXEC_ON_CRASH) { + strcat(fname, "/linux,kernel-end"); + file = fopen(fname, "r"); + if (!file) { + perror(fname); + goto error_opencdir; + } + if ((n = fread(buf, 1, MAXBYTES, file)) < 0) { + perror(fname); + goto error_openfile; + } + if (n == sizeof(uint32_t)) { + kernel_end = ((uint32_t *)buf)[0]; + } else if (n == sizeof(uint64_t)) { + kernel_end = ((uint64_t *)buf)[0]; + } else { + fprintf(stderr, "%s node has invalid size: %d\n", fname, n); + goto error_openfile; + } + fclose(file); + + /* Add kernel memory to exclude_range */ + exclude_range[i].start = 0x0UL; + exclude_range[i].end = kernel_end; + i++; + if (i >= max_memory_ranges) + realloc_memory_ranges(); + memset(fname, 0, sizeof(fname)); + sprintf(fname, "%s%s%s", + device_tree, dentry->d_name, + "/linux,crashkernel-base"); + file = fopen(fname, "r"); + if (!file) { + perror(fname); + goto error_opencdir; + } + if ((n = fread(buf, 1, MAXBYTES, file)) < 0) { + perror(fname); + goto error_openfile; + } + if (n == sizeof(uint32_t)) { + crash_base = ((uint32_t *)buf)[0]; + } else if (n == sizeof(uint64_t)) { + crash_base = ((uint64_t *)buf)[0]; + } else { + fprintf(stderr, "%s node has invalid size: %d\n", fname, n); + goto error_openfile; + } + fclose(file); + + memset(fname, 0, sizeof(fname)); + sprintf(fname, "%s%s%s", + device_tree, dentry->d_name, + "/linux,crashkernel-size"); + file = fopen(fname, "r"); + if (!file) { + perror(fname); + goto error_opencdir; + } + if ((n = fread(buf, 1, MAXBYTES, file)) < 0) { + perror(fname); + goto error_openfile; + } + if (n == sizeof(uint32_t)) { + crash_size = ((uint32_t *)buf)[0]; + } else if (n == sizeof(uint64_t)) { + crash_size = ((uint64_t *)buf)[0]; + } else { + fprintf(stderr, "%s node has invalid size: %d\n", fname, n); + goto error_openfile; + } + fclose(file); + + if (crash_base > mem_min) + mem_min = crash_base; + if (crash_base + crash_size < mem_max) + mem_max = crash_base + crash_size; + +#ifndef CONFIG_BOOKE + add_usable_mem_rgns(0, crash_base + crash_size); + /* Reserve the region (KDUMP_BACKUP_LIMIT,crash_base) */ + reserve(KDUMP_BACKUP_LIMIT, + crash_base-KDUMP_BACKUP_LIMIT); +#else + add_usable_mem_rgns(crash_base, crash_size); +#endif + } + /* + * Read the first kernel's memory limit. + * If the first kernel is booted with mem= option then + * it would export "linux,memory-limit" file + * reflecting value for the same. + */ + memset(fname, 0, sizeof(fname)); + snprintf(fname, sizeof(fname), "%s%s%s", device_tree, + dentry->d_name, "/linux,memory-limit"); + if (read_kernel_memory_limit(fname, buf) < 0) + goto error_opencdir; + + /* reserve the initrd_start and end locations. */ + memset(fname, 0, sizeof(fname)); + sprintf(fname, "%s%s%s", + device_tree, dentry->d_name, + "/linux,initrd-start"); + file = fopen(fname, "r"); + if (!file) { + errno = 0; + initrd_start = 0; + } else { + if ((n = fread(buf, 1, MAXBYTES, file)) < 0) { + perror(fname); + goto error_openfile; + } + if (n == sizeof(uint32_t)) { + initrd_start = ((uint32_t *)buf)[0]; + } else if (n == sizeof(uint64_t)) { + initrd_start = ((uint64_t *)buf)[0]; + } else { + fprintf(stderr, "%s node has invalid size: %d\n", fname, n); + goto error_openfile; + } + fclose(file); + } + + memset(fname, 0, sizeof(fname)); + sprintf(fname, "%s%s%s", + device_tree, dentry->d_name, + "/linux,initrd-end"); + file = fopen(fname, "r"); + if (!file) { + errno = 0; + initrd_end = 0; + } else { + if ((n = fread(buf, 1, MAXBYTES, file)) < 0) { + perror(fname); + goto error_openfile; + } + if (n == sizeof(uint32_t)) { + initrd_end = ((uint32_t *)buf)[0]; + } else if (n == sizeof(uint64_t)) { + initrd_end = ((uint64_t *)buf)[0]; + } else { + fprintf(stderr, "%s node has invalid size: %d\n", fname, n); + goto error_openfile; + } + fclose(file); + } + + if ((initrd_end - initrd_start) != 0 ) { + initrd_base = initrd_start; + initrd_size = initrd_end - initrd_start; + } + + if (reuse_initrd) { + /* Add initrd address to exclude_range */ + exclude_range[i].start = initrd_start; + exclude_range[i].end = initrd_end; + i++; + if (i >= max_memory_ranges) + realloc_memory_ranges(); + } + + /* HTAB */ + memset(fname, 0, sizeof(fname)); + sprintf(fname, "%s%s%s", + device_tree, dentry->d_name, + "/linux,htab-base"); + file = fopen(fname, "r"); + if (!file) { + closedir(cdir); + if (errno == ENOENT) { + /* Non LPAR */ + errno = 0; + continue; + } + perror(fname); + goto error_opendir; + } + if (fread(&htab_base, sizeof(unsigned long), 1, file) + != 1) { + perror(fname); + goto error_openfile; + } + memset(fname, 0, sizeof(fname)); + sprintf(fname, "%s%s%s", + device_tree, dentry->d_name, + "/linux,htab-size"); + file = fopen(fname, "r"); + if (!file) { + perror(fname); + goto error_opencdir; + } + if (fread(&htab_size, sizeof(unsigned long), 1, file) + != 1) { + perror(fname); + goto error_openfile; + } + /* Add htab address to exclude_range - NON-LPAR only */ + exclude_range[i].start = htab_base; + exclude_range[i].end = htab_base + htab_size; + i++; + if (i >= max_memory_ranges) + realloc_memory_ranges(); + + } /* chosen */ + if (strncmp(dentry->d_name, "rtas", 4) == 0) { + strcat(fname, "/linux,rtas-base"); + if ((file = fopen(fname, "r")) == NULL) { + perror(fname); + goto error_opencdir; + } + if (fread(&rtas_base, sizeof(unsigned int), 1, file) + != 1) { + perror(fname); + goto error_openfile; + } + memset(fname, 0, sizeof(fname)); + sprintf(fname, "%s%s%s", + device_tree, dentry->d_name, + "/linux,rtas-size"); + if ((file = fopen(fname, "r")) == NULL) { + perror(fname); + goto error_opencdir; + } + if (fread(&rtas_size, sizeof(unsigned int), 1, file) + != 1) { + perror(fname); + goto error_openfile; + } + closedir(cdir); + /* Add rtas to exclude_range */ + exclude_range[i].start = rtas_base; + exclude_range[i].end = rtas_base + rtas_size; + i++; + if (kexec_flags & KEXEC_ON_CRASH) + add_usable_mem_rgns(rtas_base, rtas_size); + } /* rtas */ + + if (!strncmp(dentry->d_name, "memory@", 7) || + !strcmp(dentry->d_name, "memory")) { + int fd; + strcat(fname, "/reg"); + if ((fd = open(fname, O_RDONLY)) < 0) { + perror(fname); + goto error_opencdir; + } + if (read_memory_region_limits(fd, &rmo_base, &rmo_top) != 0) + goto error_openfile; + + if (rmo_top > 0x30000000UL) + rmo_top = 0x30000000UL; + + close(fd); + closedir(cdir); + } /* memory */ + + if (strncmp(dentry->d_name, "pci@", 4) == 0) { + strcat(fname, "/linux,tce-base"); + file = fopen(fname, "r"); + if (!file) { + closedir(cdir); + if (errno == ENOENT) { + /* Non LPAR */ + errno = 0; + continue; + } + perror(fname); + goto error_opendir; + } + if (fread(&tce_base, sizeof(unsigned long), 1, file) + != 1) { + perror(fname); + goto error_openfile; + } + memset(fname, 0, sizeof(fname)); + sprintf(fname, "%s%s%s", + device_tree, dentry->d_name, + "/linux,tce-size"); + file = fopen(fname, "r"); + if (!file) { + perror(fname); + goto error_opencdir; + } + if (fread(&tce_size, sizeof(unsigned int), 1, file) + != 1) { + perror(fname); + goto error_openfile; + } + /* Add tce to exclude_range - NON-LPAR only */ + exclude_range[i].start = tce_base; + exclude_range[i].end = tce_base + tce_size; + i++; + if (kexec_flags & KEXEC_ON_CRASH) + add_usable_mem_rgns(tce_base, tce_size); + closedir(cdir); + } /* pci */ + } + closedir(dir); + + nr_exclude_ranges = i; + + sort_ranges(); + + + int k; + for (k = 0; k < i; k++) + dbgprintf("exclude_range sorted exclude_range[%d] " + "start:%llx, end:%llx\n", k, exclude_range[k].start, + exclude_range[k].end); + + return 0; + +error_openfile: + fclose(file); +error_opencdir: + closedir(cdir); +error_opendir: + closedir(dir); + return -1; +} + + +/* Setup a sorted list of memory ranges. */ +static int setup_memory_ranges(unsigned long kexec_flags) +{ + int i, j = 0; + + /* Get the base list of memory ranges from /proc/device-tree/memory + * nodes. Build list of ranges to be excluded from valid memory + */ + + if (get_base_ranges()) + goto out; + if (get_devtree_details(kexec_flags)) + goto out; + + for (i = 0; i < nr_exclude_ranges; i++) { + /* If first exclude range does not start with 0, include the + * first hole of valid memory from 0 - exclude_range[0].start + */ + if (i == 0) { + if (exclude_range[i].start != 0) { + memory_range[j].start = 0; + memory_range[j].end = exclude_range[i].start - 1; + memory_range[j].type = RANGE_RAM; + j++; + } + } /* i == 0 */ + /* If the last exclude range does not end at memory_max, include + * the last hole of valid memory from exclude_range[last].end - + * memory_max + */ + if (i == nr_exclude_ranges - 1) { + if (exclude_range[i].end < memory_max) { + memory_range[j].start = exclude_range[i].end + 1; + memory_range[j].end = memory_max; + memory_range[j].type = RANGE_RAM; + j++; + /* Limit the end to rmo_top */ + if (memory_range[j-1].start >= rmo_top) { + j--; + break; + } + if ((memory_range[j-1].start < rmo_top) && + (memory_range[j-1].end >= rmo_top)) { + memory_range[j-1].end = rmo_top; + break; + } + continue; + } + } /* i == nr_exclude_ranges - 1 */ + /* contiguous exclude ranges - skip */ + if (exclude_range[i+1].start == exclude_range[i].end + 1) + continue; + memory_range[j].start = exclude_range[i].end + 1; + memory_range[j].end = exclude_range[i+1].start - 1; + memory_range[j].type = RANGE_RAM; + j++; + /* Limit range to rmo_top */ + if (memory_range[j-1].start >= rmo_top) { + j--; + break; + } + if ((memory_range[j-1].start < rmo_top) && + (memory_range[j-1].end >= rmo_top)) { + memory_range[j-1].end = rmo_top; + break; + } + } + + /* fixup in case we have no exclude regions */ + if (!j) { + memory_range[0].start = base_memory_range[0].start; + memory_range[0].end = rmo_top; + memory_range[0].type = RANGE_RAM; + nr_memory_ranges = 1; + } else + nr_memory_ranges = j; + + + int k; + for (k = 0; k < j; k++) + dbgprintf("setup_memory_ranges memory_range[%d] " + "start:%llx, end:%llx\n", k, memory_range[k].start, + memory_range[k].end); + return 0; + +out: + cleanup_memory_ranges(); + return -1; +} + + +/* Return a list of valid memory ranges */ +int get_memory_ranges_dt(struct memory_range **range, int *ranges, + unsigned long kexec_flags) +{ + if (count_memory_ranges()) + return -1; + if (alloc_memory_ranges()) + return -1; + if (setup_memory_ranges(kexec_flags)) + return -1; + + *range = memory_range; + *ranges = nr_memory_ranges; + return 0; +} +#endif + +/* Return a sorted list of memory ranges. */ +int get_memory_ranges(struct memory_range **range, int *ranges, + unsigned long kexec_flags) +{ + int res = 0; + + res = init_memory_region_info(); + if (res != 0) + return res; +#ifdef WITH_GAMECUBE + return get_memory_ranges_gc(range, ranges, kexec_flags); +#else + return get_memory_ranges_dt(range, ranges, kexec_flags); +#endif +} + +struct file_type file_type[] = { + {"elf-ppc", elf_ppc_probe, elf_ppc_load, elf_ppc_usage}, + {"dol-ppc", dol_ppc_probe, dol_ppc_load, dol_ppc_usage}, + {"uImage-ppc", uImage_ppc_probe, uImage_ppc_load, uImage_ppc_usage }, +}; +int file_types = sizeof(file_type) / sizeof(file_type[0]); + +void arch_usage(void) +{ +} + +int arch_process_options(int argc, char **argv) +{ + return 0; +} + +const struct arch_map_entry arches[] = { + /* For compatibility with older patches + * use KEXEC_ARCH_DEFAULT instead of KEXEC_ARCH_PPC here. + */ + { "ppc", KEXEC_ARCH_DEFAULT }, + { NULL, 0 }, +}; + +int arch_compat_trampoline(struct kexec_info *UNUSED(info)) +{ + return 0; +} + +void arch_update_purgatory(struct kexec_info *UNUSED(info)) +{ +} + diff --git a/kexec/arch/ppc/kexec-ppc.h b/kexec/arch/ppc/kexec-ppc.h new file mode 100644 index 0000000..04e728e --- /dev/null +++ b/kexec/arch/ppc/kexec-ppc.h @@ -0,0 +1,86 @@ +#ifndef KEXEC_PPC_H +#define KEXEC_PPC_H + +#define MAXBYTES 128 +#define MAX_LINE 160 +#define CORE_TYPE_ELF32 1 +#define CORE_TYPE_ELF64 2 + +#define COMMAND_LINE_SIZE 2048 /* from kernel */ + +extern unsigned char setup_simple_start[]; +extern uint32_t setup_simple_size; + +extern struct { + uint32_t spr8; +} setup_simple_regs; + +extern unsigned char setup_dol_start[]; +extern uint32_t setup_dol_size; +extern uint64_t rmo_top; + +extern struct { + uint32_t spr8; +} setup_dol_regs; + +#define SIZE_16M (16*1024*1024UL) + +int elf_ppc_probe(const char *buf, off_t len); +int elf_ppc_load(int argc, char **argv, const char *buf, off_t len, + struct kexec_info *info); +void elf_ppc_usage(void); + +int uImage_ppc_probe(const char *buf, off_t len); +int uImage_ppc_load(int argc, char **argv, const char *buf, off_t len, + struct kexec_info *info); +void uImage_ppc_usage(void); + +int dol_ppc_probe(const char *buf, off_t len); +int dol_ppc_load(int argc, char **argv, const char *buf, off_t len, + struct kexec_info *info); +void dol_ppc_usage(void); + +/* + * During inital setup the kernel does not map the whole memory but a part of + * it. On Book-E that is 64MiB, 601 24MiB or 256MiB (if possible). + */ +#define KERNEL_ACCESS_TOP (24 * 1024 * 1024) + +/* boot block version 17 as defined by the linux kernel */ +struct bootblock { + unsigned magic, + totalsize, + off_dt_struct, + off_dt_strings, + off_mem_rsvmap, + version, + last_comp_version, + boot_physid, + dt_strings_size, + dt_struct_size; +}; + +typedef struct mem_rgns { + unsigned int size; + struct memory_range *ranges; +} mem_rgns_t; +extern mem_rgns_t usablemem_rgns; +extern int max_memory_ranges; +extern unsigned long long crash_base, crash_size; +extern unsigned long long initrd_base, initrd_size; +extern unsigned long long ramdisk_base, ramdisk_size; +extern unsigned char reuse_initrd; +extern const char *ramdisk; + +/* Method to parse the memory/reg nodes in device-tree */ +extern unsigned long dt_address_cells, dt_size_cells; +extern int init_memory_region_info(void); +extern int read_memory_region_limits(int fd, unsigned long long *start, + unsigned long long *end); +extern int get_devtree_value(const char *fname, unsigned long long *pvalue); +/*fs2dt*/ +void reserve(unsigned long long where, unsigned long long length); + +/* Defined kexec-uImage-ppc.c */ +extern char* slurp_ramdisk_ppc(const char *filename, off_t *r_size); +#endif /* KEXEC_PPC_H */ diff --git a/kexec/arch/ppc/kexec-uImage-ppc.c b/kexec/arch/ppc/kexec-uImage-ppc.c new file mode 100644 index 0000000..e8f7adc --- /dev/null +++ b/kexec/arch/ppc/kexec-uImage-ppc.c @@ -0,0 +1,325 @@ +/* + * uImage support for PowerPC + */ +#define _GNU_SOURCE +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <sys/types.h> +#include <image.h> +#include <getopt.h> +#include <arch/options.h> +#include "../../kexec.h" +#include "../../kexec-syscall.h" +#include "kexec-ppc.h" +#include "fixup_dtb.h" +#include <kexec-uImage.h> +#include "crashdump-powerpc.h" +#include <limits.h> + +int create_flatten_tree(struct kexec_info *, unsigned char **, unsigned long *, + char *); + +/* See options.h -- add any more there, too. */ +static const struct option options[] = { + KEXEC_ARCH_OPTIONS + {"command-line", 1, 0, OPT_APPEND}, + {"append", 1, 0, OPT_APPEND}, + {"ramdisk", 1, 0, OPT_RAMDISK}, + {"initrd", 1, 0, OPT_RAMDISK}, + {"dtb", 1, 0, OPT_DTB}, + {"reuse-node", 1, 0, OPT_NODES}, + {0, 0, 0, 0}, +}; +static const char short_options[] = KEXEC_ARCH_OPT_STR; + +void uImage_ppc_usage(void) +{ + printf( + " --command-line=STRING Set the kernel command line to STRING.\n" + " --append=STRING Set the kernel command line to STRING.\n" + " --ramdisk=<filename> Initial RAM disk.\n" + " --initrd=<filename> same as --ramdisk\n" + " --dtb=<filename> Specify device tree blob file.\n" + " --reuse-node=node Specify nodes which should be taken from /proc/device-tree.\n" + " Can be set multiple times.\n" + ); +} + +/* + * Load the ramdisk into buffer. + * If the supplied image is in uImage format use + * uImage_load() to read the payload from the image. + */ +char *slurp_ramdisk_ppc(const char *filename, off_t *r_size) +{ + struct Image_info img; + off_t size; + const char *buf = slurp_file(filename, &size); + int rc; + + /* Check if this is a uImage RAMDisk */ + if (!buf) + return buf; + rc = uImage_probe_ramdisk(buf, size, IH_ARCH_PPC); + if (rc < 0) + die("uImage: Corrupted ramdisk file %s\n", filename); + else if (rc == 0) { + if (uImage_load(buf, size, &img) != 0) + die("uImage: Reading %ld bytes from %s failed\n", + size, filename); + buf = img.buf; + size = img.len; + } + + *r_size = size; + return buf; +} + +int uImage_ppc_probe(const char *buf, off_t len) +{ + return uImage_probe_kernel(buf, len, IH_ARCH_PPC); +} + +static int ppc_load_bare_bits(int argc, char **argv, const char *buf, + off_t len, struct kexec_info *info, unsigned int load_addr, + unsigned int ep) +{ + char *command_line, *cmdline_buf, *crash_cmdline; + char *tmp_cmdline; + int command_line_len, crash_cmdline_len; + char *dtb; + unsigned int addr; + unsigned long dtb_addr; + unsigned long dtb_addr_actual; +#define FIXUP_ENTRYS (20) + char *fixup_nodes[FIXUP_ENTRYS + 1]; + int cur_fixup = 0; + int opt; + int ret = 0; + char *seg_buf = NULL; + off_t seg_size = 0; + unsigned long long hole_addr; + unsigned long max_addr; + char *blob_buf = NULL; + off_t blob_size = 0; + char *error_msg = NULL; + + cmdline_buf = NULL; + command_line = NULL; + tmp_cmdline = NULL; + dtb = NULL; + max_addr = LONG_MAX; + + while ((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) { + switch (opt) { + default: + /* Ignore core options */ + if (opt < OPT_ARCH_MAX) { + break; + } + case OPT_APPEND: + tmp_cmdline = optarg; + break; + + case OPT_RAMDISK: + ramdisk = optarg; + break; + + case OPT_DTB: + dtb = optarg; + break; + + case OPT_NODES: + if (cur_fixup >= FIXUP_ENTRYS) { + die("The number of entries for the fixup is too large\n"); + } + fixup_nodes[cur_fixup] = optarg; + cur_fixup++; + break; + } + } + + if (ramdisk && reuse_initrd) + die("Can't specify --ramdisk or --initrd with --reuseinitrd\n"); + + command_line_len = 0; + if (tmp_cmdline) { + command_line = tmp_cmdline; + } else { + command_line = get_command_line(); + } + command_line_len = strlen(command_line) + 1; + + fixup_nodes[cur_fixup] = NULL; + + /* + * len contains the length of the whole kernel image except the bss + * section. The 1 MiB should cover it. The purgatory and the dtb are + * allocated from memtop down towards zero so we should never get too + * close to the bss :) + */ +#define _1MiB (1 * 1024 * 1024) + + /* + * If the provided load_addr cannot be allocated, find a new + * area. Rebase the entry point based on the new load_addr. + */ + if (!valid_memory_range(info, load_addr, load_addr + (len + _1MiB))) { + int ep_offset = ep - load_addr; + + load_addr = locate_hole(info, len + _1MiB, 0, 0, max_addr, 1); + if (load_addr == ULONG_MAX) { + printf("Can't allocate memory for kernel of len %ld\n", + len + _1MiB); + return -1; + } + + ep = load_addr + ep_offset; + } + + add_segment(info, buf, len, load_addr, len + _1MiB); + + + if (info->kexec_flags & KEXEC_ON_CRASH) { + crash_cmdline = xmalloc(COMMAND_LINE_SIZE); + memset((void *)crash_cmdline, 0, COMMAND_LINE_SIZE); + ret = load_crashdump_segments(info, crash_cmdline, + max_addr, 0); + if (ret < 0) { + ret = -1; + goto out; + } + crash_cmdline_len = strlen(crash_cmdline); + } else { + crash_cmdline = NULL; + crash_cmdline_len = 0; + } + + if (crash_cmdline_len + command_line_len + 1 > COMMAND_LINE_SIZE) { + printf("Kernel command line exceeds maximum possible length\n"); + return -1; + } + + cmdline_buf = xmalloc(COMMAND_LINE_SIZE); + memset((void *)cmdline_buf, 0, COMMAND_LINE_SIZE); + + if (command_line) + strcpy(cmdline_buf, command_line); + if (crash_cmdline) + strncat(cmdline_buf, crash_cmdline, crash_cmdline_len); + + elf_rel_build_load(info, &info->rhdr, (const char *)purgatory, + purgatory_size, 0, -1, -1, 0); + + /* Here we need to initialize the device tree, and find out where + * it is going to live so we can place it directly after the + * kernel image */ + if (dtb) { + /* Grab device tree from buffer */ + blob_buf = slurp_file(dtb, &blob_size); + } else { + create_flatten_tree(info, (unsigned char **)&blob_buf, + (unsigned long *)&blob_size, cmdline_buf); + } + if (!blob_buf || !blob_size) { + error_msg = "Device tree seems to be an empty file.\n"; + goto out2; + } + + /* initial fixup for device tree */ + blob_buf = fixup_dtb_init(info, blob_buf, &blob_size, load_addr, &dtb_addr); + + if (ramdisk) { + seg_buf = slurp_ramdisk_ppc(ramdisk, &seg_size); + /* Load ramdisk at top of memory */ + hole_addr = add_buffer(info, seg_buf, seg_size, seg_size, + 0, dtb_addr + blob_size, max_addr, -1); + ramdisk_base = hole_addr; + ramdisk_size = seg_size; + } + if (reuse_initrd) { + ramdisk_base = initrd_base; + ramdisk_size = initrd_size; + } + + if (info->kexec_flags & KEXEC_ON_CRASH && ramdisk_base != 0) { + if ( (ramdisk_base < crash_base) || + (ramdisk_base > crash_base + crash_size) ) { + printf("WARNING: ramdisk is above crashkernel region!\n"); + } + else if (ramdisk_base + ramdisk_size > crash_base + crash_size) { + printf("WARNING: ramdisk overflows crashkernel region!\n"); + } + } + + /* Perform final fixup on devie tree, i.e. everything beside what + * was done above */ + fixup_dtb_finalize(info, blob_buf, &blob_size, fixup_nodes, + cmdline_buf); + dtb_addr_actual = add_buffer(info, blob_buf, blob_size, blob_size, 0, dtb_addr, + load_addr + KERNEL_ACCESS_TOP, 1); + if (dtb_addr_actual != dtb_addr) { + printf("dtb_addr_actual: %lx, dtb_addr: %lx\n", dtb_addr_actual, dtb_addr); + error_msg = "Error device tree not loadded to address it was expecting to be loaded too!\n"; + goto out2; + } + + /* set various variables for the purgatory */ + addr = ep; + elf_rel_set_symbol(&info->rhdr, "kernel", &addr, sizeof(addr)); + + addr = dtb_addr; + elf_rel_set_symbol(&info->rhdr, "dt_offset", &addr, sizeof(addr)); + +#define PUL_STACK_SIZE (16 * 1024) + addr = locate_hole(info, PUL_STACK_SIZE, 0, 0, -1, 1); + addr += PUL_STACK_SIZE; + elf_rel_set_symbol(&info->rhdr, "stack", &addr, sizeof(addr)); + /* No allocation past here in order not to overwrite the stack */ +#undef PUL_STACK_SIZE + + /* + * Fixup ThreadPointer(r2) for purgatory. + * PPC32 ELF ABI expects : + * ThreadPointer (TP) = TCB + 0x7000 + * We manually allocate a TCB space and set the TP + * accordingly. + */ +#define TCB_SIZE 1024 +#define TCB_TP_OFFSET 0x7000 /* PPC32 ELF ABI */ + addr = locate_hole(info, TCB_SIZE, 0, 0, + ((unsigned long)-1 - TCB_TP_OFFSET), + 1); + addr += TCB_SIZE + TCB_TP_OFFSET; + elf_rel_set_symbol(&info->rhdr, "my_thread_ptr", &addr, sizeof(addr)); +#undef TCB_TP_OFFSET +#undef TCB_SIZE + + addr = elf_rel_get_addr(&info->rhdr, "purgatory_start"); + info->entry = (void *)addr; + +out2: + free(cmdline_buf); +out: + free(crash_cmdline); + if (!tmp_cmdline) + free(command_line); + if (error_msg) + die("%s", error_msg); + return ret; +} + +int uImage_ppc_load(int argc, char **argv, const char *buf, off_t len, + struct kexec_info *info) +{ + struct Image_info img; + int ret; + + ret = uImage_load(buf, len, &img); + if (ret) + return ret; + + return ppc_load_bare_bits(argc, argv, img.buf, img.len, info, + img.base, img.ep); +} diff --git a/kexec/arch/ppc/libfdt-wrapper.c b/kexec/arch/ppc/libfdt-wrapper.c new file mode 100644 index 0000000..ef355d0 --- /dev/null +++ b/kexec/arch/ppc/libfdt-wrapper.c @@ -0,0 +1,189 @@ +/* + * This file does the necessary interface mapping between the bootwrapper + * device tree operations and the interface provided by shared source + * files flatdevicetree.[ch]. + * + * Copyright 2007 David Gibson, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include <stdlib.h> +#include <stddef.h> +#include <stdio.h> +#include <page.h> +#include <libfdt.h> +#include "ops.h" +#include "../../kexec.h" + +#define BAD_ERROR(err) (((err) < 0) \ + && ((err) != -FDT_ERR_NOTFOUND) \ + && ((err) != -FDT_ERR_EXISTS)) + +#define check_err(err) \ + ({ \ + if (BAD_ERROR(err) || ((err < 0) && kexec_debug)) \ + printf("%s():%d %s\n\r", __func__, __LINE__, \ + fdt_strerror(err)); \ + if (BAD_ERROR(err)) \ + exit(1); \ + (err < 0) ? -1 : 0; \ + }) + +#define offset_devp(off) \ + ({ \ + int _offset = (off); \ + check_err(_offset) ? NULL : (void *)(_offset+1); \ + }) + +#define devp_offset_find(devp) (((int)(devp))-1) +#define devp_offset(devp) (devp ? ((int)(devp))-1 : 0) + +static void *fdt; +static void *buf; /* = NULL */ +struct dt_ops dt_ops; + +#define EXPAND_GRANULARITY 1024 + +static void expand_buf(int minexpand) +{ + int size = fdt_totalsize(fdt); + int rc; + + size = _ALIGN(size + minexpand, EXPAND_GRANULARITY); + buf = realloc(buf, size); + if (!buf) + die("Couldn't find %d bytes to expand device tree\n\r", size); + rc = fdt_open_into(fdt, buf, size); + if (rc != 0) + die("Couldn't expand fdt into new buffer: %s\n\r", + fdt_strerror(rc)); + + fdt = buf; +} + +static void *fdt_wrapper_finddevice(const char *path) +{ + return offset_devp(fdt_path_offset(fdt, path)); +} + +static int fdt_wrapper_getprop(const void *devp, const char *name, + void *buf, const int buflen) +{ + const void *p; + int len; + + p = fdt_getprop(fdt, devp_offset(devp), name, &len); + if (!p) + return check_err(len); + memcpy(buf, p, min(len, buflen)); + return len; +} + +static int fdt_wrapper_setprop(const void *devp, const char *name, + const void *buf, const int len) +{ + int rc; + + rc = fdt_setprop(fdt, devp_offset(devp), name, buf, len); + if (rc == -FDT_ERR_NOSPACE) { + expand_buf(len + 16); + rc = fdt_setprop(fdt, devp_offset(devp), name, buf, len); + } + + return check_err(rc); +} + +static void *fdt_wrapper_get_parent(const void *devp) +{ + return offset_devp(fdt_parent_offset(fdt, devp_offset(devp))); +} + +static void *fdt_wrapper_create_node(const void *devp, const char *name) +{ + int offset; + + offset = fdt_add_subnode(fdt, devp_offset(devp), name); + if (offset == -FDT_ERR_NOSPACE) { + expand_buf(strlen(name) + 16); + offset = fdt_add_subnode(fdt, devp_offset(devp), name); + } + + return offset_devp(offset); +} + +static void *fdt_wrapper_find_node_by_prop_value(const void *prev, + const char *name, + const char *val, + int len) +{ + int offset = fdt_node_offset_by_prop_value(fdt, devp_offset_find(prev), + name, val, len); + return offset_devp(offset); +} + +static void *fdt_wrapper_find_node_by_compatible(const void *prev, + const char *val) +{ + int offset = fdt_node_offset_by_compatible(fdt, devp_offset_find(prev), + val); + return offset_devp(offset); +} + +static char *fdt_wrapper_get_path(const void *devp, char *buf, int len) +{ + int rc; + + rc = fdt_get_path(fdt, devp_offset(devp), buf, len); + if (check_err(rc)) + return NULL; + return buf; +} + +static unsigned long fdt_wrapper_finalize(void) +{ + int rc; + + rc = fdt_pack(fdt); + if (rc != 0) + die("Couldn't pack flat tree: %s\n\r", + fdt_strerror(rc)); + return (unsigned long)fdt; +} + +void fdt_init(void *blob) +{ + int err; + int bufsize; + + dt_ops.finddevice = fdt_wrapper_finddevice; + dt_ops.getprop = fdt_wrapper_getprop; + dt_ops.setprop = fdt_wrapper_setprop; + dt_ops.get_parent = fdt_wrapper_get_parent; + dt_ops.create_node = fdt_wrapper_create_node; + dt_ops.find_node_by_prop_value = fdt_wrapper_find_node_by_prop_value; + dt_ops.find_node_by_compatible = fdt_wrapper_find_node_by_compatible; + dt_ops.get_path = fdt_wrapper_get_path; + dt_ops.finalize = fdt_wrapper_finalize; + + /* Make sure the dt blob is the right version and so forth */ + fdt = blob; + bufsize = fdt_totalsize(fdt); + + err = fdt_open_into(fdt, fdt, bufsize); + if (err != 0) + die("fdt_init(): %s\n\r", fdt_strerror(err)); +} diff --git a/kexec/arch/ppc/ops.h b/kexec/arch/ppc/ops.h new file mode 100644 index 0000000..5e7a070 --- /dev/null +++ b/kexec/arch/ppc/ops.h @@ -0,0 +1,147 @@ +/* + * Global definition of all the bootwrapper operations. + * + * Author: Mark A. Greer <mgreer@mvista.com> + * + * 2006 (c) MontaVista Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ +#ifndef _PPC_BOOT_OPS_H_ +#define _PPC_BOOT_OPS_H_ +#include "types.h" + +#define MAX_PATH_LEN 256 +#define MAX_PROP_LEN 256 /* What should this be? */ + +typedef void (*kernel_entry_t)(unsigned long r3, unsigned long r4, void *r5); + +/* Device Tree operations */ +struct dt_ops { + void * (*finddevice)(const char *name); + int (*getprop)(const void *phandle, const char *name, void *buf, + const int buflen); + int (*setprop)(const void *phandle, const char *name, + const void *buf, const int buflen); + void *(*get_parent)(const void *phandle); + /* The node must not already exist. */ + void *(*create_node)(const void *parent, const char *name); + void *(*find_node_by_prop_value)(const void *prev, + const char *propname, + const char *propval, int proplen); + void *(*find_node_by_compatible)(const void *prev, + const char *compat); + unsigned long (*finalize)(void); + char *(*get_path)(const void *phandle, char *buf, int len); +}; +extern struct dt_ops dt_ops; + +void fdt_init(void *blob); +extern void flush_cache(void *, unsigned long); +int dt_xlate_reg(void *node, int res, unsigned long *addr, unsigned long *size); +int dt_xlate_addr(void *node, u32 *buf, int buflen, unsigned long *xlated_addr); +int dt_is_compatible(void *node, const char *compat); +void dt_get_reg_format(void *node, u32 *naddr, u32 *nsize); +int dt_get_virtual_reg(void *node, void **addr, int nres); + +static inline void *finddevice(const char *name) +{ + return (dt_ops.finddevice) ? dt_ops.finddevice(name) : NULL; +} + +static inline int getprop(void *devp, const char *name, void *buf, int buflen) +{ + return (dt_ops.getprop) ? dt_ops.getprop(devp, name, buf, buflen) : -1; +} + +static inline int setprop(void *devp, const char *name, + const void *buf, int buflen) +{ + return (dt_ops.setprop) ? dt_ops.setprop(devp, name, buf, buflen) : -1; +} +#define setprop_val(devp, name, val) \ + do { \ + typeof(val) x = (val); \ + setprop((devp), (name), &x, sizeof(x)); \ + } while (0) + +static inline int setprop_str(void *devp, const char *name, const char *buf) +{ + if (dt_ops.setprop) + return dt_ops.setprop(devp, name, buf, strlen(buf) + 1); + + return -1; +} + +static inline void *get_parent(const char *devp) +{ + return dt_ops.get_parent ? dt_ops.get_parent(devp) : NULL; +} + +static inline void *create_node(const void *parent, const char *name) +{ + return dt_ops.create_node ? dt_ops.create_node(parent, name) : NULL; +} + + +static inline void *find_node_by_prop_value(const void *prev, + const char *propname, + const char *propval, int proplen) +{ + if (dt_ops.find_node_by_prop_value) + return dt_ops.find_node_by_prop_value(prev, propname, + propval, proplen); + + return NULL; +} + +static inline void *find_node_by_prop_value_str(const void *prev, + const char *propname, + const char *propval) +{ + return find_node_by_prop_value(prev, propname, propval, + strlen(propval) + 1); +} + +static inline void *find_node_by_devtype(const void *prev, + const char *type) +{ + return find_node_by_prop_value_str(prev, "device_type", type); +} + +static inline void *find_node_by_alias(const char *alias) +{ + void *devp = finddevice("/aliases"); + + if (devp) { + char path[MAX_PATH_LEN]; + if (getprop(devp, alias, path, MAX_PATH_LEN) > 0) + return finddevice(path); + } + + return NULL; +} + +static inline void *find_node_by_compatible(const void *prev, + const char *compat) +{ + if (dt_ops.find_node_by_compatible) + return dt_ops.find_node_by_compatible(prev, compat); + + return NULL; +} + +#define dt_fixup_mac_addresses(...) \ + __dt_fixup_mac_addresses(0, __VA_ARGS__, NULL) + + +static inline char *get_path(const void *phandle, char *buf, int len) +{ + if (dt_ops.get_path) + return dt_ops.get_path(phandle, buf, len); + + return NULL; +} + +#endif /* _PPC_BOOT_OPS_H_ */ diff --git a/kexec/arch/ppc/ppc-setup-dol.S b/kexec/arch/ppc/ppc-setup-dol.S new file mode 100644 index 0000000..17169bd --- /dev/null +++ b/kexec/arch/ppc/ppc-setup-dol.S @@ -0,0 +1,174 @@ +/* + * ppc-setup-dol.S - setup glue for Nintendo's GameCube + * Copyright (C) 2004 Albert Herranz + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#include "ppc_asm.h" + + .data + .globl setup_dol_start +setup_dol_start: + + /* Try to reproduce the GameCube "native" environment */ + + /* Setup BATs */ + isync + li r8, 0 + mtspr DBAT0U, r8 + mtspr DBAT0L, r8 + mtspr DBAT1U, r8 + mtspr DBAT1L, r8 + mtspr DBAT2U, r8 + mtspr DBAT2L, r8 + mtspr DBAT3U, r8 + mtspr DBAT3L, r8 + mtspr IBAT0U, r8 + mtspr IBAT0L, r8 + mtspr IBAT1U, r8 + mtspr IBAT1L, r8 + mtspr IBAT2U, r8 + mtspr IBAT2L, r8 + mtspr IBAT3U, r8 + mtspr IBAT3L, r8 + + /* + * Memory Map + * start end size description + * 0x80000000 0x817fffff 24MB RAM, uncached + * 0xc0000000 0xc17fffff 24MB RAM, cached + * 0xc8000000 0xc81fffff 2MB Embedded Framebuffer + * 0xcc000000 Hardware registers + * 0xe0000000 Layer 2 transfer cache ??? 256KB + * + */ + + isync + lis r8, 0x8000 /* IBAT0,DBAT0 for first 16Mbytes */ + ori r8, r8, 0x01ff /* 16MB */ + mtspr IBAT0U, r8 + mtspr DBAT0U, r8 + li r8, 0x0002 /* rw */ + mtspr IBAT0L, r8 + mtspr DBAT0L, r8 + + lis r8, 0xc000 /* DBAT1 for IO mem */ + ori r8, r8, 0x1fff /* 256MB */ + mtspr DBAT1U, r8 + li r8, 0x002a /* uncached, guarded ,rw */ + mtspr DBAT1L, r8 + + lis r8, 0x8100 /* IBAT2,DBAT2 for next 8Mbytes */ + ori r8, r8, 0x00ff /* 8MB */ + mtspr IBAT2U, r8 + mtspr DBAT2U, r8 + lis r8, 0x0100 + ori r8, r8, 0x0002 /* rw */ + mtspr IBAT2L, r8 + mtspr DBAT2L, r8 + + lis r8, 0xe000 /* DBAT3 for layer 2 transfer cache ??? */ + ori r8, r8, 0x01fe /* 16MB ??? */ + mtspr DBAT3U, r8 + lis r8, 0xe000 + ori r8, r8, 0x0002 /* rw */ + mtspr DBAT3L, r8 + + sync + isync + +/* AFAIK, this is not strictly needed, although seems sane */ +#if 1 + li r9, 0 + + /* page table pointer */ + sync + mtspr SDR1, r9 + + /* segment registers */ + li r8, 16 + mtctr r8 + li r8, 0 +1: mtsrin r9, r8 /* zero */ + sync + addis r8,r8,0x1000 /* next register */ + bdnz 1b +#endif + + /* switch MMU on and continue */ + RELOC_SYM(1f) + mfmsr r0 + ori r0, r0, MSR_RI|MSR_ME|MSR_DR|MSR_IR + mtspr SRR1, r0 + oris r3, r3, 0x8000 /* adjust text address */ + mtspr SRR0, r3 + oris r1, r1, 0x8000 /* adjust stack */ + sync + rfi + +1: + /* from now on we run in a DOL-like environment */ + + + /* first, sanitize the hardware a little bit */ + /* although seems to be not needed in the general case */ + +#if 1 + /* audio */ + lis r8, 0xcc00 /* io mem */ + li r9, 0 + sth r9, 0x5036(r8) /* stop audio sample */ + stw r9, 0x6c00(r8) /* stop streaming */ + stw r9, 0x6c04(r8) /* mute */ + + /* video */ + mfspr r8, 920 /* spr920 = HID2 */ + rlwinm r8, r8, 0, 4, 2 /* stop GX FIFO, and more */ + mtspr 920, r8 + + /* exi */ + lis r8, 0xcc00 /* io mem */ +1: lwz r9,0x680c(r8) /* wait for dma transfer to complete */ + andi. r9,r9,1 + bne+ 1b + stw r9,0x6800(r8) /* disable exi interrupts */ + addi r8,r8,0x14 /* next channel */ + andi. r9,r8,0x40 /* XXX 4 channels? */ + beq+ 1b + + /* pic */ + lis r8, 0xcc00 /* io mem */ + li r9, 0 + stw r9, 0x3004(r8) /* mask all interrupts */ + stw r9, 0x3000(r8) /* clear interrupt cause */ + + /* invalidate L1 data and instructions caches */ + mfspr r8, HID0 + ori r8, r8, HID0_ICFI|HID0_DCI + mtspr HID0, r8 +#endif + + /* jump to our entry point */ + RELOC_SYM(setup_dol_regs) + mr r9, r3 + lwz r5, spr8 - setup_dol_regs(r9) + + mtlr r5 + blr + + .balign 4 + .globl setup_dol_regs +setup_dol_regs: +spr8: .long 0x00000000 + + .balign 4 +//#include "isobel_reloc_debug_console.s" + +setup_dol_end: + + .globl setup_dol_size +setup_dol_size: + .long setup_dol_end - setup_dol_start + diff --git a/kexec/arch/ppc/ppc-setup-simple.S b/kexec/arch/ppc/ppc-setup-simple.S new file mode 100644 index 0000000..1317a8d --- /dev/null +++ b/kexec/arch/ppc/ppc-setup-simple.S @@ -0,0 +1,39 @@ +/* + * ppc-setup-simple.S - (hopefully) setup for simple embedded platforms + * Copyright (C) 2004 Albert Herranz + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +/* + * Only suitable for platforms booting with MMU turned off. + * -- Albert Herranz + */ + +#include "ppc_asm.h" + + .data + .globl setup_simple_start +setup_simple_start: + + /* should perform here any required setup */ + + RELOC_SYM(setup_simple_regs) + mr r9, r3 + lwz r5, spr8 - setup_simple_regs(r9) + + mtlr r5 + blr + + .balign 4 + .globl setup_simple_regs +setup_simple_regs: +spr8: .long 0x00000000 + +setup_simple_end: + + .globl setup_simple_size +setup_simple_size: + .long setup_simple_end - setup_simple_start + diff --git a/kexec/arch/ppc/ppc_asm.h b/kexec/arch/ppc/ppc_asm.h new file mode 100644 index 0000000..36503a9 --- /dev/null +++ b/kexec/arch/ppc/ppc_asm.h @@ -0,0 +1,506 @@ +/* + * ppc_asm.h - mainly bits stolen from Linux kernel asm/reg.h and asm/ppc_asm.h + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +/* Condition Register Bit Fields */ + +#define cr0 0 +#define cr1 1 +#define cr2 2 +#define cr3 3 +#define cr4 4 +#define cr5 5 +#define cr6 6 +#define cr7 7 + + +/* General Purpose Registers (GPRs) */ + +#define r0 0 +#define r1 1 +#define r2 2 +#define r3 3 +#define r4 4 +#define r5 5 +#define r6 6 +#define r7 7 +#define r8 8 +#define r9 9 +#define r10 10 +#define r11 11 +#define r12 12 +#define r13 13 +#define r14 14 +#define r15 15 +#define r16 16 +#define r17 17 +#define r18 18 +#define r19 19 +#define r20 20 +#define r21 21 +#define r22 22 +#define r23 23 +#define r24 24 +#define r25 25 +#define r26 26 +#define r27 27 +#define r28 28 +#define r29 29 +#define r30 30 +#define r31 31 + +/* Machine State Register (MSR) Fields */ +#define MSR_SF (1<<63) +#define MSR_ISF (1<<61) +#define MSR_VEC (1<<25) /* Enable AltiVec */ +#define MSR_POW (1<<18) /* Enable Power Management */ +#define MSR_WE (1<<18) /* Wait State Enable */ +#define MSR_TGPR (1<<17) /* TLB Update registers in use */ +#define MSR_CE (1<<17) /* Critical Interrupt Enable */ +#define MSR_ILE (1<<16) /* Interrupt Little Endian */ +#define MSR_EE (1<<15) /* External Interrupt Enable */ +#define MSR_PR (1<<14) /* Problem State / Privilege Level */ +#define MSR_FP (1<<13) /* Floating Point enable */ +#define MSR_ME (1<<12) /* Machine Check Enable */ +#define MSR_FE0 (1<<11) /* Floating Exception mode 0 */ +#define MSR_SE (1<<10) /* Single Step */ +#define MSR_BE (1<<9) /* Branch Trace */ +#define MSR_DE (1<<9) /* Debug Exception Enable */ +#define MSR_FE1 (1<<8) /* Floating Exception mode 1 */ +#define MSR_IP (1<<6) /* Exception prefix 0x000/0xFFF */ +#define MSR_IR (1<<5) /* Instruction Relocate */ +#define MSR_DR (1<<4) /* Data Relocate */ +#define MSR_PE (1<<3) /* Protection Enable */ +#define MSR_PX (1<<2) /* Protection Exclusive Mode */ +#define MSR_RI (1<<1) /* Recoverable Exception */ +#define MSR_LE (1<<0) /* Little Endian */ + +/* Special Purpose Registers (SPRNs)*/ +#define SPRN_CTR 0x009 /* Count Register */ +#define SPRN_DABR 0x3F5 /* Data Address Breakpoint Register */ +#define SPRN_DAR 0x013 /* Data Address Register */ +#define SPRN_TBRL 0x10C /* Time Base Read Lower Register (user, R/O) */ +#define SPRN_TBRU 0x10D /* Time Base Read Upper Register (user, R/O) */ +#define SPRN_TBWL 0x11C /* Time Base Lower Register (super, R/W) */ +#define SPRN_TBWU 0x11D /* Time Base Upper Register (super, R/W) */ +#define SPRN_HIOR 0x137 /* 970 Hypervisor interrupt offset */ +#define SPRN_DBAT0L 0x219 /* Data BAT 0 Lower Register */ +#define SPRN_DBAT0U 0x218 /* Data BAT 0 Upper Register */ +#define SPRN_DBAT1L 0x21B /* Data BAT 1 Lower Register */ +#define SPRN_DBAT1U 0x21A /* Data BAT 1 Upper Register */ +#define SPRN_DBAT2L 0x21D /* Data BAT 2 Lower Register */ +#define SPRN_DBAT2U 0x21C /* Data BAT 2 Upper Register */ +#define SPRN_DBAT3L 0x21F /* Data BAT 3 Lower Register */ +#define SPRN_DBAT3U 0x21E /* Data BAT 3 Upper Register */ +#define SPRN_DBAT4L 0x239 /* Data BAT 4 Lower Register */ +#define SPRN_DBAT4U 0x238 /* Data BAT 4 Upper Register */ +#define SPRN_DBAT5L 0x23B /* Data BAT 5 Lower Register */ +#define SPRN_DBAT5U 0x23A /* Data BAT 5 Upper Register */ +#define SPRN_DBAT6L 0x23D /* Data BAT 6 Lower Register */ +#define SPRN_DBAT6U 0x23C /* Data BAT 6 Upper Register */ +#define SPRN_DBAT7L 0x23F /* Data BAT 7 Lower Register */ +#define SPRN_DBAT7U 0x23E /* Data BAT 7 Upper Register */ + +#define SPRN_DEC 0x016 /* Decrement Register */ +#define SPRN_DER 0x095 /* Debug Enable Regsiter */ +#define DER_RSTE 0x40000000 /* Reset Interrupt */ +#define DER_CHSTPE 0x20000000 /* Check Stop */ +#define DER_MCIE 0x10000000 /* Machine Check Interrupt */ +#define DER_EXTIE 0x02000000 /* External Interrupt */ +#define DER_ALIE 0x01000000 /* Alignment Interrupt */ +#define DER_PRIE 0x00800000 /* Program Interrupt */ +#define DER_FPUVIE 0x00400000 /* FP Unavailable Interrupt */ +#define DER_DECIE 0x00200000 /* Decrementer Interrupt */ +#define DER_SYSIE 0x00040000 /* System Call Interrupt */ +#define DER_TRE 0x00020000 /* Trace Interrupt */ +#define DER_SEIE 0x00004000 /* FP SW Emulation Interrupt */ +#define DER_ITLBMSE 0x00002000 /* Imp. Spec. Instruction TLB Miss */ +#define DER_ITLBERE 0x00001000 /* Imp. Spec. Instruction TLB Error */ +#define DER_DTLBMSE 0x00000800 /* Imp. Spec. Data TLB Miss */ +#define DER_DTLBERE 0x00000400 /* Imp. Spec. Data TLB Error */ +#define DER_LBRKE 0x00000008 /* Load/Store Breakpoint Interrupt */ +#define DER_IBRKE 0x00000004 /* Instruction Breakpoint Interrupt */ +#define DER_EBRKE 0x00000002 /* External Breakpoint Interrupt */ +#define DER_DPIE 0x00000001 /* Dev. Port Nonmaskable Request */ +#define SPRN_DMISS 0x3D0 /* Data TLB Miss Register */ +#define SPRN_DSISR 0x012 /* Data Storage Interrupt Status Register */ +#define SPRN_EAR 0x11A /* External Address Register */ +#define SPRN_HASH1 0x3D2 /* Primary Hash Address Register */ +#define SPRN_HASH2 0x3D3 /* Secondary Hash Address Resgister */ +#define SPRN_HID0 0x3F0 /* Hardware Implementation Register 0 */ +#define HID0_EMCP (1<<31) /* Enable Machine Check pin */ +#define HID0_EBA (1<<29) /* Enable Bus Address Parity */ +#define HID0_EBD (1<<28) /* Enable Bus Data Parity */ +#define HID0_SBCLK (1<<27) +#define HID0_EICE (1<<26) +#define HID0_TBEN (1<<26) /* Timebase enable - 745x */ +#define HID0_ECLK (1<<25) +#define HID0_PAR (1<<24) +#define HID0_STEN (1<<24) /* Software table search enable - 745x */ +#define HID0_HIGH_BAT (1<<23) /* Enable high BATs - 7455 */ +#define HID0_DOZE (1<<23) +#define HID0_NAP (1<<22) +#define HID0_SLEEP (1<<21) +#define HID0_DPM (1<<20) +#define HID0_BHTCLR (1<<18) /* Clear branch history table - 7450 */ +#define HID0_XAEN (1<<17) /* Extended addressing enable - 7450 */ +#define HID0_NHR (1<<16) /* Not hard reset (software bit-7450)*/ +#define HID0_ICE (1<<15) /* Instruction Cache Enable */ +#define HID0_DCE (1<<14) /* Data Cache Enable */ +#define HID0_ILOCK (1<<13) /* Instruction Cache Lock */ +#define HID0_DLOCK (1<<12) /* Data Cache Lock */ +#define HID0_ICFI (1<<11) /* Instr. Cache Flash Invalidate */ +#define HID0_DCI (1<<10) /* Data Cache Invalidate */ +#define HID0_SPD (1<<9) /* Speculative disable */ +#define HID0_SGE (1<<7) /* Store Gathering Enable */ +#define HID0_SIED (1<<7) /* Serial Instr. Execution [Disable] */ +#define HID0_DFCA (1<<6) /* Data Cache Flush Assist */ +#define HID0_LRSTK (1<<4) /* Link register stack - 745x */ +#define HID0_BTIC (1<<5) /* Branch Target Instr Cache Enable */ +#define HID0_ABE (1<<3) /* Address Broadcast Enable */ +#define HID0_FOLD (1<<3) /* Branch Folding enable - 745x */ +#define HID0_BHTE (1<<2) /* Branch History Table Enable */ +#define HID0_BTCD (1<<1) /* Branch target cache disable */ +#define HID0_NOPDST (1<<1) /* No-op dst, dstt, etc. instr. */ +#define HID0_NOPTI (1<<0) /* No-op dcbt and dcbst instr. */ + +#define SPRN_HID1 0x3F1 /* Hardware Implementation Register 1 */ +#define HID1_EMCP (1<<31) /* 7450 Machine Check Pin Enable */ +#define HID1_PC0 (1<<16) /* 7450 PLL_CFG[0] */ +#define HID1_PC1 (1<<15) /* 7450 PLL_CFG[1] */ +#define HID1_PC2 (1<<14) /* 7450 PLL_CFG[2] */ +#define HID1_PC3 (1<<13) /* 7450 PLL_CFG[3] */ +#define HID1_SYNCBE (1<<11) /* 7450 ABE for sync, eieio */ +#define HID1_ABE (1<<10) /* 7450 Address Broadcast Enable */ +#define SPRN_HID2 0x3F8 /* Hardware Implementation Register 2 */ +#define SPRN_IABR 0x3F2 /* Instruction Address Breakpoint Register */ +#define SPRN_HID4 0x3F4 /* 970 HID4 */ +#define SPRN_HID5 0x3F6 /* 970 HID5 */ +#if !defined(SPRN_IAC1) && !defined(SPRN_IAC2) +#define SPRN_IAC1 0x3F4 /* Instruction Address Compare 1 */ +#define SPRN_IAC2 0x3F5 /* Instruction Address Compare 2 */ +#endif +#define SPRN_IBAT0L 0x211 /* Instruction BAT 0 Lower Register */ +#define SPRN_IBAT0U 0x210 /* Instruction BAT 0 Upper Register */ +#define SPRN_IBAT1L 0x213 /* Instruction BAT 1 Lower Register */ +#define SPRN_IBAT1U 0x212 /* Instruction BAT 1 Upper Register */ +#define SPRN_IBAT2L 0x215 /* Instruction BAT 2 Lower Register */ +#define SPRN_IBAT2U 0x214 /* Instruction BAT 2 Upper Register */ +#define SPRN_IBAT3L 0x217 /* Instruction BAT 3 Lower Register */ +#define SPRN_IBAT3U 0x216 /* Instruction BAT 3 Upper Register */ +#define SPRN_IBAT4L 0x231 /* Instruction BAT 4 Lower Register */ +#define SPRN_IBAT4U 0x230 /* Instruction BAT 4 Upper Register */ +#define SPRN_IBAT5L 0x233 /* Instruction BAT 5 Lower Register */ +#define SPRN_IBAT5U 0x232 /* Instruction BAT 5 Upper Register */ +#define SPRN_IBAT6L 0x235 /* Instruction BAT 6 Lower Register */ +#define SPRN_IBAT6U 0x234 /* Instruction BAT 6 Upper Register */ +#define SPRN_IBAT7L 0x237 /* Instruction BAT 7 Lower Register */ +#define SPRN_IBAT7U 0x236 /* Instruction BAT 7 Upper Register */ +#define SPRN_ICMP 0x3D5 /* Instruction TLB Compare Register */ +#define SPRN_ICTC 0x3FB /* Instruction Cache Throttling Control Reg */ +#define SPRN_ICTRL 0x3F3 /* 1011 7450 icache and interrupt ctrl */ +#define ICTRL_EICE 0x08000000 /* enable icache parity errs */ +#define ICTRL_EDC 0x04000000 /* enable dcache parity errs */ +#define ICTRL_EICP 0x00000100 /* enable icache par. check */ +#define SPRN_IMISS 0x3D4 /* Instruction TLB Miss Register */ +#define SPRN_IMMR 0x27E /* Internal Memory Map Register */ +#define SPRN_L2CR 0x3F9 /* Level 2 Cache Control Regsiter */ +#define SPRN_L2CR2 0x3f8 +#define L2CR_L2E 0x80000000 /* L2 enable */ +#define L2CR_L2PE 0x40000000 /* L2 parity enable */ +#define L2CR_L2SIZ_MASK 0x30000000 /* L2 size mask */ +#define L2CR_L2SIZ_256KB 0x10000000 /* L2 size 256KB */ +#define L2CR_L2SIZ_512KB 0x20000000 /* L2 size 512KB */ +#define L2CR_L2SIZ_1MB 0x30000000 /* L2 size 1MB */ +#define L2CR_L2CLK_MASK 0x0e000000 /* L2 clock mask */ +#define L2CR_L2CLK_DISABLED 0x00000000 /* L2 clock disabled */ +#define L2CR_L2CLK_DIV1 0x02000000 /* L2 clock / 1 */ +#define L2CR_L2CLK_DIV1_5 0x04000000 /* L2 clock / 1.5 */ +#define L2CR_L2CLK_DIV2 0x08000000 /* L2 clock / 2 */ +#define L2CR_L2CLK_DIV2_5 0x0a000000 /* L2 clock / 2.5 */ +#define L2CR_L2CLK_DIV3 0x0c000000 /* L2 clock / 3 */ +#define L2CR_L2RAM_MASK 0x01800000 /* L2 RAM type mask */ +#define L2CR_L2RAM_FLOW 0x00000000 /* L2 RAM flow through */ +#define L2CR_L2RAM_PIPE 0x01000000 /* L2 RAM pipelined */ +#define L2CR_L2RAM_PIPE_LW 0x01800000 /* L2 RAM pipelined latewr */ +#define L2CR_L2DO 0x00400000 /* L2 data only */ +#define L2CR_L2I 0x00200000 /* L2 global invalidate */ +#define L2CR_L2CTL 0x00100000 /* L2 RAM control */ +#define L2CR_L2WT 0x00080000 /* L2 write-through */ +#define L2CR_L2TS 0x00040000 /* L2 test support */ +#define L2CR_L2OH_MASK 0x00030000 /* L2 output hold mask */ +#define L2CR_L2OH_0_5 0x00000000 /* L2 output hold 0.5 ns */ +#define L2CR_L2OH_1_0 0x00010000 /* L2 output hold 1.0 ns */ +#define L2CR_L2SL 0x00008000 /* L2 DLL slow */ +#define L2CR_L2DF 0x00004000 /* L2 differential clock */ +#define L2CR_L2BYP 0x00002000 /* L2 DLL bypass */ +#define L2CR_L2IP 0x00000001 /* L2 GI in progress */ +#define SPRN_L3CR 0x3FA /* Level 3 Cache Control Regsiter */ +#define L3CR_L3E 0x80000000 /* L3 enable */ +#define L3CR_L3PE 0x40000000 /* L3 data parity enable */ +#define L3CR_L3APE 0x20000000 /* L3 addr parity enable */ +#define L3CR_L3SIZ 0x10000000 /* L3 size */ +#define L3CR_L3CLKEN 0x08000000 /* L3 clock enable */ +#define L3CR_L3RES 0x04000000 /* L3 special reserved bit */ +#define L3CR_L3CLKDIV 0x03800000 /* L3 clock divisor */ +#define L3CR_L3IO 0x00400000 /* L3 instruction only */ +#define L3CR_L3SPO 0x00040000 /* L3 sample point override */ +#define L3CR_L3CKSP 0x00030000 /* L3 clock sample point */ +#define L3CR_L3PSP 0x0000e000 /* L3 P-clock sample point */ +#define L3CR_L3REP 0x00001000 /* L3 replacement algorithm */ +#define L3CR_L3HWF 0x00000800 /* L3 hardware flush */ +#define L3CR_L3I 0x00000400 /* L3 global invalidate */ +#define L3CR_L3RT 0x00000300 /* L3 SRAM type */ +#define L3CR_L3NIRCA 0x00000080 /* L3 non-integer ratio clock adj. */ +#define L3CR_L3DO 0x00000040 /* L3 data only mode */ +#define L3CR_PMEN 0x00000004 /* L3 private memory enable */ +#define L3CR_PMSIZ 0x00000001 /* L3 private memory size */ +#define SPRN_MSSCR0 0x3f6 /* Memory Subsystem Control Register 0 */ +#define SPRN_MSSSR0 0x3f7 /* Memory Subsystem Status Register 1 */ +#define SPRN_LDSTCR 0x3f8 /* Load/Store control register */ +#define SPRN_LDSTDB 0x3f4 /* */ +#define SPRN_LR 0x008 /* Link Register */ +#define SPRN_MMCR0 0x3B8 /* Monitor Mode Control Register 0 */ +#define SPRN_MMCR1 0x3BC /* Monitor Mode Control Register 1 */ +#ifndef SPRN_PIR +#define SPRN_PIR 0x3FF /* Processor Identification Register */ +#endif +#define SPRN_PMC1 0x3B9 /* Performance Counter Register 1 */ +#define SPRN_PMC2 0x3BA /* Performance Counter Register 2 */ +#define SPRN_PMC3 0x3BD /* Performance Counter Register 3 */ +#define SPRN_PMC4 0x3BE /* Performance Counter Register 4 */ +#define SPRN_PTEHI 0x3D5 /* 981 7450 PTE HI word (S/W TLB load) */ +#define SPRN_PTELO 0x3D6 /* 982 7450 PTE LO word (S/W TLB load) */ +#define SPRN_PVR 0x11F /* Processor Version Register */ +#define SPRN_RPA 0x3D6 /* Required Physical Address Register */ +#define SPRN_SDA 0x3BF /* Sampled Data Address Register */ +#define SPRN_SDR1 0x019 /* MMU Hash Base Register */ +#define SPRN_SIA 0x3BB /* Sampled Instruction Address Register */ +#define SPRN_SPRG0 0x110 /* Special Purpose Register General 0 */ +#define SPRN_SPRG1 0x111 /* Special Purpose Register General 1 */ +#define SPRN_SPRG2 0x112 /* Special Purpose Register General 2 */ +#define SPRN_SPRG3 0x113 /* Special Purpose Register General 3 */ +#define SPRN_SPRG4 0x114 /* Special Purpose Register General 4 */ +#define SPRN_SPRG5 0x115 /* Special Purpose Register General 5 */ +#define SPRN_SPRG6 0x116 /* Special Purpose Register General 6 */ +#define SPRN_SPRG7 0x117 /* Special Purpose Register General 7 */ +#define SPRN_SRR0 0x01A /* Save/Restore Register 0 */ +#define SPRN_SRR1 0x01B /* Save/Restore Register 1 */ +#define SPRN_THRM1 0x3FC /* Thermal Management Register 1 */ +/* these bits were defined in inverted endian sense originally, ugh, confusing */ +#define THRM1_TIN (1 << 31) +#define THRM1_TIV (1 << 30) +#define THRM1_THRES(x) ((x&0x7f)<<23) +#define THRM3_SITV(x) ((x&0x3fff)<<1) +#define THRM1_TID (1<<2) +#define THRM1_TIE (1<<1) +#define THRM1_V (1<<0) +#define SPRN_THRM2 0x3FD /* Thermal Management Register 2 */ +#define SPRN_THRM3 0x3FE /* Thermal Management Register 3 */ +#define THRM3_E (1<<0) +#define SPRN_TLBMISS 0x3D4 /* 980 7450 TLB Miss Register */ +#define SPRN_UMMCR0 0x3A8 /* User Monitor Mode Control Register 0 */ +#define SPRN_UMMCR1 0x3AC /* User Monitor Mode Control Register 0 */ +#define SPRN_UPMC1 0x3A9 /* User Performance Counter Register 1 */ +#define SPRN_UPMC2 0x3AA /* User Performance Counter Register 2 */ +#define SPRN_UPMC3 0x3AD /* User Performance Counter Register 3 */ +#define SPRN_UPMC4 0x3AE /* User Performance Counter Register 4 */ +#define SPRN_USIA 0x3AB /* User Sampled Instruction Address Register */ +#define SPRN_VRSAVE 0x100 /* Vector Register Save Register */ +#define SPRN_XER 0x001 /* Fixed Point Exception Register */ + +/* Bit definitions for MMCR0 and PMC1 / PMC2. */ +#define MMCR0_PMC1_CYCLES (1 << 7) +#define MMCR0_PMC1_ICACHEMISS (5 << 7) +#define MMCR0_PMC1_DTLB (6 << 7) +#define MMCR0_PMC2_DCACHEMISS 0x6 +#define MMCR0_PMC2_CYCLES 0x1 +#define MMCR0_PMC2_ITLB 0x7 +#define MMCR0_PMC2_LOADMISSTIME 0x5 + +/* Short-hand versions for a number of the above SPRNs */ +#define CTR SPRN_CTR /* Counter Register */ +#define DAR SPRN_DAR /* Data Address Register */ +#define DABR SPRN_DABR /* Data Address Breakpoint Register */ +#define DBAT0L SPRN_DBAT0L /* Data BAT 0 Lower Register */ +#define DBAT0U SPRN_DBAT0U /* Data BAT 0 Upper Register */ +#define DBAT1L SPRN_DBAT1L /* Data BAT 1 Lower Register */ +#define DBAT1U SPRN_DBAT1U /* Data BAT 1 Upper Register */ +#define DBAT2L SPRN_DBAT2L /* Data BAT 2 Lower Register */ +#define DBAT2U SPRN_DBAT2U /* Data BAT 2 Upper Register */ +#define DBAT3L SPRN_DBAT3L /* Data BAT 3 Lower Register */ +#define DBAT3U SPRN_DBAT3U /* Data BAT 3 Upper Register */ +#define DBAT4L SPRN_DBAT4L /* Data BAT 4 Lower Register */ +#define DBAT4U SPRN_DBAT4U /* Data BAT 4 Upper Register */ +#define DBAT5L SPRN_DBAT5L /* Data BAT 5 Lower Register */ +#define DBAT5U SPRN_DBAT5U /* Data BAT 5 Upper Register */ +#define DBAT6L SPRN_DBAT6L /* Data BAT 6 Lower Register */ +#define DBAT6U SPRN_DBAT6U /* Data BAT 6 Upper Register */ +#define DBAT7L SPRN_DBAT7L /* Data BAT 7 Lower Register */ +#define DBAT7U SPRN_DBAT7U /* Data BAT 7 Upper Register */ +#define DEC SPRN_DEC /* Decrement Register */ +#define DMISS SPRN_DMISS /* Data TLB Miss Register */ +#define DSISR SPRN_DSISR /* Data Storage Interrupt Status Register */ +#define EAR SPRN_EAR /* External Address Register */ +#define HASH1 SPRN_HASH1 /* Primary Hash Address Register */ +#define HASH2 SPRN_HASH2 /* Secondary Hash Address Register */ +#define HID0 SPRN_HID0 /* Hardware Implementation Register 0 */ +#define HID1 SPRN_HID1 /* Hardware Implementation Register 1 */ +#define IABR SPRN_IABR /* Instruction Address Breakpoint Register */ +#define IBAT0L SPRN_IBAT0L /* Instruction BAT 0 Lower Register */ +#define IBAT0U SPRN_IBAT0U /* Instruction BAT 0 Upper Register */ +#define IBAT1L SPRN_IBAT1L /* Instruction BAT 1 Lower Register */ +#define IBAT1U SPRN_IBAT1U /* Instruction BAT 1 Upper Register */ +#define IBAT2L SPRN_IBAT2L /* Instruction BAT 2 Lower Register */ +#define IBAT2U SPRN_IBAT2U /* Instruction BAT 2 Upper Register */ +#define IBAT3L SPRN_IBAT3L /* Instruction BAT 3 Lower Register */ +#define IBAT3U SPRN_IBAT3U /* Instruction BAT 3 Upper Register */ +#define IBAT4L SPRN_IBAT4L /* Instruction BAT 4 Lower Register */ +#define IBAT4U SPRN_IBAT4U /* Instruction BAT 4 Upper Register */ +#define IBAT5L SPRN_IBAT5L /* Instruction BAT 5 Lower Register */ +#define IBAT5U SPRN_IBAT5U /* Instruction BAT 5 Upper Register */ +#define IBAT6L SPRN_IBAT6L /* Instruction BAT 6 Lower Register */ +#define IBAT6U SPRN_IBAT6U /* Instruction BAT 6 Upper Register */ +#define IBAT7L SPRN_IBAT7L /* Instruction BAT 7 Lower Register */ +#define IBAT7U SPRN_IBAT7U /* Instruction BAT 7 Upper Register */ +#define ICMP SPRN_ICMP /* Instruction TLB Compare Register */ +#define IMISS SPRN_IMISS /* Instruction TLB Miss Register */ +#define IMMR SPRN_IMMR /* PPC 860/821 Internal Memory Map Register */ +#define L2CR SPRN_L2CR /* Classic PPC L2 cache control register */ +#define L3CR SPRN_L3CR /* PPC 745x L3 cache control register */ +#define LR SPRN_LR +#define PVR SPRN_PVR /* Processor Version */ +#define RPA SPRN_RPA /* Required Physical Address Register */ +#define SDR1 SPRN_SDR1 /* MMU hash base register */ +#define SPR0 SPRN_SPRG0 /* Supervisor Private Registers */ +#define SPR1 SPRN_SPRG1 +#define SPR2 SPRN_SPRG2 +#define SPR3 SPRN_SPRG3 +#define SPR4 SPRN_SPRG4 +#define SPR5 SPRN_SPRG5 +#define SPR6 SPRN_SPRG6 +#define SPR7 SPRN_SPRG7 +#define SPRG0 SPRN_SPRG0 +#define SPRG1 SPRN_SPRG1 +#define SPRG2 SPRN_SPRG2 +#define SPRG3 SPRN_SPRG3 +#define SPRG4 SPRN_SPRG4 +#define SPRG5 SPRN_SPRG5 +#define SPRG6 SPRN_SPRG6 +#define SPRG7 SPRN_SPRG7 +#define SRR0 SPRN_SRR0 /* Save and Restore Register 0 */ +#define SRR1 SPRN_SRR1 /* Save and Restore Register 1 */ +#define SRR2 SPRN_SRR2 /* Save and Restore Register 2 */ +#define SRR3 SPRN_SRR3 /* Save and Restore Register 3 */ +#define ICTC SPRN_ICTC /* Instruction Cache Throttling Control Reg */ +#define THRM1 SPRN_THRM1 /* Thermal Management Register 1 */ +#define THRM2 SPRN_THRM2 /* Thermal Management Register 2 */ +#define THRM3 SPRN_THRM3 /* Thermal Management Register 3 */ +#define XER SPRN_XER +#define TBRL SPRN_TBRL /* Time Base Read Lower Register */ +#define TBRU SPRN_TBRU /* Time Base Read Upper Register */ +#define TBWL SPRN_TBWL /* Time Base Write Lower Register */ +#define TBWU SPRN_TBWU /* Time Base Write Upper Register */ + +/* Processor Version Register */ + +/* Processor Version Register (PVR) field extraction */ + +#define PVR_VER(pvr) (((pvr) >> 16) & 0xFFFF) /* Version field */ +#define PVR_REV(pvr) (((pvr) >> 0) & 0xFFFF) /* Revison field */ + +/* + * IBM has further subdivided the standard PowerPC 16-bit version and + * revision subfields of the PVR for the PowerPC 403s into the following: + */ + +#define PVR_FAM(pvr) (((pvr) >> 20) & 0xFFF) /* Family field */ +#define PVR_MEM(pvr) (((pvr) >> 16) & 0xF) /* Member field */ +#define PVR_CORE(pvr) (((pvr) >> 12) & 0xF) /* Core field */ +#define PVR_CFG(pvr) (((pvr) >> 8) & 0xF) /* Configuration field */ +#define PVR_MAJ(pvr) (((pvr) >> 4) & 0xF) /* Major revision field */ +#define PVR_MIN(pvr) (((pvr) >> 0) & 0xF) /* Minor revision field */ + +/* Processor Version Numbers */ + +#define PVR_403GA 0x00200000 +#define PVR_403GB 0x00200100 +#define PVR_403GC 0x00200200 +#define PVR_403GCX 0x00201400 +#define PVR_405GP 0x40110000 +#define PVR_STB03XXX 0x40310000 +#define PVR_NP405H 0x41410000 +#define PVR_NP405L 0x41610000 +#define PVR_440GP_RB 0x40120440 +#define PVR_440GP_RC1 0x40120481 +#define PVR_440GP_RC2 0x40200481 +#define PVR_440GX_RA 0x51b21850 +#define PVR_440GX_RB 0x51b21851 +#define PVR_440GX_RB1 0x51b21852 +#define PVR_601 0x00010000 +#define PVR_602 0x00050000 +#define PVR_603 0x00030000 +#define PVR_603e 0x00060000 +#define PVR_603ev 0x00070000 +#define PVR_603r 0x00071000 +#define PVR_604 0x00040000 +#define PVR_604e 0x00090000 +#define PVR_604r 0x000A0000 +#define PVR_620 0x00140000 +#define PVR_740 0x00080000 +#define PVR_750 PVR_740 +#define PVR_740P 0x10080000 +#define PVR_750P PVR_740P +#define PVR_7400 0x000C0000 +#define PVR_7410 0x800C0000 +#define PVR_7450 0x80000000 +/* + * For the 8xx processors, all of them report the same PVR family for + * the PowerPC core. The various versions of these processors must be + * differentiated by the version number in the Communication Processor + * Module (CPM). + */ +#define PVR_821 0x00500000 +#define PVR_823 PVR_821 +#define PVR_850 PVR_821 +#define PVR_860 PVR_821 +#define PVR_8240 0x00810100 +#define PVR_8245 0x80811014 +#define PVR_8260 PVR_8240 + +/* Segment Registers */ +#define SR0 0 +#define SR1 1 +#define SR2 2 +#define SR3 3 +#define SR4 4 +#define SR5 5 +#define SR6 6 +#define SR7 7 +#define SR8 8 +#define SR9 9 +#define SR10 10 +#define SR11 11 +#define SR12 12 +#define SR13 13 +#define SR14 14 +#define SR15 15 + + +/* returns r3 = relocated address of sym */ +/* modifies r0 */ +#define RELOC_SYM(sym) \ + mflr r3; \ + bl 1f; \ +1: mflr r0; \ + mtlr r3; \ + lis r3, 1b@ha; \ + ori r3, r3, 1b@l; \ + subf r0, r3, r0; \ + lis r3, sym@ha; \ + ori r3, r3, sym@l; \ + add r3, r3, r0 + diff --git a/kexec/arch/ppc64/Makefile b/kexec/arch/ppc64/Makefile new file mode 100644 index 0000000..9caf501 --- /dev/null +++ b/kexec/arch/ppc64/Makefile @@ -0,0 +1,26 @@ +# +# kexec ppc64 (linux booting linux) +# +include $(srcdir)/kexec/libfdt/Makefile.libfdt + +ppc64_KEXEC_SRCS = kexec/arch/ppc64/kexec-elf-rel-ppc64.c +ppc64_KEXEC_SRCS += kexec/arch/ppc64/kexec-zImage-ppc64.c +ppc64_KEXEC_SRCS += kexec/arch/ppc64/kexec-elf-ppc64.c +ppc64_KEXEC_SRCS += kexec/arch/ppc64/kexec-ppc64.c +ppc64_KEXEC_SRCS += kexec/arch/ppc64/crashdump-ppc64.c +ppc64_KEXEC_SRCS += kexec/arch/ppc64/fdt.c +ppc64_KEXEC_SRCS += $(LIBFDT_SRCS:%=kexec/libfdt/%) + +ppc64_ARCH_REUSE_INITRD = + +ppc64_FS2DT = kexec/fs2dt.c +ppc64_FS2DT_INCLUDE = -include $(srcdir)/kexec/arch/ppc64/crashdump-ppc64.h \ + -include $(srcdir)/kexec/arch/ppc64/kexec-ppc64.h + +ppc64_CPPFLAGS = -I$(srcdir)/kexec/libfdt + +dist += kexec/arch/ppc64/Makefile $(ppc64_KEXEC_SRCS) \ + kexec/arch/ppc64/kexec-ppc64.h kexec/arch/ppc64/crashdump-ppc64.h \ + kexec/arch/ppc64/include/arch/fdt.h \ + kexec/arch/ppc64/include/arch/options.h + diff --git a/kexec/arch/ppc64/crashdump-ppc64.c b/kexec/arch/ppc64/crashdump-ppc64.c new file mode 100644 index 0000000..6d47898 --- /dev/null +++ b/kexec/arch/ppc64/crashdump-ppc64.c @@ -0,0 +1,644 @@ +/* + * kexec: Linux boots Linux + * + * Created by: R Sharada (sharada@in.ibm.com) + * Copyright (C) IBM Corporation, 2005. All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation (version 2 of the License). + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <limits.h> +#include <elf.h> +#include <dirent.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include "../../kexec.h" +#include "../../kexec-elf.h" +#include "../../kexec-syscall.h" +#include "../../crashdump.h" +#include "kexec-ppc64.h" +#include "../../fs2dt.h" +#include "crashdump-ppc64.h" + +#define DEVTREE_CRASHKERNEL_BASE "/proc/device-tree/chosen/linux,crashkernel-base" +#define DEVTREE_CRASHKERNEL_SIZE "/proc/device-tree/chosen/linux,crashkernel-size" + +unsigned int num_of_lmb_sets; +unsigned int is_dyn_mem_v2; +uint64_t lmb_size; + +static struct crash_elf_info elf_info64 = +{ + class: ELFCLASS64, +#if BYTE_ORDER == LITTLE_ENDIAN + data: ELFDATA2LSB, +#else + data: ELFDATA2MSB, +#endif + machine: EM_PPC64, + page_offset: PAGE_OFFSET, + lowmem_limit: MAXMEM, +}; + +static struct crash_elf_info elf_info32 = +{ + class: ELFCLASS32, + data: ELFDATA2MSB, + machine: EM_PPC64, + page_offset: PAGE_OFFSET, + lowmem_limit: MAXMEM, +}; + +extern struct arch_options_t arch_options; + +/* Stores a sorted list of RAM memory ranges for which to create elf headers. + * A separate program header is created for backup region + */ +static struct memory_range *crash_memory_range = NULL; + +/* Define a variable to replace the CRASH_MAX_MEMORY_RANGES macro */ +static int crash_max_memory_ranges; + +/* + * Used to save various memory ranges/regions needed for the captured + * kernel to boot. (lime memmap= option in other archs) + */ +mem_rgns_t usablemem_rgns = {0, NULL}; + +static unsigned long long cstart, cend; +static int memory_ranges; + +/* + * Exclude the region that lies within crashkernel and above the memory + * limit which is reflected by mem= kernel option. + */ +static void exclude_crash_region(uint64_t start, uint64_t end) +{ + /* If memory_limit is set then exclude the memory region above it. */ + if (memory_limit) { + if (start >= memory_limit) + return; + if (end > memory_limit) + end = memory_limit; + } + + if (cstart < end && cend > start) { + if (start < cstart && end > cend) { + crash_memory_range[memory_ranges].start = start; + crash_memory_range[memory_ranges].end = cstart; + crash_memory_range[memory_ranges].type = RANGE_RAM; + memory_ranges++; + crash_memory_range[memory_ranges].start = cend; + crash_memory_range[memory_ranges].end = end; + crash_memory_range[memory_ranges].type = RANGE_RAM; + memory_ranges++; + } else if (start < cstart) { + crash_memory_range[memory_ranges].start = start; + crash_memory_range[memory_ranges].end = cstart; + crash_memory_range[memory_ranges].type = RANGE_RAM; + memory_ranges++; + } else if (end > cend) { + crash_memory_range[memory_ranges].start = cend; + crash_memory_range[memory_ranges].end = end; + crash_memory_range[memory_ranges].type = RANGE_RAM; + memory_ranges++; + } + } else { + crash_memory_range[memory_ranges].start = start; + crash_memory_range[memory_ranges].end = end; + crash_memory_range[memory_ranges].type = RANGE_RAM; + memory_ranges++; + } +} + +static int get_dyn_reconf_crash_memory_ranges(void) +{ + uint64_t start, end; + uint64_t startrange, endrange; + uint64_t size; + char fname[128], buf[32]; + FILE *file; + unsigned int i; + int n; + uint32_t flags; + + strcpy(fname, "/proc/device-tree/"); + strcat(fname, "ibm,dynamic-reconfiguration-memory/ibm,dynamic-memory"); + if (is_dyn_mem_v2) + strcat(fname, "-v2"); + if ((file = fopen(fname, "r")) == NULL) { + perror(fname); + return -1; + } + + fseek(file, 4, SEEK_SET); + startrange = endrange = 0; + size = lmb_size; + for (i = 0; i < num_of_lmb_sets; i++) { + if ((n = fread(buf, 1, LMB_ENTRY_SIZE, file)) < 0) { + perror(fname); + fclose(file); + return -1; + } + if (memory_ranges >= (max_memory_ranges + 1)) { + /* No space to insert another element. */ + fprintf(stderr, + "Error: Number of crash memory ranges" + " excedeed the max limit\n"); + fclose(file); + return -1; + } + + /* + * If the property is ibm,dynamic-memory-v2, the first 4 bytes + * tell the number of sequential LMBs in this entry. + */ + if (is_dyn_mem_v2) + size = be32_to_cpu(((unsigned int *)buf)[0]) * lmb_size; + + start = be64_to_cpu(*((uint64_t *)&buf[DRCONF_ADDR])); + end = start + size; + if (start == 0 && end >= (BACKUP_SRC_END + 1)) + start = BACKUP_SRC_END + 1; + + flags = be32_to_cpu((*((uint32_t *)&buf[DRCONF_FLAGS]))); + /* skip this block if the reserved bit is set in flags (0x80) + or if the block is not assigned to this partition (0x8) */ + if ((flags & 0x80) || !(flags & 0x8)) + continue; + + if (start != endrange) { + if (startrange != endrange) + exclude_crash_region(startrange, endrange); + startrange = start; + } + endrange = end; + } + if (startrange != endrange) + exclude_crash_region(startrange, endrange); + + fclose(file); + return 0; +} + +/* + * For a given memory node, check if it is mapped to system RAM or + * to onboard memory on accelerator device like GPU card or such. + */ +static int is_coherent_device_mem(const char *fname) +{ + char fpath[PATH_LEN]; + char buf[32]; + DIR *dmem; + FILE *file; + struct dirent *mentry; + int cnt, ret = 0; + + strcpy(fpath, fname); + if ((dmem = opendir(fpath)) == NULL) { + perror(fpath); + return -1; + } + + while ((mentry = readdir(dmem)) != NULL) { + if (strcmp(mentry->d_name, "compatible")) + continue; + + strcat(fpath, "/compatible"); + if ((file = fopen(fpath, "r")) == NULL) { + perror(fpath); + ret = -1; + break; + } + if ((cnt = fread(buf, 1, 32, file)) < 0) { + perror(fpath); + fclose(file); + ret = -1; + break; + } + if (!strncmp(buf, "ibm,coherent-device-memory", 26)) { + fclose(file); + ret = 1; + break; + } + fclose(file); + } + + closedir(dmem); + return ret; +} + + +/* Reads the appropriate file and retrieves the SYSTEM RAM regions for whom to + * create Elf headers. Keeping it separate from get_memory_ranges() as + * requirements are different in the case of normal kexec and crashdumps. + * + * Normal kexec needs to look at all of available physical memory irrespective + * of the fact how much of it is being used by currently running kernel. + * Crashdumps need to have access to memory regions actually being used by + * running kernel. Expecting a different file/data structure than /proc/iomem + * to look into down the line. May be something like /proc/kernelmem or may + * be zone data structures exported from kernel. + */ +static int get_crash_memory_ranges(struct memory_range **range, int *ranges) +{ + + char device_tree[256] = "/proc/device-tree/"; + char fname[PATH_LEN]; + char buf[MAXBYTES]; + DIR *dir, *dmem; + FILE *file; + struct dirent *dentry, *mentry; + int n, ret, crash_rng_len = 0; + unsigned long long start, end; + int page_size; + + crash_max_memory_ranges = max_memory_ranges + 6; + crash_rng_len = sizeof(struct memory_range) * crash_max_memory_ranges; + + crash_memory_range = (struct memory_range *) malloc(crash_rng_len); + if (!crash_memory_range) { + fprintf(stderr, "Allocation for crash memory range failed\n"); + return -1; + } + memset(crash_memory_range, 0, crash_rng_len); + + /* create a separate program header for the backup region */ + crash_memory_range[0].start = BACKUP_SRC_START; + crash_memory_range[0].end = BACKUP_SRC_END + 1; + crash_memory_range[0].type = RANGE_RAM; + memory_ranges++; + + if ((dir = opendir(device_tree)) == NULL) { + perror(device_tree); + goto err; + } + + cstart = crash_base; + cend = crash_base + crash_size; + + while ((dentry = readdir(dir)) != NULL) { + if (!strncmp(dentry->d_name, + "ibm,dynamic-reconfiguration-memory", 35)){ + get_dyn_reconf_crash_memory_ranges(); + continue; + } + if (strncmp(dentry->d_name, "memory@", 7) && + strcmp(dentry->d_name, "memory")) + continue; + strcpy(fname, device_tree); + strcat(fname, dentry->d_name); + + ret = is_coherent_device_mem(fname); + if (ret == -1) { + closedir(dir); + goto err; + } else if (ret == 1) { + /* + * Avoid adding this memory region as it is not + * mapped to system RAM. + */ + continue; + } + + if ((dmem = opendir(fname)) == NULL) { + perror(fname); + closedir(dir); + goto err; + } + while ((mentry = readdir(dmem)) != NULL) { + if (strcmp(mentry->d_name, "reg")) + continue; + strcat(fname, "/reg"); + if ((file = fopen(fname, "r")) == NULL) { + perror(fname); + closedir(dmem); + closedir(dir); + goto err; + } + if ((n = fread(buf, 1, MAXBYTES, file)) < 0) { + perror(fname); + fclose(file); + closedir(dmem); + closedir(dir); + goto err; + } + if (memory_ranges >= (max_memory_ranges + 1)) { + /* No space to insert another element. */ + fprintf(stderr, + "Error: Number of crash memory ranges" + " excedeed the max limit\n"); + goto err; + } + + start = be64_to_cpu(((unsigned long long *)buf)[0]); + end = start + + be64_to_cpu(((unsigned long long *)buf)[1]); + if (start == 0 && end >= (BACKUP_SRC_END + 1)) + start = BACKUP_SRC_END + 1; + + exclude_crash_region(start, end); + fclose(file); + } + closedir(dmem); + } + closedir(dir); + + /* + * If RTAS region is overlapped with crashkernel, need to create ELF + * Program header for the overlapped memory. + */ + if (crash_base < rtas_base + rtas_size && + rtas_base < crash_base + crash_size) { + page_size = getpagesize(); + cstart = rtas_base; + cend = rtas_base + rtas_size; + if (cstart < crash_base) + cstart = crash_base; + if (cend > crash_base + crash_size) + cend = crash_base + crash_size; + /* + * The rtas section created here is formed by reading rtas-base + * and rtas-size from /proc/device-tree/rtas. Unfortunately + * rtas-size is not required to be a multiple of PAGE_SIZE + * The remainder of the page it ends on is just garbage, and is + * safe to read, its just not accounted in rtas-size. Since + * we're creating an elf section here though, lets round it up + * to the next page size boundary though, so makedumpfile can + * read it safely without going south on us. + */ + cend = _ALIGN(cend, page_size); + + crash_memory_range[memory_ranges].start = cstart; + crash_memory_range[memory_ranges++].end = cend; + } + + /* + * If OPAL region is overlapped with crashkernel, need to create ELF + * Program header for the overlapped memory. + */ + if (crash_base < opal_base + opal_size && + opal_base < crash_base + crash_size) { + page_size = getpagesize(); + cstart = opal_base; + cend = opal_base + opal_size; + if (cstart < crash_base) + cstart = crash_base; + if (cend > crash_base + crash_size) + cend = crash_base + crash_size; + /* + * The opal section created here is formed by reading opal-base + * and opal-size from /proc/device-tree/ibm,opal. Unfortunately + * opal-size is not required to be a multiple of PAGE_SIZE + * The remainder of the page it ends on is just garbage, and is + * safe to read, its just not accounted in opal-size. Since + * we're creating an elf section here though, lets round it up + * to the next page size boundary though, so makedumpfile can + * read it safely without going south on us. + */ + cend = _ALIGN(cend, page_size); + + crash_memory_range[memory_ranges].start = cstart; + crash_memory_range[memory_ranges++].end = cend; + } + *range = crash_memory_range; + *ranges = memory_ranges; + + int j; + dbgprintf("CRASH MEMORY RANGES\n"); + for(j = 0; j < *ranges; j++) { + start = crash_memory_range[j].start; + end = crash_memory_range[j].end; + dbgprintf("%016Lx-%016Lx\n", start, end); + } + + return 0; + +err: + if (crash_memory_range) + free(crash_memory_range); + return -1; +} + +static int add_cmdline_param(char *cmdline, uint64_t addr, char *cmdstr, + char *byte) +{ + int cmdline_size, cmdlen, len, align = 1024; + char str[COMMAND_LINE_SIZE], *ptr; + + /* Passing in =xxxK / =xxxM format. Saves space required in cmdline.*/ + switch (byte[0]) { + case 'K': + if (addr%align) + return -1; + addr = addr/align; + break; + case 'M': + addr = addr/(align *align); + break; + } + ptr = str; + strcpy(str, cmdstr); + ptr += strlen(str); + ultoa(addr, ptr); + strcat(str, byte); + len = strlen(str); + cmdlen = strlen(cmdline) + len; + cmdline_size = COMMAND_LINE_SIZE; + if (cmdlen > (cmdline_size - 1)) + die("Command line overflow\n"); + strcat(cmdline, str); + dbgprintf("Command line after adding elfcorehdr: %s\n", cmdline); + return 0; +} + +/* Loads additional segments in case of a panic kernel is being loaded. + * One segment for backup region, another segment for storing elf headers + * for crash memory image. + */ +int load_crashdump_segments(struct kexec_info *info, char* mod_cmdline, + uint64_t max_addr, unsigned long min_base) +{ + void *tmp; + unsigned long sz; + uint64_t elfcorehdr; + int nr_ranges, align = 1024, i; + unsigned long long end; + struct memory_range *mem_range; + + if (get_crash_memory_ranges(&mem_range, &nr_ranges) < 0) + return -1; + + info->backup_src_start = BACKUP_SRC_START; + info->backup_src_size = BACKUP_SRC_SIZE; + /* Create a backup region segment to store backup data*/ + sz = _ALIGN(BACKUP_SRC_SIZE, align); + tmp = xmalloc(sz); + memset(tmp, 0, sz); + info->backup_start = add_buffer(info, tmp, sz, sz, align, + 0, max_addr, 1); + reserve(info->backup_start, sz); + + /* On ppc64 memory ranges in device-tree is denoted as start + * and size rather than start and end, as is the case with + * other architectures like i386 . Because of this when loading + * the memory ranges in crashdump-elf.c the filesz calculation + * [ end - start + 1 ] goes for a toss. + * + * To be in sync with other archs adjust the end value for + * every crash memory range before calling the generic function + */ + + for (i = 0; i < nr_ranges; i++) { + end = crash_memory_range[i].end - 1; + crash_memory_range[i].end = end; + } + + + /* Create elf header segment and store crash image data. */ + if (arch_options.core_header_type == CORE_TYPE_ELF64) { + if (crash_create_elf64_headers(info, &elf_info64, + crash_memory_range, nr_ranges, + &tmp, &sz, + ELF_CORE_HEADER_ALIGN) < 0) { + free (tmp); + return -1; + } + } + else { + if (crash_create_elf32_headers(info, &elf_info32, + crash_memory_range, nr_ranges, + &tmp, &sz, + ELF_CORE_HEADER_ALIGN) < 0) { + free(tmp); + return -1; + } + } + + elfcorehdr = add_buffer(info, tmp, sz, sz, align, min_base, + max_addr, 1); + reserve(elfcorehdr, sz); + /* modify and store the cmdline in a global array. This is later + * read by flatten_device_tree and modified if required + */ + add_cmdline_param(mod_cmdline, elfcorehdr, " elfcorehdr=", "K"); + return 0; +} + +/* + * Used to save various memory regions needed for the captured kernel. + */ + +void add_usable_mem_rgns(unsigned long long base, unsigned long long size) +{ + unsigned int i; + unsigned long long end = base + size; + unsigned long long ustart, uend; + + base = _ALIGN_DOWN(base, getpagesize()); + end = _ALIGN_UP(end, getpagesize()); + + for (i=0; i < usablemem_rgns.size; i++) { + ustart = usablemem_rgns.ranges[i].start; + uend = usablemem_rgns.ranges[i].end; + if (base < uend && end > ustart) { + if ((base >= ustart) && (end <= uend)) + return; + if (base < ustart && end > uend) { + usablemem_rgns.ranges[i].start = base; + usablemem_rgns.ranges[i].end = end; +#ifdef DEBUG + fprintf(stderr, "usable memory rgn %u: new base:%llx new size:%llx\n", + i, base, size); +#endif + return; + } else if (base < ustart) { + usablemem_rgns.ranges[i].start = base; +#ifdef DEBUG + fprintf(stderr, "usable memory rgn %u: new base:%llx new size:%llx", + i, base, usablemem_rgns.ranges[i].end - base); +#endif + return; + } else if (end > uend){ + usablemem_rgns.ranges[i].end = end; +#ifdef DEBUG + fprintf(stderr, "usable memory rgn %u: new end:%llx, new size:%llx", + i, end, end - usablemem_rgns.ranges[i].start); +#endif + return; + } + } + } + usablemem_rgns.ranges[usablemem_rgns.size].start = base; + usablemem_rgns.ranges[usablemem_rgns.size++].end = end; + + dbgprintf("usable memory rgns size:%u base:%llx size:%llx\n", + usablemem_rgns.size, base, size); +} + +int get_crash_kernel_load_range(uint64_t *start, uint64_t *end) +{ + unsigned long long value; + + if (!get_devtree_value(DEVTREE_CRASHKERNEL_BASE, &value)) + *start = be64_to_cpu(value); + else + return -1; + + if (!get_devtree_value(DEVTREE_CRASHKERNEL_SIZE, &value)) + *end = *start + be64_to_cpu(value) - 1; + else + return -1; + + return 0; +} + +int is_crashkernel_mem_reserved(void) +{ + int fd; + + fd = open(DEVTREE_CRASHKERNEL_BASE, O_RDONLY); + if (fd < 0) + return 0; + close(fd); + return 1; +} + +#if 0 +static int sort_regions(mem_rgns_t *rgn) +{ + int i, j; + unsigned long long tstart, tend; + for (i = 0; i < rgn->size; i++) { + for (j = 0; j < rgn->size - i - 1; j++) { + if (rgn->ranges[j].start > rgn->ranges[j+1].start) { + tstart = rgn->ranges[j].start; + tend = rgn->ranges[j].end; + rgn->ranges[j].start = rgn->ranges[j+1].start; + rgn->ranges[j].end = rgn->ranges[j+1].end; + rgn->ranges[j+1].start = tstart; + rgn->ranges[j+1].end = tend; + } + } + } + return 0; + +} +#endif + diff --git a/kexec/arch/ppc64/crashdump-ppc64.h b/kexec/arch/ppc64/crashdump-ppc64.h new file mode 100644 index 0000000..b0cba8a --- /dev/null +++ b/kexec/arch/ppc64/crashdump-ppc64.h @@ -0,0 +1,51 @@ +#ifndef CRASHDUMP_PPC64_H +#define CRASHDUMP_PPC64_H + +#include <stdint.h> +#include <sys/types.h> + +struct kexec_info; +int load_crashdump_segments(struct kexec_info *info, char *mod_cmdline, + uint64_t max_addr, unsigned long min_base); +void add_usable_mem_rgns(unsigned long long base, unsigned long long size); + +#define PAGE_OFFSET 0xC000000000000000ULL +#define KERNELBASE PAGE_OFFSET +#define VMALLOCBASE 0xD000000000000000ULL + +#define __pa(x) ((unsigned long)(x)-PAGE_OFFSET) +#define MAXMEM (-(unsigned long)(KERNELBASE-VMALLOCBASE)) + +#define COMMAND_LINE_SIZE 2048 /* from kernel */ +/* Backup Region, First 64K of System RAM. */ +#define BACKUP_SRC_START 0x0000 +#define BACKUP_SRC_END 0xffff +#define BACKUP_SRC_SIZE (BACKUP_SRC_END - BACKUP_SRC_START + 1) + +#define KDUMP_BACKUP_LIMIT BACKUP_SRC_SIZE + +#define KERNEL_RUN_AT_ZERO_MAGIC 0x72756e30 /* "run0" */ + +extern uint64_t crash_base; +extern uint64_t crash_size; +extern uint64_t memory_limit; +extern unsigned int rtas_base; +extern unsigned int rtas_size; +extern uint64_t opal_base; +extern uint64_t opal_size; + +/* + * In case of ibm,dynamic-memory-v2 property, this is the number of LMB + * sets where each set represents a group of sequential LMB entries. In + * case of ibm,dynamic-memory property, the number of LMB sets is nothing + * but the total number of LMB entries. + */ +extern unsigned int num_of_lmb_sets; +extern unsigned int is_dyn_mem_v2; +extern uint64_t lmb_size; + +#define LMB_ENTRY_SIZE 24 +#define DRCONF_ADDR (is_dyn_mem_v2 ? 4 : 0) +#define DRCONF_FLAGS 20 + +#endif /* CRASHDUMP_PPC64_H */ diff --git a/kexec/arch/ppc64/fdt.c b/kexec/arch/ppc64/fdt.c new file mode 100644 index 0000000..8bc6d2d --- /dev/null +++ b/kexec/arch/ppc64/fdt.c @@ -0,0 +1,78 @@ +/* + * ppc64 fdt fixups + * + * Copyright 2015 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation (version 2 of the License). + * + * 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. + */ + +#include <arch/fdt.h> +#include <libfdt.h> +#include <stdio.h> +#include <stdlib.h> + +/* + * Let the kernel know it booted from kexec, as some things (e.g. + * secondary CPU release) may work differently. + */ +static int fixup_kexec_prop(void *fdt) +{ + int err, nodeoffset; + + nodeoffset = fdt_subnode_offset(fdt, 0, "chosen"); + if (nodeoffset < 0) + nodeoffset = fdt_add_subnode(fdt, 0, "chosen"); + if (nodeoffset < 0) { + printf("%s: add /chosen %s\n", __func__, + fdt_strerror(nodeoffset)); + return -1; + } + + err = fdt_setprop(fdt, nodeoffset, "linux,booted-from-kexec", + NULL, 0); + if (err < 0) { + printf("%s: couldn't write linux,booted-from-kexec: %s\n", + __func__, fdt_strerror(err)); + return -1; + } + + return 0; +} + + +/* + * For now, assume that the added content fits in the file. + * This should be the case when flattening from /proc/device-tree, + * and when passing in a dtb, dtc can be told to add padding. + */ +int fixup_dt(char **fdt, off_t *size) +{ + int ret; + + *size += 4096; + *fdt = realloc(*fdt, *size); + if (!*fdt) { + fprintf(stderr, "%s: out of memory\n", __func__); + return -1; + } + + ret = fdt_open_into(*fdt, *fdt, *size); + if (ret < 0) { + fprintf(stderr, "%s: fdt_open_into: %s\n", __func__, + fdt_strerror(ret)); + return -1; + } + + ret = fixup_kexec_prop(*fdt); + if (ret < 0) + return ret; + + return 0; +} diff --git a/kexec/arch/ppc64/include/arch/fdt.h b/kexec/arch/ppc64/include/arch/fdt.h new file mode 100644 index 0000000..b19f185 --- /dev/null +++ b/kexec/arch/ppc64/include/arch/fdt.h @@ -0,0 +1,8 @@ +#ifndef KEXEC_ARCH_PPC64_FDT +#define KEXEC_ARCH_PPC64_FDT + +#include <sys/types.h> + +int fixup_dt(char **fdt, off_t *size); + +#endif diff --git a/kexec/arch/ppc64/include/arch/options.h b/kexec/arch/ppc64/include/arch/options.h new file mode 100644 index 0000000..2bca96a --- /dev/null +++ b/kexec/arch/ppc64/include/arch/options.h @@ -0,0 +1,51 @@ +#ifndef KEXEC_ARCH_PPC64_OPTIONS_H +#define KEXEC_ARCH_PPC64_OPTIONS_H + +#define OPT_ELF64_CORE (OPT_MAX+0) +#define OPT_DT_NO_OLD_ROOT (OPT_MAX+1) +#define OPT_ARCH_MAX (OPT_MAX+2) + +/* All 'local' loader options: */ +#define OPT_APPEND (OPT_ARCH_MAX+0) +#define OPT_RAMDISK (OPT_ARCH_MAX+1) +#define OPT_DEVICETREEBLOB (OPT_ARCH_MAX+2) +#define OPT_ARGS_IGNORE (OPT_ARCH_MAX+3) +#define OPT_REUSE_CMDLINE (OPT_ARCH_MAX+4) + +/* Options relevant to the architecture (excluding loader-specific ones): */ +#define KEXEC_ARCH_OPTIONS \ + KEXEC_OPTIONS \ + { "elf64-core-headers", 0, 0, OPT_ELF64_CORE }, \ + { "dt-no-old-root", 0, 0, OPT_DT_NO_OLD_ROOT }, \ + +#define KEXEC_ARCH_OPT_STR KEXEC_OPT_STR "" + +/* The following two #defines list ALL of the options added by all of the + * architecture's loaders. + * o main() uses this complete list to scan for its options, ignoring + * arch-specific/loader-specific ones. + * o Then, arch_process_options() uses this complete list to scan for its + * options, ignoring general/loader-specific ones. + * o Then, the file_type[n].load re-scans for options, using + * KEXEC_ARCH_OPTIONS plus its loader-specific options subset. + * Any unrecognised options cause an error here. + * + * This is done so that main()'s/arch_process_options()'s getopt_long() calls + * don't choose a kernel filename from random arguments to options they don't + * recognise -- as they now recognise (if not act upon) all possible options. + */ +#define KEXEC_ALL_OPTIONS \ + KEXEC_ARCH_OPTIONS \ + { "command-line", 1, NULL, OPT_APPEND }, \ + { "append", 1, NULL, OPT_APPEND }, \ + { "ramdisk", 1, NULL, OPT_RAMDISK }, \ + { "initrd", 1, NULL, OPT_RAMDISK }, \ + { "devicetreeblob", 1, NULL, OPT_DEVICETREEBLOB }, \ + { "dtb", 1, NULL, OPT_DEVICETREEBLOB }, \ + { "args-linux", 0, NULL, OPT_ARGS_IGNORE }, \ + { "reuse-cmdline", 0, NULL, OPT_REUSE_CMDLINE }, + +#define KEXEC_ALL_OPT_STR KEXEC_OPT_STR + + +#endif /* KEXEC_ARCH_PPC64_OPTIONS_H */ diff --git a/kexec/arch/ppc64/kexec-elf-ppc64.c b/kexec/arch/ppc64/kexec-elf-ppc64.c new file mode 100644 index 0000000..01d045f --- /dev/null +++ b/kexec/arch/ppc64/kexec-elf-ppc64.c @@ -0,0 +1,496 @@ +/* + * kexec: Linux boots Linux + * + * Copyright (C) 2004 Adam Litke (agl@us.ibm.com) + * Copyright (C) 2004 IBM Corp. + * Copyright (C) 2005 R Sharada (sharada@in.ibm.com) + * Copyright (C) 2006 Mohan Kumar M (mohan@in.ibm.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation (version 2 of the License). + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#define _GNU_SOURCE +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <getopt.h> +#include <linux/elf.h> +#include "../../kexec.h" +#include "../../kexec-elf.h" +#include "../../kexec-syscall.h" +#include "kexec-ppc64.h" +#include "../../fs2dt.h" +#include "crashdump-ppc64.h" +#include <libfdt.h> +#include <arch/fdt.h> +#include <arch/options.h> + +uint64_t initrd_base, initrd_size; +unsigned char reuse_initrd = 0; +const char *ramdisk; + +int elf_ppc64_probe(const char *buf, off_t len) +{ + struct mem_ehdr ehdr; + int result; + result = build_elf_exec_info(buf, len, &ehdr, 0); + if (result < 0) { + goto out; + } + + /* Verify the architecuture specific bits */ + if ((ehdr.e_machine != EM_PPC64) && (ehdr.e_machine != EM_PPC)) { + /* for a different architecture */ + result = -1; + goto out; + } + result = 0; + out: + free_elf_info(&ehdr); + return result; +} + +void arch_reuse_initrd(void) +{ + reuse_initrd = 1; +} + +static int read_prop(char *name, void *value, size_t len) +{ + int fd; + size_t rlen; + + fd = open(name, O_RDONLY); + if (fd == -1) + return -1; + + rlen = read(fd, value, len); + if (rlen < 0) + fprintf(stderr, "Warning : Can't read %s : %s", + name, strerror(errno)); + else if (rlen != len) + fprintf(stderr, "Warning : short read from %s", name); + + close(fd); + return 0; +} + +static int elf_ppc64_load_file(int argc, char **argv, struct kexec_info *info) +{ + int ret = 0; + char *cmdline, *dtb; + char *append_cmdline = NULL; + char *reuse_cmdline = NULL; + int opt, cmdline_len = 0; + + /* See options.h -- add any more there, too. */ + static const struct option options[] = { + KEXEC_ARCH_OPTIONS + { "command-line", 1, NULL, OPT_APPEND }, + { "append", 1, NULL, OPT_APPEND }, + { "ramdisk", 1, NULL, OPT_RAMDISK }, + { "initrd", 1, NULL, OPT_RAMDISK }, + { "devicetreeblob", 1, NULL, OPT_DEVICETREEBLOB }, + { "dtb", 1, NULL, OPT_DEVICETREEBLOB }, + { "args-linux", 0, NULL, OPT_ARGS_IGNORE }, + { "reuse-cmdline", 0, NULL, OPT_REUSE_CMDLINE}, + { 0, 0, NULL, 0 }, + }; + + static const char short_options[] = KEXEC_OPT_STR ""; + + /* Parse command line arguments */ + cmdline = 0; + dtb = 0; + ramdisk = 0; + + while ((opt = getopt_long(argc, argv, short_options, + options, 0)) != -1) { + switch (opt) { + default: + /* Ignore core options */ + if (opt < OPT_ARCH_MAX) + break; + case OPT_APPEND: + append_cmdline = optarg; + break; + case OPT_RAMDISK: + ramdisk = optarg; + break; + case OPT_DEVICETREEBLOB: + dtb = optarg; + break; + case OPT_ARGS_IGNORE: + break; + case OPT_REUSE_CMDLINE: + reuse_cmdline = get_command_line(); + break; + } + } + + if (dtb) + die("--dtb not supported while using --kexec-file-syscall.\n"); + + if (reuse_initrd) + die("--reuseinitrd not supported with --kexec-file-syscall.\n"); + + cmdline = concat_cmdline(reuse_cmdline, append_cmdline); + if (!reuse_cmdline) + free(reuse_cmdline); + + if (cmdline) { + cmdline_len = strlen(cmdline) + 1; + } else { + cmdline = strdup("\0"); + cmdline_len = 1; + } + + if (ramdisk) { + info->initrd_fd = open(ramdisk, O_RDONLY); + if (info->initrd_fd == -1) { + fprintf(stderr, "Could not open initrd file %s:%s\n", + ramdisk, strerror(errno)); + ret = -1; + goto out; + } + } + + info->command_line = cmdline; + info->command_line_len = cmdline_len; + return ret; +out: + if (cmdline_len == 1) + free(cmdline); + return ret; +} + +int elf_ppc64_load(int argc, char **argv, const char *buf, off_t len, + struct kexec_info *info) +{ + struct mem_ehdr ehdr; + char *cmdline, *modified_cmdline = NULL; + char *reuse_cmdline = NULL; + char *append_cmdline = NULL; + const char *devicetreeblob; + uint64_t max_addr, hole_addr; + char *seg_buf = NULL; + off_t seg_size = 0; + struct mem_phdr *phdr; + size_t size; +#ifdef NEED_RESERVE_DTB + uint64_t *rsvmap_ptr; + struct bootblock *bb_ptr; +#endif + int result, opt; + uint64_t my_kernel, my_dt_offset; + uint64_t my_opal_base = 0, my_opal_entry = 0; + unsigned int my_panic_kernel; + uint64_t my_stack, my_backup_start; + uint64_t toc_addr; + uint32_t my_run_at_load; + unsigned int slave_code[256/sizeof (unsigned int)], master_entry; + + /* See options.h -- add any more there, too. */ + static const struct option options[] = { + KEXEC_ARCH_OPTIONS + { "command-line", 1, NULL, OPT_APPEND }, + { "append", 1, NULL, OPT_APPEND }, + { "ramdisk", 1, NULL, OPT_RAMDISK }, + { "initrd", 1, NULL, OPT_RAMDISK }, + { "devicetreeblob", 1, NULL, OPT_DEVICETREEBLOB }, + { "dtb", 1, NULL, OPT_DEVICETREEBLOB }, + { "args-linux", 0, NULL, OPT_ARGS_IGNORE }, + { "reuse-cmdline", 0, NULL, OPT_REUSE_CMDLINE}, + { 0, 0, NULL, 0 }, + }; + + static const char short_options[] = KEXEC_OPT_STR ""; + + if (info->file_mode) + return elf_ppc64_load_file(argc, argv, info); + + /* Parse command line arguments */ + initrd_base = 0; + initrd_size = 0; + cmdline = 0; + ramdisk = 0; + devicetreeblob = 0; + max_addr = 0xFFFFFFFFFFFFFFFFULL; + hole_addr = 0; + + while ((opt = getopt_long(argc, argv, short_options, + options, 0)) != -1) { + switch (opt) { + default: + /* Ignore core options */ + if (opt < OPT_ARCH_MAX) + break; + case OPT_APPEND: + append_cmdline = optarg; + break; + case OPT_RAMDISK: + ramdisk = optarg; + break; + case OPT_DEVICETREEBLOB: + devicetreeblob = optarg; + break; + case OPT_ARGS_IGNORE: + break; + case OPT_REUSE_CMDLINE: + reuse_cmdline = get_command_line(); + break; + } + } + + cmdline = concat_cmdline(reuse_cmdline, append_cmdline); + if (!reuse_cmdline) + free(reuse_cmdline); + + if (!cmdline) + fprintf(stdout, "Warning: append= option is not passed. Using the first kernel root partition\n"); + + if (ramdisk && reuse_initrd) + die("Can't specify --ramdisk or --initrd with --reuseinitrd\n"); + + /* Need to append some command line parameters internally in case of + * taking crash dumps. + */ + if (info->kexec_flags & KEXEC_ON_CRASH) { + modified_cmdline = xmalloc(COMMAND_LINE_SIZE); + memset((void *)modified_cmdline, 0, COMMAND_LINE_SIZE); + if (cmdline) { + strncpy(modified_cmdline, cmdline, COMMAND_LINE_SIZE); + modified_cmdline[COMMAND_LINE_SIZE - 1] = '\0'; + } + } + + /* Parse the Elf file */ + result = build_elf_exec_info(buf, len, &ehdr, 0); + if (result < 0) { + free_elf_info(&ehdr); + return result; + } + + /* Load the Elf data. Physical load addresses in elf64 header do not + * show up correctly. Use user supplied address for now to patch the + * elf header + */ + + phdr = &ehdr.e_phdr[0]; + size = phdr->p_filesz; + if (size > phdr->p_memsz) + size = phdr->p_memsz; + + my_kernel = hole_addr = locate_hole(info, size, 0, 0, max_addr, 1); + ehdr.e_phdr[0].p_paddr = hole_addr; + result = elf_exec_load(&ehdr, info); + if (result < 0) { + free_elf_info(&ehdr); + return result; + } + + /* If panic kernel is being loaded, additional segments need + * to be created. + */ + if (info->kexec_flags & KEXEC_ON_CRASH) { + result = load_crashdump_segments(info, modified_cmdline, + max_addr, 0); + if (result < 0) + return -1; + /* Use new command line. */ + cmdline = modified_cmdline; + } + + /* Add v2wrap to the current image */ + elf_rel_build_load(info, &info->rhdr, purgatory, + purgatory_size, 0, max_addr, 1, 0); + + /* Add a ram-disk to the current image + * Note: Add the ramdisk after elf_rel_build_load + */ + if (ramdisk) { + if (devicetreeblob) { + fprintf(stderr, + "Can't use ramdisk with device tree blob input\n"); + return -1; + } + seg_buf = slurp_file(ramdisk, &seg_size); + hole_addr = add_buffer(info, seg_buf, seg_size, seg_size, + 0, 0, max_addr, 1); + initrd_base = hole_addr; + initrd_size = seg_size; + } /* ramdisk */ + + if (devicetreeblob) { + /* Grab device tree from buffer */ + seg_buf = slurp_file(devicetreeblob, &seg_size); + } else { + /* create from fs2dt */ + create_flatten_tree(&seg_buf, &seg_size, cmdline); + } + + result = fixup_dt(&seg_buf, &seg_size); + if (result < 0) + return result; + + my_dt_offset = add_buffer(info, seg_buf, seg_size, seg_size, + 0, 0, max_addr, -1); + +#ifdef NEED_RESERVE_DTB + /* patch reserve map address for flattened device-tree + * find last entry (both 0) in the reserve mem list. Assume DT + * entry is before this one + */ + bb_ptr = (struct bootblock *)(seg_buf); + rsvmap_ptr = (uint64_t *)(seg_buf + be32_to_cpu(bb_ptr->off_mem_rsvmap)); + while (*rsvmap_ptr || *(rsvmap_ptr+1)) + rsvmap_ptr += 2; + rsvmap_ptr -= 2; + *rsvmap_ptr = cpu_to_be64(my_dt_offset); + rsvmap_ptr++; + *rsvmap_ptr = cpu_to_be64((uint64_t)be32_to_cpu(bb_ptr->totalsize)); +#endif + + if (read_prop("/proc/device-tree/ibm,opal/opal-base-address", + &my_opal_base, sizeof(my_opal_base)) == 0) { + my_opal_base = be64_to_cpu(my_opal_base); + elf_rel_set_symbol(&info->rhdr, "opal_base", + &my_opal_base, sizeof(my_opal_base)); + } + + if (read_prop("/proc/device-tree/ibm,opal/opal-entry-address", + &my_opal_entry, sizeof(my_opal_entry)) == 0) { + my_opal_entry = be64_to_cpu(my_opal_entry); + elf_rel_set_symbol(&info->rhdr, "opal_entry", + &my_opal_entry, sizeof(my_opal_entry)); + } + + /* Set kernel */ + elf_rel_set_symbol(&info->rhdr, "kernel", &my_kernel, sizeof(my_kernel)); + + /* Set dt_offset */ + elf_rel_set_symbol(&info->rhdr, "dt_offset", &my_dt_offset, + sizeof(my_dt_offset)); + + /* get slave code from new kernel, put in purgatory */ + elf_rel_get_symbol(&info->rhdr, "purgatory_start", slave_code, + sizeof(slave_code)); + master_entry = slave_code[0]; + memcpy(slave_code, phdr->p_data, sizeof(slave_code)); + slave_code[0] = master_entry; + elf_rel_set_symbol(&info->rhdr, "purgatory_start", slave_code, + sizeof(slave_code)); + + if (info->kexec_flags & KEXEC_ON_CRASH) { + my_panic_kernel = 1; + /* Set panic flag */ + elf_rel_set_symbol(&info->rhdr, "panic_kernel", + &my_panic_kernel, sizeof(my_panic_kernel)); + + /* Set backup address */ + my_backup_start = info->backup_start; + elf_rel_set_symbol(&info->rhdr, "backup_start", + &my_backup_start, sizeof(my_backup_start)); + + /* Tell relocatable kernel to run at load address + * via word before slave code in purgatory + */ + + elf_rel_get_symbol(&info->rhdr, "run_at_load", &my_run_at_load, + sizeof(my_run_at_load)); + if (my_run_at_load == KERNEL_RUN_AT_ZERO_MAGIC) + my_run_at_load = 1; + /* else it should be a fixed offset image */ + elf_rel_set_symbol(&info->rhdr, "run_at_load", &my_run_at_load, + sizeof(my_run_at_load)); + } + + /* Set stack address */ + my_stack = locate_hole(info, 16*1024, 0, 0, max_addr, 1); + my_stack += 16*1024; + elf_rel_set_symbol(&info->rhdr, "stack", &my_stack, sizeof(my_stack)); + + /* Set toc */ + toc_addr = my_r2(&info->rhdr); + elf_rel_set_symbol(&info->rhdr, "my_toc", &toc_addr, sizeof(toc_addr)); + + /* Set debug */ + elf_rel_set_symbol(&info->rhdr, "debug", &my_debug, sizeof(my_debug)); + + my_kernel = 0; + my_dt_offset = 0; + my_panic_kernel = 0; + my_backup_start = 0; + my_stack = 0; + toc_addr = 0; + my_run_at_load = 0; + my_debug = 0; + my_opal_base = 0; + my_opal_entry = 0; + + elf_rel_get_symbol(&info->rhdr, "opal_base", &my_opal_base, + sizeof(my_opal_base)); + elf_rel_get_symbol(&info->rhdr, "opal_entry", &my_opal_entry, + sizeof(my_opal_entry)); + elf_rel_get_symbol(&info->rhdr, "kernel", &my_kernel, sizeof(my_kernel)); + elf_rel_get_symbol(&info->rhdr, "dt_offset", &my_dt_offset, + sizeof(my_dt_offset)); + elf_rel_get_symbol(&info->rhdr, "run_at_load", &my_run_at_load, + sizeof(my_run_at_load)); + elf_rel_get_symbol(&info->rhdr, "panic_kernel", &my_panic_kernel, + sizeof(my_panic_kernel)); + elf_rel_get_symbol(&info->rhdr, "backup_start", &my_backup_start, + sizeof(my_backup_start)); + elf_rel_get_symbol(&info->rhdr, "stack", &my_stack, sizeof(my_stack)); + elf_rel_get_symbol(&info->rhdr, "my_toc", &toc_addr, + sizeof(toc_addr)); + elf_rel_get_symbol(&info->rhdr, "debug", &my_debug, sizeof(my_debug)); + + dbgprintf("info->entry is %p\n", info->entry); + dbgprintf("kernel is %llx\n", (unsigned long long)my_kernel); + dbgprintf("dt_offset is %llx\n", + (unsigned long long)my_dt_offset); + dbgprintf("run_at_load flag is %x\n", my_run_at_load); + dbgprintf("panic_kernel is %x\n", my_panic_kernel); + dbgprintf("backup_start is %llx\n", + (unsigned long long)my_backup_start); + dbgprintf("stack is %llx\n", (unsigned long long)my_stack); + dbgprintf("toc_addr is %llx\n", (unsigned long long)toc_addr); + dbgprintf("purgatory size is %zu\n", purgatory_size); + dbgprintf("debug is %d\n", my_debug); + dbgprintf("opal_base is %llx\n", (unsigned long long) my_opal_base); + dbgprintf("opal_entry is %llx\n", (unsigned long long) my_opal_entry); + + return 0; +} + +void elf_ppc64_usage(void) +{ + fprintf(stderr, " --command-line=<Command line> command line to append.\n"); + fprintf(stderr, " --append=<Command line> same as --command-line.\n"); + fprintf(stderr, " --ramdisk=<filename> Initial RAM disk.\n"); + fprintf(stderr, " --initrd=<filename> same as --ramdisk.\n"); + fprintf(stderr, " --devicetreeblob=<filename> Specify device tree blob file.\n"); + fprintf(stderr, " "); + fprintf(stderr, "Not applicable while using --kexec-file-syscall.\n"); + fprintf(stderr, " --reuse-cmdline Use kernel command line from running system.\n"); + fprintf(stderr, " --dtb=<filename> same as --devicetreeblob.\n"); + + fprintf(stderr, "elf support is still broken\n"); +} diff --git a/kexec/arch/ppc64/kexec-elf-rel-ppc64.c b/kexec/arch/ppc64/kexec-elf-rel-ppc64.c new file mode 100644 index 0000000..51b1354 --- /dev/null +++ b/kexec/arch/ppc64/kexec-elf-rel-ppc64.c @@ -0,0 +1,204 @@ +#include <stdio.h> +#include <elf.h> +#include <string.h> +#include "../../kexec.h" +#include "../../kexec-elf.h" +#include "kexec-ppc64.h" + +#if defined(_CALL_ELF) && _CALL_ELF == 2 +#define STO_PPC64_LOCAL_BIT 5 +#define STO_PPC64_LOCAL_MASK (7 << STO_PPC64_LOCAL_BIT) +#define PPC64_LOCAL_ENTRY_OFFSET(other) \ + (((1 << (((other) & STO_PPC64_LOCAL_MASK) >> STO_PPC64_LOCAL_BIT)) >> 2) << 2) + +static unsigned int local_entry_offset(struct mem_sym *sym) +{ + /* If this symbol has a local entry point, use it. */ + return PPC64_LOCAL_ENTRY_OFFSET(sym->st_other); +} +#else +static unsigned int local_entry_offset(struct mem_sym *UNUSED(sym)) +{ + return 0; +} +#endif + +static struct mem_shdr *toc_section(const struct mem_ehdr *ehdr) +{ + struct mem_shdr *shdr, *shdr_end; + unsigned char *strtab; + + strtab = (unsigned char *)ehdr->e_shdr[ehdr->e_shstrndx].sh_data; + shdr_end = &ehdr->e_shdr[ehdr->e_shnum]; + for (shdr = ehdr->e_shdr; shdr != shdr_end; shdr++) { + if (shdr->sh_size && + strcmp((char *)&strtab[shdr->sh_name], ".toc") == 0) { + return shdr; + } + } + + return NULL; +} + +int machine_verify_elf_rel(struct mem_ehdr *ehdr) +{ + struct mem_shdr *toc; + + if (ehdr->ei_class != ELFCLASS64) { + return 0; + } + if (ehdr->e_machine != EM_PPC64) { + return 0; + } + + /* Ensure .toc is sufficiently aligned. */ + toc = toc_section(ehdr); + if (toc && toc->sh_addralign < 256) + toc->sh_addralign = 256; + return 1; +} + +/* r2 is the TOC pointer: it actually points 0x8000 into the TOC (this + gives the value maximum span in an instruction which uses a signed + offset) */ +unsigned long my_r2(const struct mem_ehdr *ehdr) +{ + struct mem_shdr *shdr; + + shdr = toc_section(ehdr); + if (!shdr) { + die("TOC reloc without a toc section?"); + } + + return shdr->sh_addr + 0x8000; +} + +static void do_relative_toc(unsigned long value, uint16_t *location, + unsigned long mask, int complain_signed) +{ + if (complain_signed && (value + 0x8000 > 0xffff)) { + die("TOC16 relocation overflows (%lu)\n", value); + } + + if ((~mask & 0xffff) & value) { + die("bad TOC16 relocation (%lu)\n", value); + } + + *location = (*location & ~mask) | (value & mask); +} + +void machine_apply_elf_rel(struct mem_ehdr *ehdr, struct mem_sym *sym, + unsigned long r_type, void *location, unsigned long address, + unsigned long value) +{ + switch(r_type) { + case R_PPC64_ADDR32: + /* Simply set it */ + *(uint32_t *)location = value; + break; + + case R_PPC64_ADDR64: + case R_PPC64_REL64: + /* Simply set it */ + *(uint64_t *)location = value; + break; + + case R_PPC64_REL32: + *(uint32_t *)location = value - (uint32_t)location; + break; + + case R_PPC64_TOC: + *(uint64_t *)location = my_r2(ehdr); + break; + + case R_PPC64_TOC16: + do_relative_toc(value - my_r2(ehdr), location, 0xffff, 1); + break; + + case R_PPC64_TOC16_DS: + do_relative_toc(value - my_r2(ehdr), location, 0xfffc, 1); + break; + + case R_PPC64_TOC16_LO: + do_relative_toc(value - my_r2(ehdr), location, 0xffff, 0); + break; + + case R_PPC64_TOC16_LO_DS: + do_relative_toc(value - my_r2(ehdr), location, 0xfffc, 0); + break; + + case R_PPC64_TOC16_HI: + do_relative_toc((value - my_r2(ehdr)) >> 16, location, + 0xffff, 0); + break; + + case R_PPC64_TOC16_HA: + do_relative_toc((value - my_r2(ehdr) + 0x8000) >> 16, location, + 0xffff, 0); + break; + + case R_PPC64_REL24: + value += local_entry_offset(sym); + /* Convert value to relative */ + value -= address; + if (value + 0x2000000 > 0x3ffffff || (value & 3) != 0) { + die("REL24 %li out of range!\n", (long int)value); + } + + /* Only replace bits 2 through 26 */ + *(uint32_t *)location = (*(uint32_t *)location & ~0x03fffffc) | + (value & 0x03fffffc); + break; + + case R_PPC64_ADDR16_LO: + *(uint16_t *)location = value & 0xffff; + break; + + case R_PPC64_ADDR16_HI: + *(uint16_t *)location = (value >> 16) & 0xffff; + break; + + case R_PPC64_ADDR16_HA: + *(uint16_t *)location = (((value + 0x8000) >> 16) & 0xffff); + break; + + case R_PPC64_ADDR16_HIGHER: + *(uint16_t *)location = (((uint64_t)value >> 32) & 0xffff); + break; + + case R_PPC64_ADDR16_HIGHEST: + *(uint16_t *)location = (((uint64_t)value >> 48) & 0xffff); + break; + + /* R_PPC64_REL16_HA and R_PPC64_REL16_LO are handled to support + * ABIv2 r2 assignment based on r12 for PIC executable. + * Here address is know so replace + * 0: addis 2,12,.TOC.-0b@ha + * addi 2,2,.TOC.-0b@l + * by + * lis 2,.TOC.@ha + * addi 2,2,.TOC.@l + */ + case R_PPC64_REL16_HA: + /* check that we are dealing with the addis 2,12 instruction */ + if (((*(uint32_t*)location) & 0xffff0000) != 0x3c4c0000) + die("Unexpected instruction for R_PPC64_REL16_HA"); + value += my_r2(ehdr); + /* replacing by lis 2 */ + *(uint32_t *)location = 0x3c400000 + ((value >> 16) & 0xffff); + break; + + case R_PPC64_REL16_LO: + /* check that we are dealing with the addi 2,2 instruction */ + if (((*(uint32_t*)location) & 0xffff0000) != 0x38420000) + die("Unexpected instruction for R_PPC64_REL16_LO"); + + value += my_r2(ehdr) - 4; + *(uint16_t *)location = value & 0xffff; + break; + + default: + die("Unknown rela relocation: %lu\n", r_type); + break; + } +} diff --git a/kexec/arch/ppc64/kexec-ppc64.c b/kexec/arch/ppc64/kexec-ppc64.c new file mode 100644 index 0000000..611809f --- /dev/null +++ b/kexec/arch/ppc64/kexec-ppc64.c @@ -0,0 +1,969 @@ +/* + * kexec: Linux boots Linux + * + * Copyright (C) 2003-2005 Eric Biederman (ebiederm@xmission.com) + * Copyright (C) 2005 R Sharada (sharada@in.ibm.com), IBM Corporation + * + * 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 (version 2 of the License). + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <stdint.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <dirent.h> +#include <getopt.h> +#include "../../kexec.h" +#include "../../kexec-syscall.h" +#include "kexec-ppc64.h" +#include "../../fs2dt.h" +#include "crashdump-ppc64.h" +#include <arch/options.h> + +static struct memory_range *exclude_range = NULL; +static struct memory_range *memory_range = NULL; +static struct memory_range *base_memory_range = NULL; +static uint64_t rma_top; +uint64_t memory_max = 0; +uint64_t memory_limit; +static int nr_memory_ranges, nr_exclude_ranges; +uint64_t crash_base, crash_size; +unsigned int rtas_base, rtas_size; +uint64_t opal_base, opal_size; +int max_memory_ranges; + +static void cleanup_memory_ranges(void) +{ + if (memory_range) + free(memory_range); + if (base_memory_range) + free(base_memory_range); + if (exclude_range) + free(exclude_range); + if (usablemem_rgns.ranges) + free(usablemem_rgns.ranges); +} + +/* + * Allocate memory for various data structures used to hold + * values of different memory ranges + */ +static int alloc_memory_ranges(void) +{ + int memory_range_len; + + memory_range_len = sizeof(struct memory_range) * max_memory_ranges; + + memory_range = (struct memory_range *) malloc(memory_range_len); + if (!memory_range) + return -1; + + base_memory_range = (struct memory_range *) malloc(memory_range_len); + if (!base_memory_range) + goto err1; + + exclude_range = (struct memory_range *) malloc(memory_range_len); + if (!exclude_range) + goto err1; + + usablemem_rgns.ranges = (struct memory_range *) + malloc(memory_range_len); + if (!(usablemem_rgns.ranges)) + goto err1; + + memset(memory_range, 0, memory_range_len); + memset(base_memory_range, 0, memory_range_len); + memset(exclude_range, 0, memory_range_len); + memset(usablemem_rgns.ranges, 0, memory_range_len); + return 0; + +err1: + fprintf(stderr, "memory range structure allocation failure\n"); + cleanup_memory_ranges(); + return -1; + +} + +static int realloc_memory_ranges(void) +{ + size_t memory_range_len; + + max_memory_ranges++; + memory_range_len = sizeof(struct memory_range) * max_memory_ranges; + + memory_range = (struct memory_range *) realloc(memory_range, memory_range_len); + if (!memory_range) + goto err; + + base_memory_range = (struct memory_range *) realloc(base_memory_range, memory_range_len); + if (!base_memory_range) + goto err; + + exclude_range = (struct memory_range *) realloc(exclude_range, memory_range_len); + if (!exclude_range) + goto err; + + usablemem_rgns.ranges = (struct memory_range *) + realloc(usablemem_rgns.ranges, memory_range_len); + if (!(usablemem_rgns.ranges)) + goto err; + + return 0; + +err: + fprintf(stderr, "memory range structure re-allocation failure\n"); + return -1; +} + + +static void add_base_memory_range(uint64_t start, uint64_t end) +{ + base_memory_range[nr_memory_ranges].start = start; + base_memory_range[nr_memory_ranges].end = end; + base_memory_range[nr_memory_ranges].type = RANGE_RAM; + nr_memory_ranges++; + if (nr_memory_ranges >= max_memory_ranges) + realloc_memory_ranges(); + + dbgprintf("%016llx-%016llx : %x\n", + base_memory_range[nr_memory_ranges-1].start, + base_memory_range[nr_memory_ranges-1].end, + base_memory_range[nr_memory_ranges-1].type); +} + +static int get_dyn_reconf_base_ranges(void) +{ + uint64_t start, end; + uint64_t size; + char fname[128], buf[32]; + FILE *file; + unsigned int i; + int n; + + strcpy(fname, "/proc/device-tree/"); + strcat(fname, "ibm,dynamic-reconfiguration-memory/ibm,lmb-size"); + if ((file = fopen(fname, "r")) == NULL) { + perror(fname); + return -1; + } + if (fread(buf, 1, 8, file) != 8) { + perror(fname); + fclose(file); + return -1; + } + /* + * lmb_size, num_of_lmb_sets(global variables) are + * initialized once here. + */ + size = lmb_size = be64_to_cpu(((uint64_t *)buf)[0]); + fclose(file); + + strcpy(fname, "/proc/device-tree/"); + strcat(fname, + "ibm,dynamic-reconfiguration-memory/ibm,dynamic-memory"); + if ((file = fopen(fname, "r")) == NULL) { + strcat(fname, "-v2"); + if ((file = fopen(fname, "r")) == NULL) { + perror(fname); + return -1; + } + + is_dyn_mem_v2 = 1; + } + + /* first 4 bytes tell the number of lmb set entries */ + if (fread(buf, 1, 4, file) != 4) { + perror(fname); + fclose(file); + return -1; + } + num_of_lmb_sets = be32_to_cpu(((unsigned int *)buf)[0]); + + for (i = 0; i < num_of_lmb_sets; i++) { + if ((n = fread(buf, 1, LMB_ENTRY_SIZE, file)) < 0) { + perror(fname); + fclose(file); + return -1; + } + if (nr_memory_ranges >= max_memory_ranges) { + fclose(file); + return -1; + } + + /* + * If the property is ibm,dynamic-memory-v2, the first 4 bytes + * tell the number of sequential LMBs in this entry. + */ + if (is_dyn_mem_v2) + size = be32_to_cpu(((unsigned int *)buf)[0]) * lmb_size; + + start = be64_to_cpu(*((uint64_t *)&buf[DRCONF_ADDR])); + end = start + size; + add_base_memory_range(start, end); + } + fclose(file); + return 0; +} + +/* Sort the base ranges in memory - this is useful for ensuring that our + * ranges are in ascending order, even if device-tree read of memory nodes + * is done differently. Also, could be used for other range coalescing later + */ +static int sort_base_ranges(void) +{ + int i, j; + unsigned long long tstart, tend; + + for (i = 0; i < nr_memory_ranges - 1; i++) { + for (j = 0; j < nr_memory_ranges - i - 1; j++) { + if (base_memory_range[j].start > base_memory_range[j+1].start) { + tstart = base_memory_range[j].start; + tend = base_memory_range[j].end; + base_memory_range[j].start = base_memory_range[j+1].start; + base_memory_range[j].end = base_memory_range[j+1].end; + base_memory_range[j+1].start = tstart; + base_memory_range[j+1].end = tend; + } + } + } + return 0; +} + +/* Get base memory ranges */ +static int get_base_ranges(void) +{ + uint64_t start, end; + char device_tree[256] = "/proc/device-tree/"; + char fname[256]; + char buf[MAXBYTES]; + DIR *dir, *dmem; + FILE *file; + struct dirent *dentry, *mentry; + int n; + + if ((dir = opendir(device_tree)) == NULL) { + perror(device_tree); + return -1; + } + while ((dentry = readdir(dir)) != NULL) { + if (!strncmp(dentry->d_name, + "ibm,dynamic-reconfiguration-memory", 35)) { + get_dyn_reconf_base_ranges(); + continue; + } + if (strncmp(dentry->d_name, "memory@", 7) && + strcmp(dentry->d_name, "memory")) + continue; + strcpy(fname, device_tree); + strcat(fname, dentry->d_name); + if ((dmem = opendir(fname)) == NULL) { + perror(fname); + closedir(dir); + return -1; + } + while ((mentry = readdir(dmem)) != NULL) { + if (strcmp(mentry->d_name, "reg")) + continue; + strcat(fname, "/reg"); + if ((file = fopen(fname, "r")) == NULL) { + perror(fname); + closedir(dmem); + closedir(dir); + return -1; + } + if ((n = fread(buf, 1, MAXBYTES, file)) < 0) { + perror(fname); + fclose(file); + closedir(dmem); + closedir(dir); + return -1; + } + if (nr_memory_ranges >= max_memory_ranges) { + if (realloc_memory_ranges() < 0) + break; + } + start = be64_to_cpu(((uint64_t *)buf)[0]); + end = start + be64_to_cpu(((uint64_t *)buf)[1]); + add_base_memory_range(start, end); + fclose(file); + } + closedir(dmem); + } + closedir(dir); + sort_base_ranges(); + memory_max = base_memory_range[nr_memory_ranges - 1].end; + dbgprintf("get base memory ranges:%d\n", nr_memory_ranges); + + return 0; +} + +/* Sort the exclude ranges in memory */ +static int sort_ranges(void) +{ + int i, j; + uint64_t tstart, tend; + for (i = 0; i < nr_exclude_ranges - 1; i++) { + for (j = 0; j < nr_exclude_ranges - i - 1; j++) { + if (exclude_range[j].start > exclude_range[j+1].start) { + tstart = exclude_range[j].start; + tend = exclude_range[j].end; + exclude_range[j].start = exclude_range[j+1].start; + exclude_range[j].end = exclude_range[j+1].end; + exclude_range[j+1].start = tstart; + exclude_range[j+1].end = tend; + } + } + } + return 0; +} + +void scan_reserved_ranges(unsigned long kexec_flags, int *range_index) +{ + char fname[256], buf[16]; + FILE *file; + int i = *range_index; + + strcpy(fname, "/proc/device-tree/reserved-ranges"); + + file = fopen(fname, "r"); + if (file == NULL) { + if (errno != ENOENT) { + perror(fname); + return; + } + errno = 0; + /* File not present. Non PowerKVM system. */ + return; + } + + /* + * Each reserved range is an (address,size) pair, 2 cells each, + * totalling 4 cells per range. + */ + while (fread(buf, sizeof(uint64_t) * 2, 1, file) == 1) { + uint64_t base, size; + + base = be64_to_cpu(((uint64_t *)buf)[0]); + size = be64_to_cpu(((uint64_t *)buf)[1]); + + exclude_range[i].start = base; + exclude_range[i].end = base + size; + i++; + if (i >= max_memory_ranges) + realloc_memory_ranges(); + + reserve(base, size); + } + fclose(file); + *range_index = i; +} + +/* Return 0 if fname/value valid, -1 otherwise */ +int get_devtree_value(const char *fname, unsigned long long *value) +{ + FILE *file; + char buf[MAXBYTES]; + int n = -1; + + if ((file = fopen(fname, "r"))) { + n = fread(buf, 1, MAXBYTES, file); + fclose(file); + } + + if (n == sizeof(uint32_t)) + *value = ((uint32_t *)buf)[0]; + else if (n == sizeof(uint64_t)) + *value = ((uint64_t *)buf)[0]; + else { + fprintf(stderr, "%s node has invalid size: %d\n", fname, n); + return -1; + } + + return 0; +} + +/* Get devtree details and create exclude_range array + * Also create usablemem_ranges for KEXEC_ON_CRASH + */ +static int get_devtree_details(unsigned long kexec_flags) +{ + uint64_t rma_base = -1, base; + uint64_t tce_base; + unsigned int tce_size; + uint64_t htab_base, htab_size; + uint64_t kernel_end; + uint64_t initrd_start, initrd_end; + char buf[MAXBYTES]; + char device_tree[256] = "/proc/device-tree/"; + char fname[256]; + DIR *dir, *cdir; + FILE *file; + struct dirent *dentry; + struct stat fstat; + int n, i = 0; + + if ((dir = opendir(device_tree)) == NULL) { + perror(device_tree); + return -1; + } + + scan_reserved_ranges(kexec_flags, &i); + + while ((dentry = readdir(dir)) != NULL) { + if (strncmp(dentry->d_name, "chosen", 6) && + strncmp(dentry->d_name, "memory@", 7) && + strcmp(dentry->d_name, "memory") && + strncmp(dentry->d_name, "pci@", 4) && + strncmp(dentry->d_name, "rtas", 4) && + strncmp(dentry->d_name, "ibm,opal", 8)) + continue; + strcpy(fname, device_tree); + strcat(fname, dentry->d_name); + if ((cdir = opendir(fname)) == NULL) { + perror(fname); + goto error_opendir; + } + + if (strncmp(dentry->d_name, "chosen", 6) == 0) { + strcat(fname, "/linux,kernel-end"); + if ((file = fopen(fname, "r")) == NULL) { + perror(fname); + goto error_opencdir; + } + if (fread(&kernel_end, sizeof(uint64_t), 1, file) != 1) { + perror(fname); + goto error_openfile; + } + fclose(file); + kernel_end = be64_to_cpu(kernel_end); + + /* Add kernel memory to exclude_range */ + exclude_range[i].start = 0x0UL; + exclude_range[i].end = kernel_end; + i++; + if (i >= max_memory_ranges) + realloc_memory_ranges(); + + if (kexec_flags & KEXEC_ON_CRASH) { + memset(fname, 0, sizeof(fname)); + strcpy(fname, device_tree); + strcat(fname, dentry->d_name); + strcat(fname, "/linux,crashkernel-base"); + if ((file = fopen(fname, "r")) == NULL) { + perror(fname); + goto error_opencdir; + } + if (fread(&crash_base, sizeof(uint64_t), 1, + file) != 1) { + perror(fname); + goto error_openfile; + } + fclose(file); + crash_base = be64_to_cpu(crash_base); + + memset(fname, 0, sizeof(fname)); + strcpy(fname, device_tree); + strcat(fname, dentry->d_name); + strcat(fname, "/linux,crashkernel-size"); + if ((file = fopen(fname, "r")) == NULL) { + perror(fname); + goto error_opencdir; + } + if (fread(&crash_size, sizeof(uint64_t), 1, + file) != 1) { + perror(fname); + goto error_openfile; + } + fclose(file); + crash_size = be64_to_cpu(crash_size); + + if (crash_base > mem_min) + mem_min = crash_base; + if (crash_base + crash_size < mem_max) + mem_max = crash_base + crash_size; + + add_usable_mem_rgns(0, crash_base + crash_size); + reserve(KDUMP_BACKUP_LIMIT, crash_base-KDUMP_BACKUP_LIMIT); + } + /* + * Read the first kernel's memory limit. + * If the first kernel is booted with mem= option then + * it would export "linux,memory-limit" file + * reflecting value for the same. + */ + memset(fname, 0, sizeof(fname)); + strcpy(fname, device_tree); + strcat(fname, dentry->d_name); + strcat(fname, "/linux,memory-limit"); + if ((file = fopen(fname, "r")) == NULL) { + if (errno != ENOENT) { + perror(fname); + goto error_opencdir; + } + errno = 0; + /* + * File not present. + * fall through. On older kernel this file + * is not present. + */ + } else { + if (fread(&memory_limit, sizeof(uint64_t), 1, + file) != 1) { + perror(fname); + goto error_openfile; + } + fclose(file); + memory_limit = be64_to_cpu(memory_limit); + } + + memset(fname, 0, sizeof(fname)); + strcpy(fname, device_tree); + strcat(fname, dentry->d_name); + strcat(fname, "/linux,htab-base"); + if ((file = fopen(fname, "r")) == NULL) { + closedir(cdir); + if (errno == ENOENT) { + /* Non LPAR */ + errno = 0; + continue; + } + perror(fname); + goto error_opendir; + } + if (fread(&htab_base, sizeof(uint64_t), 1, file) != 1) { + perror(fname); + goto error_openfile; + } + fclose(file); + htab_base = be64_to_cpu(htab_base); + + memset(fname, 0, sizeof(fname)); + strcpy(fname, device_tree); + strcat(fname, dentry->d_name); + strcat(fname, "/linux,htab-size"); + if ((file = fopen(fname, "r")) == NULL) { + perror(fname); + goto error_opencdir; + } + if (fread(&htab_size, sizeof(uint64_t), 1, file) != 1) { + perror(fname); + goto error_openfile; + } + fclose(file); + htab_size = be64_to_cpu(htab_size); + + /* Add htab address to exclude_range - NON-LPAR only */ + exclude_range[i].start = htab_base; + exclude_range[i].end = htab_base + htab_size; + i++; + if (i >= max_memory_ranges) + realloc_memory_ranges(); + + /* reserve the initrd_start and end locations. */ + if (reuse_initrd) { + memset(fname, 0, sizeof(fname)); + strcpy(fname, device_tree); + strcat(fname, dentry->d_name); + strcat(fname, "/linux,initrd-start"); + if ((file = fopen(fname, "r")) == NULL) { + perror(fname); + goto error_opencdir; + } + /* check for 4 and 8 byte initrd offset sizes */ + if (stat(fname, &fstat) != 0) { + perror(fname); + goto error_openfile; + } + if (fread(&initrd_start, fstat.st_size, 1, file) != 1) { + perror(fname); + goto error_openfile; + } + initrd_start = be64_to_cpu(initrd_start); + fclose(file); + + memset(fname, 0, sizeof(fname)); + strcpy(fname, device_tree); + strcat(fname, dentry->d_name); + strcat(fname, "/linux,initrd-end"); + if ((file = fopen(fname, "r")) == NULL) { + perror(fname); + goto error_opencdir; + } + /* check for 4 and 8 byte initrd offset sizes */ + if (stat(fname, &fstat) != 0) { + perror(fname); + goto error_openfile; + } + if (fread(&initrd_end, fstat.st_size, 1, file) != 1) { + perror(fname); + goto error_openfile; + } + initrd_end = be64_to_cpu(initrd_end); + fclose(file); + + /* Add initrd address to exclude_range */ + exclude_range[i].start = initrd_start; + exclude_range[i].end = initrd_end; + i++; + if (i >= max_memory_ranges) + realloc_memory_ranges(); + } + } /* chosen */ + + if (strncmp(dentry->d_name, "rtas", 4) == 0) { + strcat(fname, "/linux,rtas-base"); + if ((file = fopen(fname, "r")) == NULL) { + perror(fname); + goto error_opencdir; + } + if (fread(&rtas_base, sizeof(unsigned int), 1, file) != 1) { + perror(fname); + goto error_openfile; + } + fclose(file); + rtas_base = be32_to_cpu(rtas_base); + memset(fname, 0, sizeof(fname)); + strcpy(fname, device_tree); + strcat(fname, dentry->d_name); + strcat(fname, "/rtas-size"); + if ((file = fopen(fname, "r")) == NULL) { + perror(fname); + goto error_opencdir; + } + if (fread(&rtas_size, sizeof(unsigned int), 1, file) != 1) { + perror(fname); + goto error_openfile; + } + fclose(file); + closedir(cdir); + rtas_size = be32_to_cpu(rtas_size); + /* Add rtas to exclude_range */ + exclude_range[i].start = rtas_base; + exclude_range[i].end = rtas_base + rtas_size; + i++; + if (i >= max_memory_ranges) + realloc_memory_ranges(); + if (kexec_flags & KEXEC_ON_CRASH) + add_usable_mem_rgns(rtas_base, rtas_size); + } /* rtas */ + + if (strncmp(dentry->d_name, "ibm,opal", 8) == 0) { + strcat(fname, "/opal-base-address"); + file = fopen(fname, "r"); + if (file == NULL) { + perror(fname); + goto error_opencdir; + } + if (fread(&opal_base, sizeof(uint64_t), 1, file) != 1) { + perror(fname); + goto error_openfile; + } + opal_base = be64_to_cpu(opal_base); + fclose(file); + + memset(fname, 0, sizeof(fname)); + strcpy(fname, device_tree); + strcat(fname, dentry->d_name); + strcat(fname, "/opal-runtime-size"); + file = fopen(fname, "r"); + if (file == NULL) { + perror(fname); + goto error_opencdir; + } + if (fread(&opal_size, sizeof(uint64_t), 1, file) != 1) { + perror(fname); + goto error_openfile; + } + fclose(file); + closedir(cdir); + opal_size = be64_to_cpu(opal_size); + /* Add OPAL to exclude_range */ + exclude_range[i].start = opal_base; + exclude_range[i].end = opal_base + opal_size; + i++; + if (i >= max_memory_ranges) + realloc_memory_ranges(); + if (kexec_flags & KEXEC_ON_CRASH) + add_usable_mem_rgns(opal_base, opal_size); + } /* ibm,opal */ + + if (!strncmp(dentry->d_name, "memory@", 7) || + !strcmp(dentry->d_name, "memory")) { + strcat(fname, "/reg"); + if ((file = fopen(fname, "r")) == NULL) { + perror(fname); + goto error_opencdir; + } + if ((n = fread(buf, 1, MAXBYTES, file)) < 0) { + perror(fname); + goto error_openfile; + } + base = be64_to_cpu(((uint64_t *)buf)[0]); + if (base < rma_base) { + rma_base = base; + rma_top = base + be64_to_cpu(((uint64_t *)buf)[1]); + } + + fclose(file); + closedir(cdir); + } /* memory */ + + if (strncmp(dentry->d_name, "pci@", 4) == 0) { + strcat(fname, "/linux,tce-base"); + if ((file = fopen(fname, "r")) == NULL) { + closedir(cdir); + if (errno == ENOENT) { + /* Non LPAR */ + errno = 0; + continue; + } + perror(fname); + goto error_opendir; + } + if (fread(&tce_base, sizeof(uint64_t), 1, file) != 1) { + perror(fname); + goto error_openfile; + } + fclose(file); + tce_base = be64_to_cpu(tce_base); + memset(fname, 0, sizeof(fname)); + strcpy(fname, device_tree); + strcat(fname, dentry->d_name); + strcat(fname, "/linux,tce-size"); + if ((file = fopen(fname, "r")) == NULL) { + perror(fname); + goto error_opencdir; + } + if (fread(&tce_size, sizeof(unsigned int), 1, file) != 1) { + perror(fname); + goto error_openfile; + } + fclose(file); + tce_size = be32_to_cpu(tce_size); + /* Add tce to exclude_range - NON-LPAR only */ + exclude_range[i].start = tce_base; + exclude_range[i].end = tce_base + tce_size; + i++; + if (i >= max_memory_ranges) + realloc_memory_ranges(); + if (kexec_flags & KEXEC_ON_CRASH) + add_usable_mem_rgns(tce_base, tce_size); + closedir(cdir); + } /* pci */ + } + closedir(dir); + + nr_exclude_ranges = i; + + sort_ranges(); + + int k; + for (k = 0; k < i; k++) + dbgprintf("exclude_range sorted exclude_range[%d] " + "start:%llx, end:%llx\n", k, exclude_range[k].start, + exclude_range[k].end); + + return 0; + +error_openfile: + fclose(file); +error_opencdir: + closedir(cdir); +error_opendir: + closedir(dir); + return -1; +} + +/* Setup a sorted list of memory ranges. */ +int setup_memory_ranges(unsigned long kexec_flags) +{ + int i, j = 0; + + /* Get the base list of memory ranges from /proc/device-tree/memory + * nodes. Build list of ranges to be excluded from valid memory + */ + + if (get_base_ranges()) + goto out; + if (get_devtree_details(kexec_flags)) + goto out; + + for (i = 0; i < nr_exclude_ranges; i++) { + /* If first exclude range does not start with 0, include the + * first hole of valid memory from 0 - exclude_range[0].start + */ + if (i == 0) { + if (exclude_range[i].start != 0) { + memory_range[j].start = 0; + memory_range[j].end = exclude_range[i].start - 1; + memory_range[j].type = RANGE_RAM; + j++; + if (j >= max_memory_ranges) + realloc_memory_ranges(); + } + } /* i == 0 */ + /* If the last exclude range does not end at memory_max, include + * the last hole of valid memory from exclude_range[last].end - + * memory_max + */ + if (i == nr_exclude_ranges - 1) { + if (exclude_range[i].end < memory_max) { + memory_range[j].start = exclude_range[i].end + 1; + memory_range[j].end = memory_max; + memory_range[j].type = RANGE_RAM; + j++; + if (j >= max_memory_ranges) + realloc_memory_ranges(); + /* Limit the end to rma_top */ + if (memory_range[j-1].start >= rma_top) { + j--; + break; + } + if ((memory_range[j-1].start < rma_top) && + (memory_range[j-1].end >= rma_top)) { + memory_range[j-1].end = rma_top; + break; + } + continue; + } + } /* i == nr_exclude_ranges - 1 */ + /* contiguous exclude ranges - skip */ + if (exclude_range[i+1].start == exclude_range[i].end + 1) + continue; + memory_range[j].start = exclude_range[i].end + 1; + memory_range[j].end = exclude_range[i+1].start - 1; + memory_range[j].type = RANGE_RAM; + j++; + if (j >= max_memory_ranges) + realloc_memory_ranges(); + /* Limit range to rma_top */ + if (memory_range[j-1].start >= rma_top) { + j--; + break; + } + if ((memory_range[j-1].start < rma_top) && + (memory_range[j-1].end >= rma_top)) { + memory_range[j-1].end = rma_top; + break; + } + } + nr_memory_ranges = j; + + int k; + for (k = 0; k < j; k++) + dbgprintf("setup_memory_ranges memory_range[%d] " + "start:%llx, end:%llx\n", k, memory_range[k].start, + memory_range[k].end); + return 0; + +out: + cleanup_memory_ranges(); + return -1; +} + +/* Return a list of valid memory ranges */ +int get_memory_ranges(struct memory_range **range, int *ranges, + unsigned long kexec_flags) +{ + /* allocate memory_range dynamically */ + max_memory_ranges = 1; + + if (alloc_memory_ranges()) + return -1; + if (setup_memory_ranges(kexec_flags)) + return -1; + + /* + * copy the memory here, another realloc_memory_ranges might + * corrupt the old memory + */ + *range = calloc(sizeof(struct memory_range), nr_memory_ranges); + if (*range == NULL) + return -1; + memmove(*range, memory_range, + sizeof(struct memory_range) * nr_memory_ranges); + + *ranges = nr_memory_ranges; + dbgprintf("get memory ranges:%d\n", nr_memory_ranges); + return 0; +} + +struct file_type file_type[] = { + { "elf-ppc64", elf_ppc64_probe, elf_ppc64_load, elf_ppc64_usage }, +}; +int file_types = sizeof(file_type) / sizeof(file_type[0]); + +void arch_usage(void) +{ + fprintf(stderr, " --elf64-core-headers Prepare core headers in ELF64 format\n"); + fprintf(stderr, " --dt-no-old-root Do not reuse old kernel root= param.\n" \ + " while creating flatten device tree.\n"); +} + +struct arch_options_t arch_options = { + .core_header_type = CORE_TYPE_ELF64, +}; + +int arch_process_options(int argc, char **argv) +{ + /* We look for all options so getopt_long doesn't start reordering + * argv[] before file_type[n].load() gets a look in. + */ + static const struct option options[] = { + KEXEC_ALL_OPTIONS + { 0, 0, NULL, 0 }, + }; + static const char short_options[] = KEXEC_ALL_OPT_STR; + int opt; + + opterr = 0; /* Don't complain about unrecognized options here */ + while((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) { + switch(opt) { + default: + break; + case OPT_ELF64_CORE: + arch_options.core_header_type = CORE_TYPE_ELF64; + break; + case OPT_DT_NO_OLD_ROOT: + dt_no_old_root = 1; + break; + } + } + /* Reset getopt for the next pass; called in other source modules */ + opterr = 1; + optind = 1; + return 0; +} + +const struct arch_map_entry arches[] = { + /* We are running a 32-bit kexec-tools on 64-bit ppc64. + * So pass KEXEC_ARCH_PPC64 here + */ + { "ppc64", KEXEC_ARCH_PPC64 }, + { "ppc64le", KEXEC_ARCH_PPC64 }, + { NULL, 0 }, +}; + +int arch_compat_trampoline(struct kexec_info *UNUSED(info)) +{ + return 0; +} + +void arch_update_purgatory(struct kexec_info *UNUSED(info)) +{ +} diff --git a/kexec/arch/ppc64/kexec-ppc64.h b/kexec/arch/ppc64/kexec-ppc64.h new file mode 100644 index 0000000..434b4bf --- /dev/null +++ b/kexec/arch/ppc64/kexec-ppc64.h @@ -0,0 +1,45 @@ +#ifndef KEXEC_PPC64_H +#define KEXEC_PPC64_H + +#define PATH_LEN 256 +#define MAXBYTES 128 +#define MAX_LINE 160 +#define CORE_TYPE_ELF32 1 +#define CORE_TYPE_ELF64 2 + +#define BOOT_BLOCK_VERSION 17 +#define BOOT_BLOCK_LAST_COMP_VERSION 17 +#if (BOOT_BLOCK_VERSION < 16) +# define NEED_STRUCTURE_BLOCK_EXTRA_PAD +#endif +#define HAVE_DYNAMIC_MEMORY +#define NEED_RESERVE_DTB + +extern int get_devtree_value(const char *fname, unsigned long long *pvalue); + +int setup_memory_ranges(unsigned long kexec_flags); + +int elf_ppc64_probe(const char *buf, off_t len); +int elf_ppc64_load(int argc, char **argv, const char *buf, off_t len, + struct kexec_info *info); +void elf_ppc64_usage(void); + +struct mem_ehdr; +unsigned long my_r2(const struct mem_ehdr *ehdr); + +extern uint64_t initrd_base, initrd_size; +extern int max_memory_ranges; +extern unsigned char reuse_initrd; + +struct arch_options_t { + int core_header_type; +}; + +typedef struct mem_rgns { + unsigned int size; + struct memory_range *ranges; +} mem_rgns_t; + +extern mem_rgns_t usablemem_rgns; + +#endif /* KEXEC_PPC64_H */ diff --git a/kexec/arch/ppc64/kexec-zImage-ppc64.c b/kexec/arch/ppc64/kexec-zImage-ppc64.c new file mode 100644 index 0000000..e946205 --- /dev/null +++ b/kexec/arch/ppc64/kexec-zImage-ppc64.c @@ -0,0 +1,184 @@ +/* + * kexec: Linux boots Linux + * + * Copyright (C) 2004 Adam Litke (agl@us.ibm.com) + * Copyright (C) 2004 IBM Corp. + * + * 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 (version 2 of the License). + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#define _GNU_SOURCE +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <getopt.h> +#include <linux/elf.h> +#include "../../kexec.h" + +#define MAX_HEADERS 32 + +int zImage_ppc64_probe(FILE *file) +{ + Elf32_Ehdr elf; + int valid; + + if (fseek(file, 0, SEEK_SET) < 0) { + fprintf(stderr, "seek error: %s\n", + strerror(errno)); + return -1; + } + if (fread(&elf, sizeof(Elf32_Ehdr), 1, file) != 1) { + fprintf(stderr, "read error: %s\n", + strerror(errno)); + return -1; + } + + if (elf.e_machine == EM_PPC64) { + fprintf(stderr, "Elf64 not supported\n"); + return -1; + } + + valid = (elf.e_ident[EI_MAG0] == ELFMAG0 && + elf.e_ident[EI_MAG1] == ELFMAG1 && + elf.e_ident[EI_MAG2] == ELFMAG2 && + elf.e_ident[EI_MAG3] == ELFMAG3 && + elf.e_ident[EI_CLASS] == ELFCLASS32 && + elf.e_ident[EI_DATA] == ELFDATA2MSB && + elf.e_type == ET_EXEC && + elf.e_machine == EM_PPC); + + return valid ? 0 : -1; +} + +int zImage_ppc64_load(FILE *file, int UNUSED(argc), char **UNUSED(argv), + void **ret_entry, struct kexec_segment **ret_segments, + int *ret_nr_segments) +{ + Elf32_Ehdr elf; + Elf32_Phdr *p, *ph; + struct kexec_segment *segment; + int i; + unsigned long memsize, filesize, offset, load_loc = 0; + + /* Parse command line arguments */ + + /* Read in the Elf32 header */ + if (fseek(file, 0, SEEK_SET) < 0) { + perror("seek error:"); + return -1; + } + if (fread(&elf, sizeof(Elf32_Ehdr), 1, file) != 1) { + perror("read error: "); + return -1; + } + if (elf.e_phnum > MAX_HEADERS) { + fprintf(stderr, + "Only kernels with %i program headers are supported\n", + MAX_HEADERS); + return -1; + } + + /* Read the section header */ + ph = (Elf32_Phdr *)malloc(sizeof(Elf32_Phdr) * elf.e_phnum); + if (ph == 0) { + perror("malloc failed: "); + return -1; + } + if (fseek(file, elf.e_phoff, SEEK_SET) < 0) { + perror("seek failed: "); + free(ph); + return -1; + } + if (fread(ph, sizeof(Elf32_Phdr) * elf.e_phnum, 1, file) != 1) { + perror("read error: "); + free(ph); + return -1; + } + + *ret_segments = malloc(elf.e_phnum * sizeof(struct kexec_segment)); + if (*ret_segments == 0) { + fprintf(stderr, "malloc failed: %s\n", + strerror(errno)); + free(ph); + return -1; + } + segment = ret_segments[0]; + + /* Scan through the program header */ + memsize = filesize = offset = 0; + p = ph; + for (i = 0; i < elf.e_phnum; ++i, ++p) { + if (p->p_type != PT_LOAD || p->p_offset == 0) + continue; + if (memsize == 0) { + offset = p->p_offset; + memsize = p->p_memsz; + filesize = p->p_filesz; + load_loc = p->p_vaddr; + } else { + memsize = p->p_offset + p->p_memsz - offset; + filesize = p->p_offset + p->p_filesz - offset; + } + } + if (memsize == 0) { + fprintf(stderr, "Can't find a loadable segment.\n"); + free(ph); + return -1; + } + + /* Load program segments */ + p = ph; + segment->buf = malloc(filesize); + if (segment->buf == 0) { + perror("malloc failed: "); + free(ph); + return -1; + } + for (i = 0; i < elf.e_phnum; ++i, ++p) { + unsigned long mem_offset; + if (p->p_type != PT_LOAD || p->p_offset == 0) + continue; + + /* skip to the actual image */ + if (fseek(file, p->p_offset, SEEK_SET) < 0) { + perror("seek error: "); + free(ph); + return -1; + } + mem_offset = p->p_vaddr - load_loc; + if (fread((void *)segment->buf+mem_offset, p->p_filesz, 1, + file) != 1) { + perror("read error: "); + free(ph); + return -1; + } + } + segment->mem = (void *) load_loc; + segment->memsz = memsize; + segment->bufsz = filesize; + *ret_entry = (void *)(uintptr_t)elf.e_entry; + *ret_nr_segments = i - 1; + free(ph); + return 0; +} + +void zImage_ppc64_usage(void) +{ + fprintf(stderr, "zImage support is still broken\n"); +} diff --git a/kexec/arch/s390/Makefile b/kexec/arch/s390/Makefile new file mode 100644 index 0000000..fab3e68 --- /dev/null +++ b/kexec/arch/s390/Makefile @@ -0,0 +1,11 @@ +# +# kexec s390 (linux booting linux) +# +s390_KEXEC_SRCS = kexec/arch/s390/kexec-s390.c +s390_KEXEC_SRCS += kexec/arch/s390/kexec-image.c +s390_KEXEC_SRCS += kexec/arch/s390/kexec-elf-rel-s390.c +s390_KEXEC_SRCS += kexec/arch/s390/crashdump-s390.c + +dist += kexec/arch/s390/Makefile $(s390_KEXEC_SRCS) \ + kexec/arch/s390/kexec-s390.h \ + kexec/arch/s390/include/arch/options.h diff --git a/kexec/arch/s390/crashdump-s390.c b/kexec/arch/s390/crashdump-s390.c new file mode 100644 index 0000000..3bd9efe --- /dev/null +++ b/kexec/arch/s390/crashdump-s390.c @@ -0,0 +1,90 @@ +/* + * kexec/arch/s390/crashdump-s390.c + * + * Copyright IBM Corp. 2011 + * + * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com> + */ + +#ifdef __s390x__ +#define _GNU_SOURCE + +#include <stdio.h> +#include <elf.h> +#include <limits.h> +#include <string.h> +#include "../../kexec.h" +#include "../../kexec-syscall.h" +#include "../../kexec/crashdump.h" +#include "kexec-s390.h" + +/* + * Create ELF core header + */ +static int create_elf_header(struct kexec_info *info, unsigned long crash_base, + unsigned long crash_end) +{ +#ifdef WITH_ELF_HEADER + static struct memory_range crash_memory_range[MAX_MEMORY_RANGES]; + unsigned long elfcorehdr, elfcorehdr_size, bufsz; + struct crash_elf_info elf_info; + char str[COMMAND_LINESIZE]; + int ranges; + void *tmp; + + memset(&elf_info, 0, sizeof(elf_info)); + + elf_info.data = ELFDATA2MSB; + elf_info.machine = EM_S390; + elf_info.class = ELFCLASS64; + elf_info.get_note_info = get_crash_notes_per_cpu; + + if (get_memory_ranges_s390(crash_memory_range, &ranges, 0)) + return -1; + + if (crash_create_elf64_headers(info, &elf_info, crash_memory_range, + ranges, &tmp, &bufsz, + ELF_CORE_HEADER_ALIGN)) + return -1; + + elfcorehdr = add_buffer(info, tmp, bufsz, bufsz, 1024, + crash_base, crash_end, -1); + elfcorehdr_size = bufsz; + snprintf(str, sizeof(str), " elfcorehdr=%ld@%ldK\n", + elfcorehdr_size, elfcorehdr / 1024); + if (command_line_add(info, str)) + return -1; +#endif + return 0; +} + +/* + * Load additional segments for kdump kernel + */ +int load_crashdump_segments(struct kexec_info *info, unsigned long crash_base, + unsigned long crash_end) +{ + unsigned long crash_size = crash_size = crash_end - crash_base + 1; + + if (create_elf_header(info, crash_base, crash_end)) + return -1; + elf_rel_build_load(info, &info->rhdr, (const char *) purgatory, + purgatory_size, crash_base + 0x2000, + crash_base + 0x10000, -1, 0); + elf_rel_set_symbol(&info->rhdr, "crash_base", &crash_base, + sizeof(crash_base)); + elf_rel_set_symbol(&info->rhdr, "crash_size", &crash_size, + sizeof(crash_size)); + info->entry = (void *) elf_rel_get_addr(&info->rhdr, "purgatory_start"); + return 0; +} +#else +/* + * kdump is not available for s390 + */ +int load_crashdump_segments(struct kexec_info *info, unsigned long crash_base, + unsigned long crash_end) +{ + return -1; +} +#endif diff --git a/kexec/arch/s390/include/arch/options.h b/kexec/arch/s390/include/arch/options.h new file mode 100644 index 0000000..c150244 --- /dev/null +++ b/kexec/arch/s390/include/arch/options.h @@ -0,0 +1,40 @@ +#ifndef KEXEC_ARCH_S390_OPTIONS_H +#define KEXEC_ARCH_S390_OPTIONS_H + +#define OPT_ARCH_MAX (OPT_MAX+0) +#define OPT_APPEND (OPT_MAX+0) +#define OPT_RAMDISK (OPT_MAX+1) +#define OPT_REUSE_CMDLINE (OPT_MAX+2) + +/* Options relevant to the architecture (excluding loader-specific ones), + * in this case none: + */ +#define KEXEC_ARCH_OPTIONS \ + KEXEC_OPTIONS \ + +#define KEXEC_ARCH_OPT_STR KEXEC_OPT_STR "" + +/* The following two #defines list ALL of the options added by all of the + * architecture's loaders. + * o main() uses this complete list to scan for its options, ignoring + * arch-specific/loader-specific ones. + * o Then, arch_process_options() uses this complete list to scan for its + * options, ignoring general/loader-specific ones. + * o Then, the file_type[n].load re-scans for options, using + * KEXEC_ARCH_OPTIONS plus its loader-specific options subset. + * Any unrecognised options cause an error here. + * + * This is done so that main()'s/arch_process_options()'s getopt_long() calls + * don't choose a kernel filename from random arguments to options they don't + * recognise -- as they now recognise (if not act upon) all possible options. + */ +#define KEXEC_ALL_OPTIONS \ + KEXEC_ARCH_OPTIONS \ + {"command-line", 1, 0, OPT_APPEND}, \ + {"append", 1, 0, OPT_APPEND}, \ + {"initrd", 1, 0, OPT_RAMDISK}, \ + {"reuse-cmdline", 0, 0, OPT_REUSE_CMDLINE }, + +#define KEXEC_ALL_OPT_STR KEXEC_ARCH_OPT_STR + +#endif /* KEXEC_ARCH_S390_OPTIONS_H */ diff --git a/kexec/arch/s390/kexec-elf-rel-s390.c b/kexec/arch/s390/kexec-elf-rel-s390.c new file mode 100644 index 0000000..91ba86a --- /dev/null +++ b/kexec/arch/s390/kexec-elf-rel-s390.c @@ -0,0 +1,78 @@ +/* + * kexec/arch/s390/kexec-elf-rel-s390.c + * + * Copyright IBM Corp. 2005,2011 + * + * Author(s): Heiko Carstens <heiko.carstens@de.ibm.com> + * + */ + +#include <stdio.h> +#include <elf.h> +#include "../../kexec.h" +#include "../../kexec-elf.h" + +int machine_verify_elf_rel(struct mem_ehdr *ehdr) +{ + if (ehdr->ei_data != ELFDATA2MSB) + return 0; + if (ehdr->ei_class != ELFCLASS64) + return 0; + if (ehdr->e_machine != EM_S390) + return 0; + return 1; +} + +void machine_apply_elf_rel(struct mem_ehdr *UNUSED(ehdr), + struct mem_sym *UNUSED(sym), + unsigned long r_type, + void *loc, + unsigned long address, + unsigned long val) +{ + switch (r_type) { + case R_390_8: /* Direct 8 bit. */ + case R_390_12: /* Direct 12 bit. */ + case R_390_16: /* Direct 16 bit. */ + case R_390_20: /* Direct 20 bit. */ + case R_390_32: /* Direct 32 bit. */ + case R_390_64: /* Direct 64 bit. */ + if (r_type == R_390_8) + *(unsigned char *) loc = val; + else if (r_type == R_390_12) + *(unsigned short *) loc = (val & 0xfff) | + (*(unsigned short *) loc & 0xf000); + else if (r_type == R_390_16) + *(unsigned short *) loc = val; + else if (r_type == R_390_20) + *(unsigned int *) loc = + (*(unsigned int *) loc & 0xf00000ff) | + (val & 0xfff) << 16 | (val & 0xff000) >> 4; + else if (r_type == R_390_32) + *(unsigned int *) loc = val; + else if (r_type == R_390_64) + *(unsigned long *) loc = val; + break; + case R_390_PC16: /* PC relative 16 bit. */ + case R_390_PC16DBL: /* PC relative 16 bit shifted by 1. */ + case R_390_PC32DBL: /* PC relative 32 bit shifted by 1. */ + case R_390_PLT32DBL: /* 32 bit PC rel. PLT shifted by 1. */ + case R_390_PC32: /* PC relative 32 bit. */ + case R_390_PC64: /* PC relative 64 bit. */ + val -= address; + if (r_type == R_390_PC16) + *(unsigned short *) loc = val; + else if (r_type == R_390_PC16DBL) + *(unsigned short *) loc = val >> 1; + else if (r_type == R_390_PC32DBL || r_type == R_390_PLT32DBL) + *(unsigned int *) loc = val >> 1; + else if (r_type == R_390_PC32) + *(unsigned int *) loc = val; + else if (r_type == R_390_PC64) + *(unsigned long *) loc = val; + break; + default: + die("Unknown rela relocation: 0x%lx 0x%lx\n", r_type, address); + break; + } +} diff --git a/kexec/arch/s390/kexec-image.c b/kexec/arch/s390/kexec-image.c new file mode 100644 index 0000000..69aaf96 --- /dev/null +++ b/kexec/arch/s390/kexec-image.c @@ -0,0 +1,236 @@ +/* + * kexec/arch/s390/kexec-image.c + * + * (C) Copyright IBM Corp. 2005 + * + * Author(s): Rolf Adelsberger <adelsberger@de.ibm.com> + * Heiko Carstens <heiko.carstens@de.ibm.com> + * + */ + +#define _GNU_SOURCE +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <getopt.h> +#include "../../kexec.h" +#include "../../kexec-syscall.h" +#include "../../kexec/crashdump.h" +#include "kexec-s390.h" +#include <arch/options.h> +#include <fcntl.h> + +static uint64_t crash_base, crash_end; + +static void add_segment_check(struct kexec_info *info, const void *buf, + size_t bufsz, unsigned long base, size_t memsz) +{ + if (info->kexec_flags & KEXEC_ON_CRASH) + if (base + memsz > crash_end - crash_base) + die("Not enough crashkernel memory to load segments\n"); + add_segment(info, buf, bufsz, crash_base + base, memsz); +} + +int command_line_add(struct kexec_info *info, const char *str) +{ + char *tmp = NULL; + + tmp = concat_cmdline(info->command_line, str); + if (!tmp) { + fprintf(stderr, "out of memory\n"); + return -1; + } + + free(info->command_line); + info->command_line = tmp; + return 0; +} + +int image_s390_load_file(int argc, char **argv, struct kexec_info *info) +{ + const char *ramdisk = NULL; + int opt; + + static const struct option options[] = + { + KEXEC_ALL_OPTIONS + {0, 0, 0, 0}, + }; + static const char short_options[] = KEXEC_OPT_STR ""; + + while ((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) { + switch(opt) { + case OPT_APPEND: + if (command_line_add(info, optarg)) + return -1; + break; + case OPT_RAMDISK: + ramdisk = optarg; + break; + case OPT_REUSE_CMDLINE: + free(info->command_line); + info->command_line = get_command_line(); + break; + } + } + + if (ramdisk) { + info->initrd_fd = open(ramdisk, O_RDONLY); + if (info->initrd_fd == -1) { + fprintf(stderr, "Could not open initrd file %s:%s\n", + ramdisk, strerror(errno)); + free(info->command_line); + info->command_line = NULL; + return -1; + } + } + + if (info->command_line) + info->command_line_len = strlen(info->command_line) + 1; + else + info->command_line_len = 0; + return 0; +} + +int +image_s390_load(int argc, char **argv, const char *kernel_buf, + off_t kernel_size, struct kexec_info *info) +{ + void *krnl_buffer; + char *rd_buffer; + const char *ramdisk; + off_t ramdisk_len; + unsigned int ramdisk_origin; + int opt, ret = -1; + + if (info->file_mode) + return image_s390_load_file(argc, argv, info); + + static const struct option options[] = + { + KEXEC_ALL_OPTIONS + {0, 0, 0, 0}, + }; + static const char short_options[] = KEXEC_OPT_STR ""; + + ramdisk = NULL; + ramdisk_len = 0; + ramdisk_origin = 0; + + while ((opt = getopt_long(argc,argv,short_options,options,0)) != -1) { + switch(opt) { + case OPT_APPEND: + if (command_line_add(info, optarg)) + return -1; + break; + case OPT_REUSE_CMDLINE: + free(info->command_line); + info->command_line = get_command_line(); + break; + case OPT_RAMDISK: + ramdisk = optarg; + break; + } + } + + if (info->kexec_flags & KEXEC_ON_CRASH) { + if (parse_iomem_single("Crash kernel\n", &crash_base, + &crash_end)) + goto out; + } + + /* Add kernel segment */ + add_segment_check(info, kernel_buf + IMAGE_READ_OFFSET, + kernel_size - IMAGE_READ_OFFSET, IMAGE_READ_OFFSET, + kernel_size - IMAGE_READ_OFFSET); + + /* We do want to change the kernel image */ + krnl_buffer = (void *) kernel_buf + IMAGE_READ_OFFSET; + + /* + * Load ramdisk if present: If image is larger than RAMDISK_ORIGIN_ADDR, + * we load the ramdisk directly behind the image with 1 MiB alignment. + */ + if (ramdisk) { + rd_buffer = slurp_file_mmap(ramdisk, &ramdisk_len); + if (rd_buffer == NULL) { + fprintf(stderr, "Could not read ramdisk.\n"); + goto out; + } + ramdisk_origin = MAX(RAMDISK_ORIGIN_ADDR, kernel_size); + ramdisk_origin = _ALIGN_UP(ramdisk_origin, 0x100000); + add_segment_check(info, rd_buffer, ramdisk_len, + ramdisk_origin, ramdisk_len); + } + if (info->kexec_flags & KEXEC_ON_CRASH) { + if (load_crashdump_segments(info, crash_base, crash_end)) + goto out; + } else { + info->entry = (void *) IMAGE_READ_OFFSET; + } + + /* Register the ramdisk and crashkernel memory in the kernel. */ + { + unsigned long long *tmp; + + tmp = krnl_buffer + INITRD_START_OFFS; + *tmp = (unsigned long long) ramdisk_origin; + + tmp = krnl_buffer + INITRD_SIZE_OFFS; + *tmp = (unsigned long long) ramdisk_len; + + if (info->kexec_flags & KEXEC_ON_CRASH) { + tmp = krnl_buffer + OLDMEM_BASE_OFFS; + *tmp = crash_base; + + tmp = krnl_buffer + OLDMEM_SIZE_OFFS; + *tmp = crash_end - crash_base + 1; + } + } + + if (info->command_line) { + unsigned long maxsize; + char *dest = krnl_buffer + COMMAND_LINE_OFFS; + + maxsize = *(unsigned long *)(krnl_buffer + MAX_COMMAND_LINESIZE_OFFS); + if (!maxsize) + maxsize = LEGACY_COMMAND_LINESIZE; + + if (strlen(info->command_line) > maxsize-1) { + fprintf(stderr, "command line too long, maximum allowed size %ld\n", + maxsize-1); + goto out; + } + strncpy(dest, info->command_line, maxsize-1); + dest[maxsize-1] = '\0'; + } + ret = 0; +out: + free(info->command_line); + info->command_line = NULL; + return ret; +} + +int +image_s390_probe(const char *UNUSED(kernel_buf), off_t UNUSED(kernel_size)) +{ + /* + * Can't reliably tell if an image is valid, + * therefore everything is valid. + */ + return 0; +} + +void +image_s390_usage(void) +{ + printf("--command-line=STRING Set the kernel command line to STRING.\n" + "--append=STRING Set the kernel command line to STRING.\n" + "--initrd=FILENAME Use the file FILENAME as a ramdisk.\n" + "--reuse-cmdline Use kernel command line from running system.\n" + ); +} diff --git a/kexec/arch/s390/kexec-s390.c b/kexec/arch/s390/kexec-s390.c new file mode 100644 index 0000000..33ba6b9 --- /dev/null +++ b/kexec/arch/s390/kexec-s390.c @@ -0,0 +1,269 @@ +/* + * kexec/arch/s390/kexec-s390.c + * + * Copyright IBM Corp. 2005,2011 + * + * Author(s): Rolf Adelsberger <adelsberger@de.ibm.com> + * Michael Holzheu <holzheu@linux.vnet.ibm.com> + * + */ + +#define _GNU_SOURCE +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <stdint.h> +#include <string.h> +#include <getopt.h> +#include <sys/types.h> +#include <dirent.h> +#include "../../kexec.h" +#include "../../kexec-syscall.h" +#include "kexec-s390.h" +#include <arch/options.h> + +static struct memory_range memory_range[MAX_MEMORY_RANGES]; + +/* + * Read string from file + */ +static void read_str(char *string, const char *path, size_t len) +{ + size_t rc; + FILE *fh; + + fh = fopen(path, "rb"); + if (fh == NULL) + die("Could not open \"%s\"", path); + rc = fread(string, 1, len - 1, fh); + if (rc == 0 && ferror(fh)) + die("Could not read \"%s\"", path); + fclose(fh); + string[rc] = 0; + if (string[strlen(string) - 1] == '\n') + string[strlen(string) - 1] = 0; +} + +/* + * Return number of memory chunks + */ +static int memory_range_cnt(struct memory_range chunks[]) +{ + int i; + + for (i = 0; i < MAX_MEMORY_RANGES; i++) { + if (chunks[i].end == 0) + break; + } + return i; +} + +/* + * Create memory hole with given address and size + * + * lh = local hole + */ +static void add_mem_hole(struct memory_range chunks[], unsigned long addr, + unsigned long size) +{ + unsigned long lh_start, lh_end, lh_size, chunk_cnt; + int i; + + chunk_cnt = memory_range_cnt(chunks); + + for (i = 0; i < chunk_cnt; i++) { + if (addr + size <= chunks[i].start) + break; + if (addr > chunks[i].end) + continue; + lh_start = MAX(addr, chunks[i].start); + lh_end = MIN(addr + size - 1, chunks[i].end); + lh_size = lh_end - lh_start + 1; + if (lh_start == chunks[i].start && lh_end == chunks[i].end) { + /* Remove chunk */ + memmove(&chunks[i], &chunks[i + 1], + sizeof(struct memory_range) * + (MAX_MEMORY_RANGES - (i + 1))); + memset(&chunks[MAX_MEMORY_RANGES - 1], 0, + sizeof(struct memory_range)); + chunk_cnt--; + i--; + } else if (lh_start == chunks[i].start) { + /* Make chunk smaller at start */ + chunks[i].start = chunks[i].start + lh_size; + break; + } else if (lh_end == chunks[i].end) { + /* Make chunk smaller at end */ + chunks[i].end = lh_start - 1; + } else { + /* Split chunk into two */ + if (chunk_cnt >= MAX_MEMORY_RANGES) + die("Unable to create memory hole: %i", i); + memmove(&chunks[i + 1], &chunks[i], + sizeof(struct memory_range) * + (MAX_MEMORY_RANGES - (i + 1))); + chunks[i + 1].start = lh_start + lh_size; + chunks[i].end = lh_start - 1; + break; + } + } +} + +/* + * Remove offline memory from memory chunks + */ +static void remove_offline_memory(struct memory_range memory_range[]) +{ + unsigned long block_size, chunk_nr; + struct dirent *dirent; + char path[PATH_MAX]; + char str[64]; + DIR *dir; + + read_str(str, "/sys/devices/system/memory/block_size_bytes", + sizeof(str)); + sscanf(str, "%lx", &block_size); + + dir = opendir("/sys/devices/system/memory"); + if (!dir) + die("Could not read \"/sys/devices/system/memory\""); + while ((dirent = readdir(dir))) { + if (sscanf(dirent->d_name, "memory%ld\n", &chunk_nr) != 1) + continue; + sprintf(path, "/sys/devices/system/memory/%s/state", + dirent->d_name); + read_str(str, path, sizeof(str)); + if (strncmp(str, "offline", 6) != 0) + continue; + add_mem_hole(memory_range, chunk_nr * block_size, block_size); + } + closedir(dir); +} + +/* + * Get memory ranges of type "System RAM" from /proc/iomem. If with_crashk=1 + * then also type "Crash kernel" is added. + */ +int get_memory_ranges_s390(struct memory_range memory_range[], int *ranges, + int with_crashk) +{ + char crash_kernel[] = "Crash kernel\n"; + char sys_ram[] = "System RAM\n"; + const char *iomem = proc_iomem(); + FILE *fp; + char line[80]; + int current_range = 0; + + fp = fopen(iomem,"r"); + if(fp == 0) { + fprintf(stderr,"Unable to open %s: %s\n",iomem,strerror(errno)); + return -1; + } + + /* Setup the compare string properly. */ + while (fgets(line, sizeof(line), fp) != 0) { + unsigned long long start, end; + int cons; + char *str; + + if (current_range == MAX_MEMORY_RANGES) + break; + + sscanf(line,"%llx-%llx : %n", &start, &end, &cons); + str = line+cons; + if ((memcmp(str, sys_ram, strlen(sys_ram)) == 0) || + ((memcmp(str, crash_kernel, strlen(crash_kernel)) == 0) && + with_crashk)) { + memory_range[current_range].start = start; + memory_range[current_range].end = end; + memory_range[current_range].type = RANGE_RAM; + current_range++; + } + else { + continue; + } + } + fclose(fp); + remove_offline_memory(memory_range); + *ranges = memory_range_cnt(memory_range); + return 0; +} + +/* + * get_memory_ranges: + * Return a list of memory ranges by parsing the file returned by + * proc_iomem() + * + * INPUT: + * - Pointer to an array of memory_range structures. + * - Pointer to an integer with holds the number of memory ranges. + * + * RETURN: + * - 0 on normal execution. + * - (-1) if something went wrong. + */ + +int get_memory_ranges(struct memory_range **range, int *ranges, + unsigned long flags) +{ + uint64_t start, end; + + if (get_memory_ranges_s390(memory_range, ranges, + flags & KEXEC_ON_CRASH)) + return -1; + *range = memory_range; + if ((flags & KEXEC_ON_CRASH) && !(flags & KEXEC_PRESERVE_CONTEXT)) { + if (parse_iomem_single("Crash kernel\n", &start, &end)) + return -1; + if (start > mem_min) + mem_min = start; + if (end < mem_max) + mem_max = end; + } + return 0; +} + +/* Supported file types and callbacks */ +struct file_type file_type[] = { + { "image", image_s390_probe, image_s390_load, image_s390_usage}, +}; +int file_types = sizeof(file_type) / sizeof(file_type[0]); + + +void arch_usage(void) +{ +} + +int arch_process_options(int UNUSED(argc), char **UNUSED(argv)) +{ + return 0; +} + +const struct arch_map_entry arches[] = { + { "s390", KEXEC_ARCH_S390 }, + { "s390x", KEXEC_ARCH_S390 }, + { NULL, 0 }, +}; + +int arch_compat_trampoline(struct kexec_info *UNUSED(info)) +{ + return 0; +} + +void arch_update_purgatory(struct kexec_info *UNUSED(info)) +{ +} + +int is_crashkernel_mem_reserved(void) +{ + uint64_t start, end; + + return parse_iomem_single("Crash kernel\n", &start, &end) == 0 ? + (start != end) : 0; +} + +int get_crash_kernel_load_range(uint64_t *start, uint64_t *end) +{ + return parse_iomem_single("Crash kernel\n", start, end); +} diff --git a/kexec/arch/s390/kexec-s390.h b/kexec/arch/s390/kexec-s390.h new file mode 100644 index 0000000..6a99518 --- /dev/null +++ b/kexec/arch/s390/kexec-s390.h @@ -0,0 +1,38 @@ +/* + * kexec/arch/s390/kexec-s390.h + * + * (C) Copyright IBM Corp. 2005 + * + * Author(s): Rolf Adelsberger <adelsberger@de.ibm.com> + * + */ + +#ifndef KEXEC_S390_H +#define KEXEC_S390_H + +#define IMAGE_READ_OFFSET 0x10000 + +#define RAMDISK_ORIGIN_ADDR 0x800000 +#define INITRD_START_OFFS 0x408 +#define INITRD_SIZE_OFFS 0x410 +#define OLDMEM_BASE_OFFS 0x418 +#define OLDMEM_SIZE_OFFS 0x420 +#define MAX_COMMAND_LINESIZE_OFFS 0x430 +#define COMMAND_LINE_OFFS 0x480 +#define LEGACY_COMMAND_LINESIZE 896 +#define MAX_MEMORY_RANGES 1024 + +#define MAX(x, y) ((x) > (y) ? (x) : (y)) +#define MIN(x, y) ((x) < (y) ? (x) : (y)) + +extern int image_s390_load(int, char **, const char *, off_t, struct kexec_info *); +extern int image_s390_probe(const char *, off_t); +extern void image_s390_usage(void); +extern int load_crashdump_segments(struct kexec_info *info, + unsigned long crash_base, + unsigned long crash_end); +extern int get_memory_ranges_s390(struct memory_range range[], int *ranges, + int with_crashk); +extern int command_line_add(struct kexec_info *info, const char *str); + +#endif /* KEXEC_S390_H */ diff --git a/kexec/arch/sh/Makefile b/kexec/arch/sh/Makefile new file mode 100644 index 0000000..7cf40ae --- /dev/null +++ b/kexec/arch/sh/Makefile @@ -0,0 +1,22 @@ +# +# kexec sh (linux booting linux) +# +sh_KEXEC_SRCS += kexec/arch/sh/kexec-sh.c +sh_KEXEC_SRCS += kexec/arch/sh/kexec-uImage-sh.c +sh_KEXEC_SRCS += kexec/arch/sh/kexec-zImage-sh.c +sh_KEXEC_SRCS += kexec/arch/sh/kexec-netbsd-sh.c +sh_KEXEC_SRCS += kexec/arch/sh/kexec-elf-sh.c +sh_KEXEC_SRCS += kexec/arch/sh/kexec-elf-rel-sh.c +sh_KEXEC_SRCS += kexec/arch/sh/netbsd_booter.S +sh_KEXEC_SRCS += kexec/arch/sh/crashdump-sh.c + +sh_UIMAGE = kexec/kexec-uImage.c + +sh_ADD_BUFFER = +sh_ADD_SEGMENT = +sh_VIRT_TO_PHYS = + +dist += kexec/arch/sh/Makefile $(sh_KEXEC_SRCS) \ + kexec/arch/sh/kexec-sh.h \ + kexec/arch/sh/crashdump-sh.h \ + kexec/arch/sh/include/arch/options.h diff --git a/kexec/arch/sh/crashdump-sh.c b/kexec/arch/sh/crashdump-sh.c new file mode 100644 index 0000000..36e9aaf --- /dev/null +++ b/kexec/arch/sh/crashdump-sh.c @@ -0,0 +1,166 @@ +/* + * crashdump-sh.c - crashdump for SuperH + * Copyright (C) 2008 Magnus Damm + * + * Based on x86 and ppc64 implementation, written by + * Vivek Goyal (vgoyal@in.ibm.com), R Sharada (sharada@in.ibm.com) + * Copyright (C) IBM Corporation, 2005. All rights reserved + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#define _GNU_SOURCE +#include <stddef.h> +#include <stdio.h> +#include <errno.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <getopt.h> +#include <elf.h> +#include "../../kexec.h" +#include "../../kexec-elf.h" +#include "../../kexec-elf-boot.h" +#include "../../kexec-syscall.h" +#include "../../crashdump.h" +#include "kexec-sh.h" +#include "crashdump-sh.h" +#include <arch/options.h> + +#define CRASH_MAX_MEMORY_RANGES 64 +static struct memory_range crash_memory_range[CRASH_MAX_MEMORY_RANGES]; + +static int crash_sh_range_nr; +static int crash_sh_memory_range_callback(void *UNUSED(data), int UNUSED(nr), + char *str, + unsigned long long base, + unsigned long long length) +{ + + struct memory_range *range = crash_memory_range; + struct memory_range *range2 = crash_memory_range; + + range += crash_sh_range_nr; + + if (crash_sh_range_nr >= CRASH_MAX_MEMORY_RANGES) { + return 1; + } + + if (strncmp(str, "System RAM\n", 11) == 0) { + range->start = base; + range->end = base + length - 1; + range->type = RANGE_RAM; + crash_sh_range_nr++; + } + + if (strncmp(str, "Crash kernel\n", 13) == 0) { + if (!crash_sh_range_nr) + die("Unsupported /proc/iomem format\n"); + + range2 = range - 1; + if ((base + length - 1) < range2->end) { + range->start = base + length; + range->end = range2->end; + range->type = RANGE_RAM; + crash_sh_range_nr++; + } + range2->end = base - 1; + } + + return 0; +} + +/* Return a sorted list of available memory ranges. */ +static int crash_get_memory_ranges(struct memory_range **range, int *ranges) +{ + crash_sh_range_nr = 0; + + kexec_iomem_for_each_line(NULL, crash_sh_memory_range_callback, NULL); + *range = crash_memory_range; + *ranges = crash_sh_range_nr; + return 0; +} + +static struct crash_elf_info elf_info32 = +{ + class: ELFCLASS32, + data: ELFDATA2LSB, + machine: EM_SH, + page_offset: PAGE_OFFSET, +}; + +static int add_cmdline_param(char *cmdline, uint64_t addr, char *cmdstr, + char *byte) +{ + int cmdlen, len, align = 1024; + char str[COMMAND_LINE_SIZE], *ptr; + + /* Passing in =xxxK / =xxxM format. Saves space required in cmdline.*/ + switch (byte[0]) { + case 'K': + if (addr%align) + return -1; + addr = addr/align; + break; + case 'M': + addr = addr/(align *align); + break; + } + ptr = str; + strcpy(str, cmdstr); + ptr += strlen(str); + ultoa(addr, ptr); + strcat(str, byte); + len = strlen(str); + cmdlen = strlen(cmdline) + len; + if (cmdlen > (COMMAND_LINE_SIZE - 1)) + die("Command line overflow\n"); + strcat(cmdline, str); + + dbgprintf("Command line after adding elfcorehdr: %s\n", cmdline); + + return 0; +} + +/* Loads additional segments in case of a panic kernel is being loaded. + * One segment for storing elf headers for crash memory image. + */ +int load_crashdump_segments(struct kexec_info *info, char* mod_cmdline) +{ + void *tmp; + unsigned long sz, elfcorehdr; + int nr_ranges; + struct memory_range *mem_range; + + if (crash_get_memory_ranges(&mem_range, &nr_ranges) < 0) + return -1; + + if (crash_create_elf32_headers(info, &elf_info32, + mem_range, nr_ranges, + &tmp, &sz, + ELF_CORE_HEADER_ALIGN) < 0) + return -1; + + elfcorehdr = add_buffer_phys_virt(info, tmp, sz, sz, 1024, + 0, 0xffffffff, -1, 0); + + dbgprintf("Created elf header segment at 0x%lx\n", elfcorehdr); + add_cmdline_param(mod_cmdline, elfcorehdr, " elfcorehdr=", "K"); + add_cmdline_param(mod_cmdline, elfcorehdr - mem_min, " mem=", "K"); + + return 0; +} + +int is_crashkernel_mem_reserved(void) +{ + uint64_t start, end; + + return parse_iomem_single("Crash kernel\n", &start, &end) == 0 ? + (start != end) : 0; +} + +int get_crash_kernel_load_range(uint64_t *start, uint64_t *end) +{ + return parse_iomem_single("Crash kernel\n", start, end); +} diff --git a/kexec/arch/sh/crashdump-sh.h b/kexec/arch/sh/crashdump-sh.h new file mode 100644 index 0000000..c5d4102 --- /dev/null +++ b/kexec/arch/sh/crashdump-sh.h @@ -0,0 +1,9 @@ +#ifndef CRASHDUMP_SH_H +#define CRASHDUMP_SH_H + +struct kexec_info; +int load_crashdump_segments(struct kexec_info *info, char* mod_cmdline); + +#define PAGE_OFFSET 0x80000000 + +#endif /* CRASHDUMP_SH_H */ diff --git a/kexec/arch/sh/include/arch/options.h b/kexec/arch/sh/include/arch/options.h new file mode 100644 index 0000000..f923eb4 --- /dev/null +++ b/kexec/arch/sh/include/arch/options.h @@ -0,0 +1,42 @@ +#ifndef KEXEC_ARCH_SH_OPTIONS_H +#define KEXEC_ARCH_SH_OPTIONS_H + +#define OPT_ARCH_MAX (OPT_MAX+0) +#define OPT_APPEND (OPT_ARCH_MAX+1) +#define OPT_EMPTYZERO (OPT_ARCH_MAX+2) +#define OPT_NBSD_HOWTO (OPT_ARCH_MAX+3) +#define OPT_NBSD_MROOT (OPT_ARCH_MAX+4) + +/* Options relevant to the architecture (excluding loader-specific ones): */ +#define KEXEC_ARCH_OPTIONS \ + KEXEC_OPTIONS \ + {"command-line", 1, 0, OPT_APPEND}, \ + {"append", 1, 0, OPT_APPEND}, \ + {"empty-zero", 1, 0, OPT_APPEND}, \ + {"howto", 1, 0, OPT_NBSD_HOWTO}, \ + {"miniroot", 1, 0, OPT_NBSD_MROOT}, +/* These options seem to be loader-specific rather than superh-specific, so + * ought to be moved to KEXEC_ALL_OPTIONS below and parsed in the relevant + * loader, e.g. kexec-netbsd-sh.c + */ + +#define KEXEC_ARCH_OPT_STR KEXEC_OPT_STR "" + +/* The following two #defines list ALL of the options added by all of the + * architecture's loaders. + * o main() uses this complete list to scan for its options, ignoring + * arch-specific/loader-specific ones. + * o Then, arch_process_options() uses this complete list to scan for its + * options, ignoring general/loader-specific ones. + * o Then, the file_type[n].load re-scans for options, using + * KEXEC_ARCH_OPTIONS plus its loader-specific options subset. + * Any unrecognised options cause an error here. + * + * This is done so that main()'s/arch_process_options()'s getopt_long() calls + * don't choose a kernel filename from random arguments to options they don't + * recognise -- as they now recognise (if not act upon) all possible options. + */ +#define KEXEC_ALL_OPTIONS KEXEC_ARCH_OPTIONS +#define KEXEC_ALL_OPT_STR KEXEC_ARCH_OPT_STR + +#endif /* KEXEC_ARCH_SH_OPTIONS_H */ diff --git a/kexec/arch/sh/kexec-elf-rel-sh.c b/kexec/arch/sh/kexec-elf-rel-sh.c new file mode 100644 index 0000000..3993ee8 --- /dev/null +++ b/kexec/arch/sh/kexec-elf-rel-sh.c @@ -0,0 +1,54 @@ +/* + * kexec-elf-rel-sh.c - ELF relocations for SuperH + * Copyright (C) 2008 Paul Mundt + * + * Based on the SHcompact module loader (arch/sh/kernel/module.c) in the + * Linux kernel, which is written by: + * + * Copyright (C) 2003 - 2008 Kaz Kojima & Paul Mundt + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ +#include <stdio.h> +#include <elf.h> +#include "../../kexec.h" +#include "../../kexec-elf.h" + +int machine_verify_elf_rel(struct mem_ehdr *ehdr) +{ + /* Intentionally don't bother with endianness validation, it's + * configurable */ + + if (ehdr->ei_class != ELFCLASS32) + return 0; + if (ehdr->e_machine != EM_SH) + return 0; + + return 1; +} + +void machine_apply_elf_rel(struct mem_ehdr *UNUSED(ehdr), + struct mem_sym *UNUSED(sym), unsigned long r_type, void *orig_loc, + unsigned long UNUSED(address), unsigned long relocation) +{ + uint32_t *location = orig_loc; + uint32_t value; + + switch (r_type) { + case R_SH_DIR32: + value = get_unaligned(location); + value += relocation; + put_unaligned(value, location); + break; + case R_SH_REL32: + relocation = (relocation - (uint32_t)location); + value = get_unaligned(location); + value += relocation; + put_unaligned(value, location); + break; + default: + die("Unknown rela relocation: %lu\n", r_type); + break; + } +} diff --git a/kexec/arch/sh/kexec-elf-sh.c b/kexec/arch/sh/kexec-elf-sh.c new file mode 100644 index 0000000..897552c --- /dev/null +++ b/kexec/arch/sh/kexec-elf-sh.c @@ -0,0 +1,136 @@ +/* + * kexec: Linux boots Linux + * + * Copyright (C) 2008 Magnus Damm + * + * Based on x86 implementation, + * Copyright (C) 2003-2005 Eric Biederman (ebiederm@xmission.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation (version 2 of the License). + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#define _GNU_SOURCE +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <limits.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <getopt.h> +#include <elf.h> +#include "../../kexec.h" +#include "../../kexec-syscall.h" +#include "../../kexec-elf.h" +#include "../../kexec-elf-boot.h" +#include <arch/options.h> +#include "crashdump-sh.h" +#include "kexec-sh.h" + +int elf_sh_probe(const char *buf, off_t len) +{ + struct mem_ehdr ehdr; + int result; + result = build_elf_exec_info(buf, len, &ehdr, 0); + if (result < 0) + goto out; + + /* Verify the architecuture specific bits */ + if (ehdr.e_machine != EM_SH) { + result = -1; + goto out; + } + + result = 0; + out: + free_elf_info(&ehdr); + return result; +} + +void elf_sh_usage(void) +{ + printf(" --append=STRING Set the kernel command line to STRING\n" + ); +} + +int elf_sh_load(int argc, char **argv, const char *buf, off_t len, + struct kexec_info *info) +{ + struct mem_ehdr ehdr; + char *command_line; + char *modified_cmdline; + struct mem_sym sym; + int opt, rc; + static const struct option options[] = { + KEXEC_ARCH_OPTIONS + { 0, 0, 0, 0 }, + }; + + static const char short_options[] = KEXEC_OPT_STR ""; + + /* + * Parse the command line arguments + */ + command_line = modified_cmdline = 0; + while((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) { + switch(opt) { + default: + /* Ignore core options */ + if (opt < OPT_ARCH_MAX) { + break; + } + case OPT_APPEND: + command_line = optarg; + break; + } + } + + /* Need to append some command line parameters internally in case of + * taking crash dumps. + */ + if (info->kexec_flags & (KEXEC_ON_CRASH | KEXEC_PRESERVE_CONTEXT)) { + modified_cmdline = xmalloc(COMMAND_LINE_SIZE); + memset((void *)modified_cmdline, 0, COMMAND_LINE_SIZE); + if (command_line) { + strncpy(modified_cmdline, command_line, + COMMAND_LINE_SIZE); + modified_cmdline[COMMAND_LINE_SIZE - 1] = '\0'; + } + } + + /* Load the ELF executable */ + elf_exec_build_load(info, &ehdr, buf, len, 0); + info->entry = (void *)virt_to_phys(ehdr.e_entry); + + /* If panic kernel is being loaded, additional segments need + * to be created. */ + if (info->kexec_flags & (KEXEC_ON_CRASH | KEXEC_PRESERVE_CONTEXT)) { + rc = load_crashdump_segments(info, modified_cmdline); + if (rc < 0) + return -1; + /* Use new command line. */ + command_line = modified_cmdline; + } + + /* If we're booting a vmlinux then fill in empty_zero_page */ + if (elf_rel_find_symbol(&ehdr, "empty_zero_page", &sym) == 0) { + char *zp = (void *)ehdr.e_shdr[sym.st_shndx].sh_data; + + kexec_sh_setup_zero_page(zp, 4096, command_line); + } + + return 0; +} diff --git a/kexec/arch/sh/kexec-netbsd-sh.c b/kexec/arch/sh/kexec-netbsd-sh.c new file mode 100644 index 0000000..ed93759 --- /dev/null +++ b/kexec/arch/sh/kexec-netbsd-sh.c @@ -0,0 +1,149 @@ +/* + * kexec-netbsd-sh.c - kexec netbsd loader for the SH + * Copyright (C) 2005 kogiidena@eggplant.ddo.jp + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#define _GNU_SOURCE +#include <stddef.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <stdint.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <getopt.h> +#include <elf.h> +#include <boot/elf_boot.h> +#include <ip_checksum.h> +#include "../../kexec.h" +#include "../../kexec-elf.h" +#include <arch/options.h> + +static const int probe_debug = 0; +extern const unsigned char netbsd_booter[]; + +/* + * netbsd_sh_probe - sanity check the elf image + * + * Make sure that the file image has a reasonable chance of working. + */ +int netbsd_sh_probe(const char *buf, off_t UNUSED(len)) +{ + Elf32_Ehdr *ehdr; + + ehdr = (Elf32_Ehdr *)buf; + if(memcmp(buf, ELFMAG, SELFMAG) != 0){ + return -1; + } + if (ehdr->e_machine != EM_SH) { + return -1; + } + return 0; +} + +void netbsd_sh_usage(void) +{ + printf( + " --howto=VALUE NetBSD kernel boot option.\n" + " --miniroot=FILE NetBSD miniroot ramdisk.\n\n"); +} + +int netbsd_sh_load(int argc, char **argv, const char *buf, off_t UNUSED(len), + struct kexec_info *info) +{ + const char *howto, *miniroot; + unsigned long entry, start, size, psz; + char *miniroot_buf; + off_t miniroot_length; + unsigned int howto_value; + unsigned char *param; + unsigned long *paraml; + unsigned char *img; + + int opt; + + static const struct option options[] = { + KEXEC_ARCH_OPTIONS + {0, 0, 0, 0}, + }; + + static const char short_options[] = KEXEC_ARCH_OPT_STR ""; + + howto = miniroot = 0; + while ((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) { + switch (opt) { + default: + /* Ignore core options */ + if (opt < OPT_ARCH_MAX) { + break; + } + case OPT_NBSD_HOWTO: + howto = optarg; + break; + case OPT_NBSD_MROOT: + miniroot = optarg; + break; + } + } + + /* howto */ + howto_value = 0; + if(howto){ + howto_value = strtol(howto, NULL, 0); + } + + psz = getpagesize(); + + /* Parse the Elf file */ + { + Elf32_Ehdr *ehdr; + Elf32_Phdr *phdr; + unsigned long bbs; + ehdr = (Elf32_Ehdr *)buf; + phdr = (Elf32_Phdr *)&buf[ehdr->e_phoff]; + + entry = ehdr->e_entry; + img = (unsigned char *)&buf[phdr->p_offset]; + start = (phdr->p_paddr) & 0x1fffffff; + bbs = phdr->p_filesz; + size = phdr->p_memsz; + + if(size < bbs){ + size = bbs; + } + + size = _ALIGN(size, psz); + memset(&img[bbs], 0, size-bbs); + add_segment(info, img, size, start, size); + start += size; + } + + /* miniroot file */ + miniroot_buf = 0; + if (miniroot) { + miniroot_buf = slurp_file(miniroot, &miniroot_length); + howto_value |= 0x200; + size = _ALIGN(miniroot_length, psz); + add_segment(info, miniroot_buf, size, start, size); + start += size; + } + + /* howto & bootinfo */ + param = xmalloc(4096); + memset(param, 0, 4096); + paraml = (unsigned long *) ¶m[256]; + memcpy(param, netbsd_booter, 256); + paraml[0] = entry; + paraml[1] = howto_value; + add_segment(info, param, 4096, start, 4096); + + /* For now we don't have arguments to pass :( */ + info->entry = (void *) (start | 0xa0000000); + return 0; +} diff --git a/kexec/arch/sh/kexec-sh.c b/kexec/arch/sh/kexec-sh.c new file mode 100644 index 0000000..ce341c8 --- /dev/null +++ b/kexec/arch/sh/kexec-sh.c @@ -0,0 +1,259 @@ +/* + * kexec-sh.c - kexec for the SH + * Copyright (C) 2004 kogiidena@eggplant.ddo.jp + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#define _GNU_SOURCE +#include <stddef.h> +#include <stdio.h> +#include <errno.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <getopt.h> +#include "../../kexec.h" +#include "../../kexec-syscall.h" +#include "kexec-sh.h" +#include <arch/options.h> + +#define MAX_MEMORY_RANGES 64 +static struct memory_range memory_range[MAX_MEMORY_RANGES]; + +static int kexec_sh_memory_range_callback(void *UNUSED(data), int nr, + char *UNUSED(str), + unsigned long long base, + unsigned long long length) +{ + if (nr < MAX_MEMORY_RANGES) { + memory_range[nr].start = base; + memory_range[nr].end = base + length - 1; + memory_range[nr].type = RANGE_RAM; + return 0; + } + + return 1; +} + +/* Return a sorted list of available memory ranges. */ +int get_memory_ranges(struct memory_range **range, int *ranges, + unsigned long kexec_flags) +{ + int nr, ret; + nr = kexec_iomem_for_each_line("System RAM\n", + kexec_sh_memory_range_callback, NULL); + *range = memory_range; + *ranges = nr; + + /* + * Redefine the memory region boundaries if kernel + * exports the limits and if it is panic kernel. + * Override user values only if kernel exported values are + * subset of user defined values. + */ + if (kexec_flags & KEXEC_ON_CRASH) { + unsigned long long start, end; + + ret = parse_iomem_single("Crash kernel\n", &start, &end); + if (ret != 0) { + fprintf(stderr, "parse_iomem_single failed.\n"); + return -1; + } + + if (start > mem_min) + mem_min = start; + if (end < mem_max) + mem_max = end; + } + + return 0; +} + +/* Supported file types and callbacks */ +struct file_type file_type[] = { + /* uImage is probed before zImage because the latter also accepts + uncompressed images. */ + { "uImage-sh", uImage_sh_probe, uImage_sh_load, zImage_sh_usage }, + { "zImage-sh", zImage_sh_probe, zImage_sh_load, zImage_sh_usage }, + { "elf-sh", elf_sh_probe, elf_sh_load, elf_sh_usage }, + { "netbsd-sh", netbsd_sh_probe, netbsd_sh_load, netbsd_sh_usage }, +}; +int file_types = sizeof(file_type) / sizeof(file_type[0]); + + +void arch_usage(void) +{ + + printf( + " none\n\n" + "Default options:\n" + " --append=\"%s\"\n" + " STRING of --append is set from /proc/cmdline as default.\n" + ,get_append()); + +} + +int arch_process_options(int argc, char **argv) +{ + /* The common options amongst loaders (e.g. --append) should be read + * here, and the loader-specific options (e.g. NetBSD stuff) should + * then be re-parsed in the loader. + * (e.g. in kexec-netbsd-sh.c, for example.) + */ + static const struct option options[] = { + KEXEC_ALL_OPTIONS + { 0, 0, NULL, 0 }, + }; + static const char short_options[] = KEXEC_ARCH_OPT_STR; + int opt; + + opterr = 0; /* Don't complain about unrecognized options here */ + while((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) { + switch(opt) { + default: + /* Ignore core options */ + if (opt < OPT_MAX) { + break; + } + case OPT_APPEND: + case OPT_NBSD_HOWTO: + case OPT_NBSD_MROOT: + ; + } + } + /* Reset getopt for the next pass; called in other source modules */ + opterr = 1; + optind = 1; + return 0; +} + +const struct arch_map_entry arches[] = { + /* For compatibility with older patches + * use KEXEC_ARCH_DEFAULT instead of KEXEC_ARCH_SH here. + */ + { "sh3", KEXEC_ARCH_DEFAULT }, + { "sh4", KEXEC_ARCH_DEFAULT }, + { "sh4a", KEXEC_ARCH_DEFAULT }, + { "sh4al-dsp", KEXEC_ARCH_DEFAULT }, + { NULL, 0 }, +}; + +int arch_compat_trampoline(struct kexec_info *UNUSED(info)) +{ + return 0; +} + +void arch_update_purgatory(struct kexec_info *UNUSED(info)) +{ +} + +char append_buf[256]; + +char *get_append(void) +{ + FILE *fp; + int len; + if((fp = fopen("/proc/cmdline", "r")) == NULL){ + die("/proc/cmdline file open error !!\n"); + } + fgets(append_buf, 256, fp); + len = strlen(append_buf); + append_buf[len-1] = 0; + fclose(fp); + return append_buf; +} + +void kexec_sh_setup_zero_page(char *zero_page_buf, size_t zero_page_size, + char *cmd_line) +{ + size_t n = zero_page_size - 0x100; + + memset(zero_page_buf, 0, zero_page_size); + + if (cmd_line) { + if (n > strlen(cmd_line)) + n = strlen(cmd_line); + + memcpy(zero_page_buf + 0x100, cmd_line, n); + zero_page_buf[0x100 + n] = '\0'; + } +} + +static int is_32bit(void) +{ + const char *cpuinfo = "/proc/cpuinfo"; + char line[MAX_LINE]; + FILE *fp; + int status = 0; + + fp = fopen(cpuinfo, "r"); + if (!fp) + die("Cannot open %s\n", cpuinfo); + + while(fgets(line, sizeof(line), fp) != 0) { + const char *key = "address sizes"; + const char *value = " 32 bits physical"; + char *p; + if (strncmp(line, key, strlen(key))) + continue; + p = strchr(line + strlen(key), ':'); + if (!p) + continue; + if (!strncmp(p + 1, value, strlen(value))) + status = 1; + break; + } + + fclose(fp); + + return status; +} + +unsigned long virt_to_phys(unsigned long addr) +{ + unsigned long seg = addr & 0xe0000000; + unsigned long long start = 0; + int have_32bit = is_32bit(); + + if (seg != 0x80000000 && (have_32bit || seg != 0xc0000000)) + die("Virtual address %p is not in P1%s\n", (void *)addr, + have_32bit ? "" : " or P2"); + + /* If 32bit addressing is used then the base of system RAM + * is an offset into physical memory. */ + if (have_32bit) { + unsigned long long end; + int ret; + + /* Assume there is only one "System RAM" region */ + ret = parse_iomem_single("System RAM\n", &start, &end); + if (ret) + die("Could not parse System RAM region " + "in /proc/iomem\n"); + } + + return addr - seg + start; +} + +/* + * add_segment() should convert base to a physical address on superh, + * while the default is just to work with base as is */ +void add_segment(struct kexec_info *info, const void *buf, size_t bufsz, + unsigned long base, size_t memsz) +{ + add_segment_phys_virt(info, buf, bufsz, base, memsz, 1); +} + +/* + * add_buffer() should convert base to a physical address on superh, + * while the default is just to work with base as is */ +unsigned long add_buffer(struct kexec_info *info, const void *buf, + unsigned long bufsz, unsigned long memsz, + unsigned long buf_align, unsigned long buf_min, + unsigned long buf_max, int buf_end) +{ + return add_buffer_phys_virt(info, buf, bufsz, memsz, buf_align, + buf_min, buf_max, buf_end, 1); +} diff --git a/kexec/arch/sh/kexec-sh.h b/kexec/arch/sh/kexec-sh.h new file mode 100644 index 0000000..7d28ade --- /dev/null +++ b/kexec/arch/sh/kexec-sh.h @@ -0,0 +1,29 @@ +#ifndef KEXEC_SH_H +#define KEXEC_SH_H + +#define COMMAND_LINE_SIZE 2048 + +int uImage_sh_probe(const char *buf, off_t len); +int uImage_sh_load(int argc, char **argv, const char *buf, off_t len, + struct kexec_info *info); + +int zImage_sh_probe(const char *buf, off_t len); +int zImage_sh_load(int argc, char **argv, const char *buf, off_t len, + struct kexec_info *info); +void zImage_sh_usage(void); + +int elf_sh_probe(const char *buf, off_t len); +int elf_sh_load(int argc, char **argv, const char *buf, off_t len, + struct kexec_info *info); +void elf_sh_usage(void); + +int netbsd_sh_probe(const char *buf, off_t len); +int netbsd_sh_load(int argc, char **argv, const char *buf, off_t len, + struct kexec_info *info); +void netbsd_sh_usage(void); + +char *get_append(void); +void kexec_sh_setup_zero_page(char *zero_page_buf, size_t zero_page_size, + char *cmd_line); + +#endif /* KEXEC_SH_H */ diff --git a/kexec/arch/sh/kexec-uImage-sh.c b/kexec/arch/sh/kexec-uImage-sh.c new file mode 100644 index 0000000..130f12c --- /dev/null +++ b/kexec/arch/sh/kexec-uImage-sh.c @@ -0,0 +1,24 @@ +/* + * uImage support added by Marc Andre Tanner <mat@brain-dump.org> + * + * Cloned from ARM by Paul Mundt, 2009. + */ +#include <stdint.h> +#include <string.h> +#include <sys/types.h> +#include <image.h> +#include <kexec-uImage.h> +#include "../../kexec.h" +#include "kexec-sh.h" + +int uImage_sh_probe(const char *buf, off_t len) +{ + return uImage_probe_kernel(buf, len, IH_ARCH_SH); +} + +int uImage_sh_load(int argc, char **argv, const char *buf, off_t len, + struct kexec_info *info) +{ + return zImage_sh_load(argc, argv, buf + sizeof(struct image_header), + len - sizeof(struct image_header), info); +} diff --git a/kexec/arch/sh/kexec-zImage-sh.c b/kexec/arch/sh/kexec-zImage-sh.c new file mode 100644 index 0000000..6dc2c13 --- /dev/null +++ b/kexec/arch/sh/kexec-zImage-sh.c @@ -0,0 +1,142 @@ +/* + * kexec-zImage-sh.c - kexec zImage loader for the SH + * Copyright (C) 2005 kogiidena@eggplant.ddo.jp + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#define _GNU_SOURCE +#include <stddef.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <stdint.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <getopt.h> +#include <elf.h> +#include <boot/elf_boot.h> +#include <ip_checksum.h> +#include "../../kexec.h" +#include "../../kexec-elf.h" +#include <arch/options.h> +#include "kexec-sh.h" + +static const int probe_debug = 0; + +#define HEAD32_KERNEL_START_ADDR 0 +#define HEAD32_DECOMPRESS_KERNEL_ADDR 1 +#define HEAD32_INIT_STACK_ADDR 2 +#define HEAD32_INIT_SR 3 +#define HEAD32_INIT_SR_VALUE 0x400000F0 + +static unsigned long zImage_head32(const char *buf, int offs) +{ + unsigned long *values = (void *)buf; + int k; + + for (k = (0x200 / 4) - 1; k > 0; k--) + if (values[k] != 0x00090009) /* not nop + nop padding*/ + return values[k - offs]; + + return 0; +} + +/* + * zImage_sh_probe - sanity check the elf image + * + * Make sure that the file image has a reasonable chance of working. + */ +int zImage_sh_probe(const char *buf, off_t UNUSED(len)) +{ + if (memcmp(&buf[0x202], "HdrS", 4) != 0) + return -1; + + if (zImage_head32(buf, HEAD32_INIT_SR) != HEAD32_INIT_SR_VALUE) + return -1; + + return 0; +} + +void zImage_sh_usage(void) +{ + printf( + " --append=STRING Set the kernel command line to STRING.\n" + " --empty-zero=ADDRESS Set the kernel top ADDRESS. \n\n"); + +} + +int zImage_sh_load(int argc, char **argv, const char *buf, off_t len, + struct kexec_info *info) +{ + char *command_line; + int opt; + unsigned long empty_zero, zero_page_base, zero_page_size, k; + unsigned long image_base; + char *param; + + static const struct option options[] = { + KEXEC_ARCH_OPTIONS + {0, 0, 0, 0}, + }; + + static const char short_options[] = KEXEC_ARCH_OPT_STR ""; + + command_line = 0; + while ((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) { + switch (opt) { + default: + /* Ignore core options */ + if (opt < OPT_ARCH_MAX) { + break; + } + case OPT_APPEND: + command_line = optarg; + break; + } + } + + if (!command_line) + command_line = get_append(); + + /* assume the zero page is the page before the vmlinux entry point. + * we don't know the page size though, but 64k seems to be max. + * put several 4k zero page copies before the entry point to cover + * all combinations. + */ + + empty_zero = zImage_head32(buf, HEAD32_KERNEL_START_ADDR); + + zero_page_size = 0x10000; + zero_page_base = virt_to_phys(empty_zero - zero_page_size); + + while (!valid_memory_range(info, zero_page_base, + zero_page_base + zero_page_size - 1)) { + zero_page_base += 0x1000; + zero_page_size -= 0x1000; + if (zero_page_size == 0) + die("Unable to determine zero page size from %p \n", + (void *)empty_zero); + } + + param = xmalloc(zero_page_size); + for (k = 0; k < (zero_page_size / 0x1000); k++) + kexec_sh_setup_zero_page(param + (k * 0x1000), 0x1000, + command_line); + + add_segment(info, param, zero_page_size, + 0x80000000 | zero_page_base, zero_page_size); + + /* load image a bit above the zero page, round up to 64k + * the zImage will relocate itself, but only up seems supported. + */ + + image_base = _ALIGN(empty_zero, 0x10000); + add_segment(info, buf, len, image_base, len); + info->entry = (void *)virt_to_phys(image_base); + return 0; +} diff --git a/kexec/arch/sh/netbsd_booter.S b/kexec/arch/sh/netbsd_booter.S new file mode 100644 index 0000000..d4d16df --- /dev/null +++ b/kexec/arch/sh/netbsd_booter.S @@ -0,0 +1,47 @@ + .globl netbsd_booter +netbsd_booter: + mov.l ccr,r0 + mov #0,r1 + mov.l r1,@r0 + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + mova netbsd_start,r0 + mov.l @r0,r1 + add #4,r0 + mov.l @r0,r4 ! howto + add #4,r0 + mov r0,r5 ! bootinfo + jmp @r1 + nop + nop + nop + .align 4 +ccr: .long 0xff00001c + + .align 8 +netbsd_start: + .long 0x8c001000 + .long 0x200 ! howto + .long 0 ! bootinfo + .long 0 ! bootinfo + .long 0 ! bootinfo + .long 0 ! bootinfo + .long 0 ! bootinfo + .long 0 ! bootinfo + .long 0 ! bootinfo + .long 0 ! bootinfo + .long 0 ! bootinfo + .long 0 ! bootinfo + .long 0 ! bootinfo + .long 0 ! bootinfo + .long 0 ! bootinfo + .long 0 ! bootinfo + diff --git a/kexec/arch/x86_64/Makefile b/kexec/arch/x86_64/Makefile new file mode 100644 index 0000000..275add5 --- /dev/null +++ b/kexec/arch/x86_64/Makefile @@ -0,0 +1,24 @@ +# +# kexec x86_64 (linux booting linux) +# +x86_64_KEXEC_SRCS = kexec/arch/i386/kexec-elf-x86.c +x86_64_KEXEC_SRCS += kexec/arch/i386/kexec-bzImage.c +x86_64_KEXEC_SRCS += kexec/arch/i386/kexec-multiboot-x86.c +x86_64_KEXEC_SRCS += kexec/arch/i386/kexec-mb2-x86.c +x86_64_KEXEC_SRCS += kexec/arch/i386/kexec-beoboot-x86.c +x86_64_KEXEC_SRCS += kexec/arch/i386/kexec-nbi.c +x86_64_KEXEC_SRCS += kexec/arch/i386/x86-linux-setup.c +x86_64_KEXEC_SRCS += kexec/arch/i386/kexec-x86-common.c +x86_64_KEXEC_SRCS += kexec/arch/i386/crashdump-x86.c + +x86_64_KEXEC_SRCS_native = kexec/arch/x86_64/kexec-x86_64.c +x86_64_KEXEC_SRCS_native += kexec/arch/x86_64/kexec-elf-x86_64.c +x86_64_KEXEC_SRCS_native += kexec/arch/x86_64/kexec-elf-rel-x86_64.c +x86_64_KEXEC_SRCS_native += kexec/arch/x86_64/kexec-bzImage64.c + +x86_64_KEXEC_SRCS += $(x86_64_KEXEC_SRCS_native) + +# Don't add sources in i386/ to dist, as i386/Makefile adds them +dist += kexec/arch/x86_64/Makefile $(x86_64_KEXEC_SRCS_native) \ + kexec/arch/x86_64/kexec-x86_64.h \ + kexec/arch/x86_64/include/arch/options.h diff --git a/kexec/arch/x86_64/include/arch/options.h b/kexec/arch/x86_64/include/arch/options.h new file mode 120000 index 0000000..047b0f9 --- /dev/null +++ b/kexec/arch/x86_64/include/arch/options.h @@ -0,0 +1 @@ +../../../i386/include/arch/options.h
\ No newline at end of file diff --git a/kexec/arch/x86_64/kexec-bzImage64.c b/kexec/arch/x86_64/kexec-bzImage64.c new file mode 100644 index 0000000..aba4e3b --- /dev/null +++ b/kexec/arch/x86_64/kexec-bzImage64.c @@ -0,0 +1,397 @@ +/* + * kexec: Linux boots Linux + * + * Copyright (C) 2003-2010 Eric Biederman (ebiederm@xmission.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation (version 2 of the License). + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#define _GNU_SOURCE +#include <stddef.h> +#include <stdio.h> +#include <string.h> +#include <limits.h> +#include <stdlib.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <getopt.h> +#include <elf.h> +#include <boot/elf_boot.h> +#include <ip_checksum.h> +#include <x86/x86-linux.h> +#include "../../kexec.h" +#include "../../kexec-elf.h" +#include "../../kexec-syscall.h" +#include "kexec-x86_64.h" +#include "../i386/x86-linux-setup.h" +#include "../i386/crashdump-x86.h" +#include <arch/options.h> + +static const int probe_debug = 0; + +int bzImage64_probe(const char *buf, off_t len) +{ + const struct x86_linux_header *header; + + if ((uintmax_t)len < (uintmax_t)(2 * 512)) { + if (probe_debug) + fprintf(stderr, "File is too short to be a bzImage!\n"); + return -1; + } + header = (const struct x86_linux_header *)buf; + if (memcmp(header->header_magic, "HdrS", 4) != 0) { + if (probe_debug) + fprintf(stderr, "Not a bzImage\n"); + return -1; + } + if (header->boot_sector_magic != 0xAA55) { + if (probe_debug) + fprintf(stderr, "No x86 boot sector present\n"); + /* No x86 boot sector present */ + return -1; + } + if (header->protocol_version < 0x020C) { + if (probe_debug) + fprintf(stderr, "Must be at least protocol version 2.12\n"); + /* Must be at least protocol version 2.12 */ + return -1; + } + if ((header->loadflags & 1) == 0) { + if (probe_debug) + fprintf(stderr, "zImage not a bzImage\n"); + /* Not a bzImage */ + return -1; + } + if ((header->xloadflags & 3) != 3) { + if (probe_debug) + fprintf(stderr, "Not a relocatable bzImage64\n"); + /* Must be KERNEL_64 and CAN_BE_LOADED_ABOVE_4G */ + return -1; + } + +#define XLF_EFI_KEXEC (1 << 4) + if ((header->xloadflags & XLF_EFI_KEXEC) == XLF_EFI_KEXEC) + bzImage_support_efi_boot = 1; + + /* I've got a relocatable bzImage64 */ + if (probe_debug) + fprintf(stderr, "It's a relocatable bzImage64\n"); + return 0; +} + +void bzImage64_usage(void) +{ + printf( " --entry-32bit Use the kernels 32bit entry point.\n" + " --real-mode Use the kernels real mode entry point.\n" + " --command-line=STRING Set the kernel command line to STRING.\n" + " --append=STRING Set the kernel command line to STRING.\n" + " --reuse-cmdline Use kernel command line from running system.\n" + " --initrd=FILE Use FILE as the kernel's initial ramdisk.\n" + " --ramdisk=FILE Use FILE as the kernel's initial ramdisk.\n" + ); +} + +static int do_bzImage64_load(struct kexec_info *info, + const char *kernel, off_t kernel_len, + const char *command_line, off_t command_line_len, + const char *initrd, off_t initrd_len) +{ + struct x86_linux_header setup_header; + struct x86_linux_param_header *real_mode; + int setup_sects; + size_t size; + int kern16_size; + unsigned long setup_base, setup_size, setup_header_size; + struct entry64_regs regs64; + char *modified_cmdline; + unsigned long cmdline_end; + unsigned long align, addr, k_size; + unsigned kern16_size_needed; + + /* + * Find out about the file I am about to load. + */ + if ((uintmax_t)kernel_len < (uintmax_t)(2 * 512)) + return -1; + + memcpy(&setup_header, kernel, sizeof(setup_header)); + setup_sects = setup_header.setup_sects; + if (setup_sects == 0) + setup_sects = 4; + kern16_size = (setup_sects + 1) * 512; + if (kernel_len < kern16_size) { + fprintf(stderr, "BzImage truncated?\n"); + return -1; + } + + if ((uintmax_t)command_line_len > (uintmax_t)setup_header.cmdline_size) { + dbgprintf("Kernel command line too long for kernel!\n"); + return -1; + } + + /* Need to append some command line parameters internally in case of + * taking crash dumps. + */ + if (info->kexec_flags & (KEXEC_ON_CRASH | KEXEC_PRESERVE_CONTEXT)) { + modified_cmdline = xmalloc(COMMAND_LINE_SIZE); + memset((void *)modified_cmdline, 0, COMMAND_LINE_SIZE); + if (command_line) { + strncpy(modified_cmdline, command_line, + COMMAND_LINE_SIZE); + modified_cmdline[COMMAND_LINE_SIZE - 1] = '\0'; + } + + /* If panic kernel is being loaded, additional segments need + * to be created. load_crashdump_segments will take care of + * loading the segments as high in memory as possible, hence + * in turn as away as possible from kernel to avoid being + * stomped by the kernel. + */ + if (load_crashdump_segments(info, modified_cmdline, -1, 0) < 0) + return -1; + + /* Use new command line buffer */ + command_line = modified_cmdline; + command_line_len = strlen(command_line) + 1; + } + + /* x86_64 purgatory could be anywhere */ + elf_rel_build_load(info, &info->rhdr, purgatory, purgatory_size, + 0x3000, -1, -1, 0); + dbgprintf("Loaded purgatory at addr 0x%lx\n", info->rhdr.rel_addr); + /* The argument/parameter segment */ + kern16_size_needed = kern16_size; + if (kern16_size_needed < 4096) + kern16_size_needed = 4096; + setup_size = kern16_size_needed + command_line_len + + PURGATORY_CMDLINE_SIZE; + real_mode = xmalloc(setup_size); + memset(real_mode, 0, setup_size); + + /* only copy setup_header */ + setup_header_size = kernel[0x201] + 0x202 - 0x1f1; + if (setup_header_size > 0x7f) + setup_header_size = 0x7f; + memcpy((unsigned char *)real_mode + 0x1f1, kernel + 0x1f1, + setup_header_size); + + /* No real mode code will be executing. setup segment can be loaded + * anywhere as we will be just reading command line. + */ + setup_base = add_buffer(info, real_mode, setup_size, setup_size, + 16, 0x3000, -1, -1); + + dbgprintf("Loaded real_mode_data and command line at 0x%lx\n", + setup_base); + + /* The main kernel segment */ + k_size = kernel_len - kern16_size; + /* need to use run-time size for buffer searching */ + dbgprintf("kernel init_size 0x%x\n", real_mode->init_size); + size = _ALIGN(real_mode->init_size, 4096); + align = real_mode->kernel_alignment; + addr = add_buffer(info, kernel + kern16_size, k_size, + size, align, 0x100000, -1, -1); + if (addr == ULONG_MAX) + die("can not load bzImage64"); + dbgprintf("Loaded 64bit kernel at 0x%lx\n", addr); + + /* Tell the kernel what is going on */ + setup_linux_bootloader_parameters_high(info, real_mode, setup_base, + kern16_size_needed, command_line, command_line_len, + initrd, initrd_len, 1); /* put initrd high too */ + + elf_rel_get_symbol(&info->rhdr, "entry64_regs", ®s64, + sizeof(regs64)); + regs64.rbx = 0; /* Bootstrap processor */ + regs64.rsi = setup_base; /* Pointer to the parameters */ + regs64.rip = addr + 0x200; /* the entry point for startup_64 */ + regs64.rsp = elf_rel_get_addr(&info->rhdr, "stack_end"); /* Stack, unused */ + elf_rel_set_symbol(&info->rhdr, "entry64_regs", ®s64, + sizeof(regs64)); + + cmdline_end = setup_base + kern16_size_needed + command_line_len - 1; + elf_rel_set_symbol(&info->rhdr, "cmdline_end", &cmdline_end, + sizeof(unsigned long)); + + /* Fill in the information BIOS calls would normally provide. */ + setup_linux_system_parameters(info, real_mode); + + return 0; +} + +/* This assumes file is being loaded using file based kexec syscall */ +int bzImage64_load_file(int argc, char **argv, struct kexec_info *info) +{ + int ret = 0; + char *command_line = NULL, *tmp_cmdline = NULL; + const char *ramdisk = NULL, *append = NULL; + int entry_16bit = 0, entry_32bit = 0; + int opt; + int command_line_len; + + /* See options.h -- add any more there, too. */ + static const struct option options[] = { + KEXEC_ARCH_OPTIONS + { "command-line", 1, 0, OPT_APPEND }, + { "append", 1, 0, OPT_APPEND }, + { "reuse-cmdline", 0, 0, OPT_REUSE_CMDLINE }, + { "initrd", 1, 0, OPT_RAMDISK }, + { "ramdisk", 1, 0, OPT_RAMDISK }, + { "real-mode", 0, 0, OPT_REAL_MODE }, + { "entry-32bit", 0, 0, OPT_ENTRY_32BIT }, + { 0, 0, 0, 0 }, + }; + static const char short_options[] = KEXEC_ARCH_OPT_STR "d"; + + while ((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) { + switch (opt) { + default: + /* Ignore core options */ + if (opt < OPT_ARCH_MAX) + break; + case OPT_APPEND: + append = optarg; + break; + case OPT_REUSE_CMDLINE: + tmp_cmdline = get_command_line(); + break; + case OPT_RAMDISK: + ramdisk = optarg; + break; + case OPT_REAL_MODE: + entry_16bit = 1; + break; + case OPT_ENTRY_32BIT: + entry_32bit = 1; + break; + } + } + command_line = concat_cmdline(tmp_cmdline, append); + if (tmp_cmdline) + free(tmp_cmdline); + command_line_len = 0; + if (command_line) { + command_line_len = strlen(command_line) + 1; + } else { + command_line = strdup("\0"); + command_line_len = 1; + } + + if (entry_16bit || entry_32bit) { + fprintf(stderr, "Kexec2 syscall does not support 16bit" + " or 32bit entry yet\n"); + ret = -1; + goto out; + } + + if (ramdisk) { + info->initrd_fd = open(ramdisk, O_RDONLY); + if (info->initrd_fd == -1) { + fprintf(stderr, "Could not open initrd file %s:%s\n", + ramdisk, strerror(errno)); + ret = -1; + goto out; + } + } + + info->command_line = command_line; + info->command_line_len = command_line_len; + return ret; +out: + free(command_line); + return ret; +} + +int bzImage64_load(int argc, char **argv, const char *buf, off_t len, + struct kexec_info *info) +{ + char *command_line = NULL, *tmp_cmdline = NULL; + const char *ramdisk = NULL, *append = NULL; + char *ramdisk_buf; + off_t ramdisk_length = 0; + int command_line_len; + int entry_16bit = 0, entry_32bit = 0; + int opt; + int result; + + if (info->file_mode) + return bzImage64_load_file(argc, argv, info); + + /* See options.h -- add any more there, too. */ + static const struct option options[] = { + KEXEC_ARCH_OPTIONS + { "command-line", 1, 0, OPT_APPEND }, + { "append", 1, 0, OPT_APPEND }, + { "reuse-cmdline", 0, 0, OPT_REUSE_CMDLINE }, + { "initrd", 1, 0, OPT_RAMDISK }, + { "ramdisk", 1, 0, OPT_RAMDISK }, + { "real-mode", 0, 0, OPT_REAL_MODE }, + { "entry-32bit", 0, 0, OPT_ENTRY_32BIT }, + { 0, 0, 0, 0 }, + }; + static const char short_options[] = KEXEC_ARCH_OPT_STR "d"; + + while ((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) { + switch (opt) { + default: + /* Ignore core options */ + if (opt < OPT_ARCH_MAX) + break; + case OPT_APPEND: + append = optarg; + break; + case OPT_REUSE_CMDLINE: + tmp_cmdline = get_command_line(); + break; + case OPT_RAMDISK: + ramdisk = optarg; + break; + case OPT_REAL_MODE: + entry_16bit = 1; + break; + case OPT_ENTRY_32BIT: + entry_32bit = 1; + break; + } + } + command_line = concat_cmdline(tmp_cmdline, append); + if (tmp_cmdline) + free(tmp_cmdline); + command_line_len = 0; + if (command_line) { + command_line_len = strlen(command_line) + 1; + } else { + command_line = strdup("\0"); + command_line_len = 1; + } + ramdisk_buf = 0; + if (ramdisk) + ramdisk_buf = slurp_file(ramdisk, &ramdisk_length); + + if (entry_16bit || entry_32bit) + result = do_bzImage_load(info, buf, len, command_line, + command_line_len, ramdisk_buf, + ramdisk_length, 0, 0, entry_16bit); + else + result = do_bzImage64_load(info, buf, len, command_line, + command_line_len, ramdisk_buf, + ramdisk_length); + + free(command_line); + return result; +} diff --git a/kexec/arch/x86_64/kexec-elf-rel-x86_64.c b/kexec/arch/x86_64/kexec-elf-rel-x86_64.c new file mode 100644 index 0000000..0d22f3b --- /dev/null +++ b/kexec/arch/x86_64/kexec-elf-rel-x86_64.c @@ -0,0 +1,94 @@ +#include <stdio.h> +#include <elf.h> +#include "../../kexec.h" +#include "../../kexec-elf.h" + +int machine_verify_elf_rel(struct mem_ehdr *ehdr) +{ + if (ehdr->ei_data != ELFDATA2LSB) { + return 0; + } + if (ehdr->ei_class != ELFCLASS64 && + ehdr->ei_class != ELFCLASS32) { /* x32 */ + return 0; + } + if (ehdr->e_machine != EM_X86_64) { + return 0; + } + return 1; +} + +static const char *reloc_name(unsigned long r_type) +{ + static const char *r_name[] = { + "R_X86_64_NONE", + "R_X86_64_64", + "R_X86_64_PC32", + "R_X86_64_GOT32", + "R_X86_64_PLT32", + "R_X86_64_COPY", + "R_X86_64_GLOB_DAT", + "R_X86_64_JUMP_SLOT", + "R_X86_64_RELATIVE", + "R_X86_64_GOTPCREL", + "R_X86_64_32", + "R_X86_64_32S", + "R_X86_64_16", + "R_X86_64_PC16", + "R_X86_64_8", + "R_X86_64_PC8", + "R_X86_64_DTPMOD64", + "R_X86_64_DTPOFF64", + "R_X86_64_TPOFF64", + "R_X86_64_TLSGD", + "R_X86_64_TLSLD", + "R_X86_64_DTPOFF32", + "R_X86_64_GOTTPOFF", + "R_X86_64_TPOFF32", + }; + static char buf[100]; + const char *name; + if (r_type < (sizeof(r_name)/sizeof(r_name[0]))){ + name = r_name[r_type]; + } + else { + sprintf(buf, "R_X86_64_%lu", r_type); + name = buf; + } + return name; +} + +void machine_apply_elf_rel(struct mem_ehdr *UNUSED(ehdr), + struct mem_sym *UNUSED(sym), unsigned long r_type, void *location, + unsigned long address, unsigned long value) +{ + dbgprintf("%s\n", reloc_name(r_type)); + switch(r_type) { + case R_X86_64_NONE: + break; + case R_X86_64_64: + *(uint64_t *)location = value; + break; + case R_X86_64_32: + *(uint32_t *)location = value; + if (value != *(uint32_t *)location) + goto overflow; + break; + case R_X86_64_32S: + *(uint32_t *)location = value; + if ((int64_t)value != *(int32_t *)location) + goto overflow; + break; + case R_X86_64_PC32: + case R_X86_64_PLT32: + *(uint32_t *)location = value - address; + break; + default: + die("Unhandled rela relocation: %s\n", reloc_name(r_type)); + break; + } + return; + overflow: + die("overflow in relocation type %s val %lx\n", + reloc_name(r_type), value); +} diff --git a/kexec/arch/x86_64/kexec-elf-x86_64.c b/kexec/arch/x86_64/kexec-elf-x86_64.c new file mode 100644 index 0000000..7f9540a --- /dev/null +++ b/kexec/arch/x86_64/kexec-elf-x86_64.c @@ -0,0 +1,257 @@ +/* + * kexec: Linux boots Linux + * + * Copyright (C) 2003-2005 Eric Biederman (ebiederm@xmission.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation (version 2 of the License). + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#define _GNU_SOURCE +#include <stddef.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <limits.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <getopt.h> +#include <elf.h> +#include <x86/x86-linux.h> +#include "../../kexec.h" +#include "../../kexec-syscall.h" +#include "../../kexec-elf.h" +#include "../../kexec-elf-boot.h" +#include "../i386/x86-linux-setup.h" +#include "kexec-x86_64.h" +#include "../i386/kexec-x86.h" +#include "../i386/crashdump-x86.h" +#include <arch/options.h> + +int elf_x86_64_probe(const char *buf, off_t len) +{ + return elf_x86_any_probe(buf, len, CORE_TYPE_ELF64); +} + +void elf_x86_64_usage(void) +{ + printf( " --command-line=STRING Set the kernel command line to STRING\n" + " --append=STRING Set the kernel command line to STRING\n" + " --reuse-cmdline Use kernel command line from running system.\n" + " --initrd=FILE Use FILE as the kernel's initial ramdisk.\n" + " --ramdisk=FILE Use FILE as the kernel's initial ramdisk.\n" + " --args-linux Pass linux kernel style options\n" + " --args-elf Pass elf boot notes\n" + " --args-none Jump directly from the kernel\n" + ); +} + +int elf_x86_64_load(int argc, char **argv, const char *buf, off_t len, + struct kexec_info *info) +{ + struct mem_ehdr ehdr; + const char *append = NULL; + char *command_line = NULL, *modified_cmdline; + char *tmp_cmdline = NULL; + int command_line_len; + const char *ramdisk; + unsigned long entry, max_addr; + int arg_style; +#define ARG_STYLE_ELF 0 +#define ARG_STYLE_LINUX 1 +#define ARG_STYLE_NONE 2 + int opt; + int result = 0; + const char *error_msg = NULL; + + /* See options.h and add any new options there too! */ + static const struct option options[] = { + KEXEC_ARCH_OPTIONS + { "command-line", 1, NULL, OPT_APPEND }, + { "append", 1, NULL, OPT_APPEND }, + { "reuse-cmdline", 0, NULL, OPT_REUSE_CMDLINE }, + { "initrd", 1, NULL, OPT_RAMDISK }, + { "ramdisk", 1, NULL, OPT_RAMDISK }, + { "args-elf", 0, NULL, OPT_ARGS_ELF }, + { "args-linux", 0, NULL, OPT_ARGS_LINUX }, + { "args-none", 0, NULL, OPT_ARGS_NONE }, + { 0, 0, NULL, 0 }, + }; + + static const char short_options[] = KEXEC_ARCH_OPT_STR ""; + + /* + * Parse the command line arguments + */ + arg_style = ARG_STYLE_ELF; + modified_cmdline = 0; + ramdisk = 0; + while((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) { + switch(opt) { + default: + /* Ignore core options */ + if (opt < OPT_ARCH_MAX) { + break; + } + fprintf(stderr, "Unknown option: opt: %d\n", opt); + case OPT_APPEND: + append = optarg; + break; + case OPT_REUSE_CMDLINE: + tmp_cmdline = get_command_line(); + break; + case OPT_RAMDISK: + ramdisk = optarg; + break; + case OPT_ARGS_ELF: + arg_style = ARG_STYLE_ELF; + break; + case OPT_ARGS_LINUX: + arg_style = ARG_STYLE_LINUX; + break; + case OPT_ARGS_NONE: +#ifdef __x86_64__ + arg_style = ARG_STYLE_NONE; +#else + die("--args-none only works on arch x86_64\n"); +#endif + break; + } + } + command_line = concat_cmdline(tmp_cmdline, append); + if (tmp_cmdline) + free(tmp_cmdline); + command_line_len = 0; + if (command_line) { + command_line_len = strlen(command_line) +1; + } + + /* Need to append some command line parameters internally in case of + * taking crash dumps. + */ + if (info->kexec_flags & KEXEC_ON_CRASH) { + modified_cmdline = xmalloc(COMMAND_LINE_SIZE); + memset((void *)modified_cmdline, 0, COMMAND_LINE_SIZE); + if (command_line) { + strncpy(modified_cmdline, command_line, + COMMAND_LINE_SIZE); + modified_cmdline[COMMAND_LINE_SIZE - 1] = '\0'; + } + } + + /* Load the ELF executable */ + elf_exec_build_load(info, &ehdr, buf, len, 0); + + entry = ehdr.e_entry; + max_addr = elf_max_addr(&ehdr); + + /* Do we want arguments? */ + if (arg_style != ARG_STYLE_NONE) { + /* Load the setup code */ + elf_rel_build_load(info, &info->rhdr, purgatory, purgatory_size, + 0, ULONG_MAX, 1, 0); + } + if (arg_style == ARG_STYLE_NONE) { + info->entry = (void *)entry; + + } + else if (arg_style == ARG_STYLE_ELF) { + unsigned long note_base; + struct entry64_regs regs; + + /* Setup the ELF boot notes */ + note_base = elf_boot_notes(info, max_addr, command_line, command_line_len); + + /* Initialize the registers */ + elf_rel_get_symbol(&info->rhdr, "entry64_regs", ®s, sizeof(regs)); + regs.rdi = note_base; /* The notes (arg1) */ + regs.rip = entry; /* The entry point */ + regs.rsp = elf_rel_get_addr(&info->rhdr, "stack_end"); /* Stack, unused */ + elf_rel_set_symbol(&info->rhdr, "entry64_regs", ®s, sizeof(regs)); + + if (ramdisk) { + error_msg = "Ramdisks not supported with generic elf arguments"; + goto out; + } + } + else if (arg_style == ARG_STYLE_LINUX) { + struct x86_linux_faked_param_header *hdr; + unsigned long param_base; + char *ramdisk_buf; + off_t ramdisk_length; + struct entry64_regs regs; + int rc=0; + + /* Get the linux parameter header */ + hdr = xmalloc(sizeof(*hdr)); + param_base = add_buffer(info, hdr, sizeof(*hdr), sizeof(*hdr), + 16, 0, max_addr, 1); + + /* Initialize the parameter header */ + memset(hdr, 0, sizeof(*hdr)); + init_linux_parameters(&hdr->hdr); + + /* Add a ramdisk to the current image */ + ramdisk_buf = 0; + ramdisk_length = 0; + if (ramdisk) { + ramdisk_buf = slurp_file(ramdisk, &ramdisk_length); + } + + /* If panic kernel is being loaded, additional segments need + * to be created. */ + if (info->kexec_flags & KEXEC_ON_CRASH) { + rc = load_crashdump_segments(info, modified_cmdline, + max_addr, 0); + if (rc < 0) { + result = -1; + goto out; + } + /* Use new command line. */ + free(command_line); + command_line = modified_cmdline; + command_line_len = strlen(modified_cmdline) + 1; + modified_cmdline = NULL; + } + + /* Tell the kernel what is going on */ + setup_linux_bootloader_parameters(info, &hdr->hdr, param_base, + offsetof(struct x86_linux_faked_param_header, command_line), + command_line, command_line_len, + ramdisk_buf, ramdisk_length); + + /* Fill in the information bios calls would usually provide */ + setup_linux_system_parameters(info, &hdr->hdr); + + /* Initialize the registers */ + elf_rel_get_symbol(&info->rhdr, "entry64_regs", ®s, sizeof(regs)); + regs.rbx = 0; /* Bootstrap processor */ + regs.rsi = param_base; /* Pointer to the parameters */ + regs.rip = entry; /* the entry point */ + regs.rsp = elf_rel_get_addr(&info->rhdr, "stack_end"); /* Stack, unused */ + elf_rel_set_symbol(&info->rhdr, "entry64_regs", ®s, sizeof(regs)); + } + else { + error_msg = "Unknown argument style\n"; + } + +out: + free(command_line); + free(modified_cmdline); + if (error_msg) + die("%s", error_msg); + return result; +} diff --git a/kexec/arch/x86_64/kexec-x86_64.c b/kexec/arch/x86_64/kexec-x86_64.c new file mode 100644 index 0000000..ffd84f0 --- /dev/null +++ b/kexec/arch/x86_64/kexec-x86_64.c @@ -0,0 +1,190 @@ +/* + * kexec: Linux boots Linux + * + * Copyright (C) 2003,2004 Eric Biederman (ebiederm@xmission.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation (version 2 of the License). + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <stddef.h> +#include <stdio.h> +#include <errno.h> +#include <stdint.h> +#include <string.h> +#include <limits.h> +#include <stdlib.h> +#include <getopt.h> +#include "../../kexec.h" +#include "../../kexec-elf.h" +#include "../../kexec-syscall.h" +#include "kexec-x86_64.h" +#include "../i386/crashdump-x86.h" +#include <arch/options.h> + +struct file_type file_type[] = { + { "multiboot2-x86", multiboot2_x86_probe, multiboot2_x86_load, + multiboot2_x86_usage }, + { "elf-x86_64", elf_x86_64_probe, elf_x86_64_load, elf_x86_64_usage }, + { "multiboot-x86", multiboot_x86_probe, multiboot_x86_load, + multiboot_x86_usage }, + { "elf-x86", elf_x86_probe, elf_x86_load, elf_x86_usage }, + { "bzImage64", bzImage64_probe, bzImage64_load, bzImage64_usage }, + { "bzImage", bzImage_probe, bzImage_load, bzImage_usage }, + { "beoboot-x86", beoboot_probe, beoboot_load, beoboot_usage }, + { "nbi-x86", nbi_probe, nbi_load, nbi_usage }, +}; +int file_types = sizeof(file_type)/sizeof(file_type[0]); + + +void arch_usage(void) +{ + printf( + " --reset-vga Attempt to reset a standard vga device\n" + " --serial=<port> Specify the serial port for debug output\n" + " --serial-baud=<baud_rate> Specify the serial port baud rate\n" + " --console-vga Enable the vga console\n" + " --console-serial Enable the serial console\n" + " --pass-memmap-cmdline Pass memory map via command line in kexec on panic case\n" + " --noefi Disable efi support\n" + " --reuse-video-type Reuse old boot time video type blindly\n" + ); +} + +struct arch_options_t arch_options = { + .reset_vga = 0, + .serial_base = 0x3f8, + .serial_baud = 0, + .console_vga = 0, + .console_serial = 0, + .core_header_type = CORE_TYPE_ELF64, + .pass_memmap_cmdline = 0, + .noefi = 0, + .reuse_video_type = 0, +}; + +int arch_process_options(int argc, char **argv) +{ + static const struct option options[] = { + KEXEC_ALL_OPTIONS + { 0, 0, NULL, 0 }, + }; + static const char short_options[] = KEXEC_ALL_OPT_STR; + int opt; + unsigned long value; + char *end; + + opterr = 0; /* Don't complain about unrecognized options here */ + while((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) { + switch(opt) { + default: + break; + case OPT_RESET_VGA: + arch_options.reset_vga = 1; + break; + case OPT_CONSOLE_VGA: + arch_options.console_vga = 1; + break; + case OPT_CONSOLE_SERIAL: + arch_options.console_serial = 1; + break; + case OPT_SERIAL: + value = ULONG_MAX; + if (strcmp(optarg, "ttyS0") == 0) { + value = 0x3f8; + } + else if (strcmp(optarg, "ttyS1") == 0) { + value = 0x2f8; + } + else if (strncmp(optarg, "0x", 2) == 0) { + value = strtoul(optarg +2, &end, 16); + if (*end != '\0') { + value = ULONG_MAX; + } + } + if (value >= 65536) { + fprintf(stderr, "Bad serial port base '%s'\n", + optarg); + usage(); + return -1; + + } + arch_options.serial_base = value; + break; + case OPT_SERIAL_BAUD: + value = strtoul(optarg, &end, 0); + if ((value > 115200) || ((115200 %value) != 0) || + (value < 9600) || (*end)) + { + fprintf(stderr, "Bad serial port baud rate '%s'\n", + optarg); + usage(); + return -1; + + } + arch_options.serial_baud = value; + break; + case OPT_PASS_MEMMAP_CMDLINE: + arch_options.pass_memmap_cmdline = 1; + break; + case OPT_NOEFI: + arch_options.noefi = 1; + break; + case OPT_REUSE_VIDEO_TYPE: + arch_options.reuse_video_type = 1; + break; + } + } + /* Reset getopt for the next pass; called in other source modules */ + opterr = 1; + optind = 1; + return 0; +} + +const struct arch_map_entry arches[] = { + { "x86_64", KEXEC_ARCH_X86_64 }, + { NULL, 0 }, +}; + +int arch_compat_trampoline(struct kexec_info *UNUSED(info)) +{ + return 0; +} + +void arch_update_purgatory(struct kexec_info *info) +{ + uint8_t panic_kernel = 0; + + elf_rel_set_symbol(&info->rhdr, "reset_vga", + &arch_options.reset_vga, sizeof(arch_options.reset_vga)); + elf_rel_set_symbol(&info->rhdr, "serial_base", + &arch_options.serial_base, sizeof(arch_options.serial_base)); + elf_rel_set_symbol(&info->rhdr, "serial_baud", + &arch_options.serial_baud, sizeof(arch_options.serial_baud)); + elf_rel_set_symbol(&info->rhdr, "console_vga", + &arch_options.console_vga, sizeof(arch_options.console_vga)); + elf_rel_set_symbol(&info->rhdr, "console_serial", + &arch_options.console_serial, sizeof(arch_options.console_serial)); + elf_rel_set_symbol(&info->rhdr, "backup_src_start", + &info->backup_src_start, sizeof(info->backup_src_start)); + elf_rel_set_symbol(&info->rhdr, "backup_src_size", + &info->backup_src_size, sizeof(info->backup_src_size)); + + if (info->kexec_flags & KEXEC_ON_CRASH) { + panic_kernel = 1; + elf_rel_set_symbol(&info->rhdr, "backup_start", + &info->backup_start, sizeof(info->backup_start)); + } + elf_rel_set_symbol(&info->rhdr, "panic_kernel", + &panic_kernel, sizeof(panic_kernel)); +} diff --git a/kexec/arch/x86_64/kexec-x86_64.h b/kexec/arch/x86_64/kexec-x86_64.h new file mode 100644 index 0000000..21c3a73 --- /dev/null +++ b/kexec/arch/x86_64/kexec-x86_64.h @@ -0,0 +1,41 @@ +#ifndef KEXEC_X86_64_H +#define KEXEC_X86_64_H + +#include "../i386/kexec-x86.h" + +struct entry64_regs { + uint64_t rax; + uint64_t rbx; + uint64_t rcx; + uint64_t rdx; + uint64_t rsi; + uint64_t rdi; + uint64_t rsp; + uint64_t rbp; + uint64_t r8; + uint64_t r9; + uint64_t r10; + uint64_t r11; + uint64_t r12; + uint64_t r13; + uint64_t r14; + uint64_t r15; + uint64_t rip; +}; + +int elf_x86_64_probe(const char *buf, off_t len); +int elf_x86_64_load(int argc, char **argv, const char *buf, off_t len, + struct kexec_info *info); +void elf_x86_64_usage(void); + +int bzImage64_probe(const char *buf, off_t len); +int bzImage64_load(int argc, char **argv, const char *buf, off_t len, + struct kexec_info *info); +void bzImage64_usage(void); + +int multiboot2_x86_load(int argc, char **argv, const char *buf, off_t len, + struct kexec_info *info); +void multiboot2_x86_usage(void); +int multiboot2_x86_probe(const char *buf, off_t buf_len); + +#endif /* KEXEC_X86_64_H */ diff --git a/kexec/arch_reuse_initrd.c b/kexec/arch_reuse_initrd.c new file mode 100644 index 0000000..b92b8d8 --- /dev/null +++ b/kexec/arch_reuse_initrd.c @@ -0,0 +1,8 @@ +#include "kexec.h" + +unsigned char reuse_initrd = 0; + +void arch_reuse_initrd(void) +{ + die("--reuseinitrd not implemented on this architecture\n"); +} diff --git a/kexec/crashdump-elf.c b/kexec/crashdump-elf.c new file mode 100644 index 0000000..b8bb686 --- /dev/null +++ b/kexec/crashdump-elf.c @@ -0,0 +1,241 @@ + +#if !defined(FUNC) || !defined(EHDR) || !defined(PHDR) +#error FUNC, EHDR and PHDR must be defined +#endif + +#if (ELF_WIDTH == 64) +#define dbgprintf_phdr(prefix, phdr) \ +do { \ + dbgprintf("%s: p_type = %u, p_offset = 0x%llx p_paddr = 0x%llx " \ + "p_vaddr = 0x%llx p_filesz = 0x%llx p_memsz = 0x%llx\n", \ + (prefix), (phdr)->p_type, \ + (unsigned long long)((phdr)->p_offset), \ + (unsigned long long)((phdr)->p_paddr), \ + (unsigned long long)((phdr)->p_vaddr), \ + (unsigned long long)((phdr)->p_filesz), \ + (unsigned long long)((phdr)->p_memsz)); \ +} while(0) +#else +#define dbgprintf_phdr(prefix, phdr) \ +do { \ + dbgprintf("%s: p_type = %u, p_offset = 0x%x " "p_paddr = 0x%x " \ + "p_vaddr = 0x%x p_filesz = 0x%x p_memsz = 0x%x\n", \ + (prefix), (phdr)->p_type, (phdr)->p_offset, (phdr)->p_paddr, \ + (phdr)->p_vaddr, (phdr)->p_filesz, (phdr)->p_memsz); \ +} while(0) +#endif + +/* Prepares the crash memory headers and stores in supplied buffer. */ +int FUNC(struct kexec_info *info, + struct crash_elf_info *elf_info, + struct memory_range *range, int ranges, + void **buf, unsigned long *size, unsigned long align) +{ + EHDR *elf; + PHDR *phdr; + int i; + unsigned long sz; + char *bufp; + long int nr_cpus = 0; + uint64_t notes_addr, notes_len; + uint64_t vmcoreinfo_addr, vmcoreinfo_len; + int has_vmcoreinfo = 0; + int (*get_note_info)(int cpu, uint64_t *addr, uint64_t *len); + long int count_cpu; + + if (xen_present()) + nr_cpus = xen_get_nr_phys_cpus(); + else + nr_cpus = sysconf(_SC_NPROCESSORS_CONF); + + if (nr_cpus < 0) { + return -1; + } + + if (xen_present()) { + if (!get_xen_vmcoreinfo(&vmcoreinfo_addr, &vmcoreinfo_len)) + has_vmcoreinfo = 1; + } else + if (!get_kernel_vmcoreinfo(&vmcoreinfo_addr, &vmcoreinfo_len)) + has_vmcoreinfo = 1; + + sz = sizeof(EHDR) + (nr_cpus + has_vmcoreinfo) * sizeof(PHDR) + + ranges * sizeof(PHDR); + + /* + * Certain architectures such as x86_64 and ia64 require a separate + * PT_LOAD program header for the kernel. This is controlled through + * elf_info->kern_size. + * + * The separate PT_LOAD program header is required either because the + * kernel is mapped at a different location than the rest of the + * physical memory or because we need to support relocatable kernels. + * Or both as on x86_64. + * + * In the relocatable kernel case this PT_LOAD segment is used to tell + * where the kernel was actually loaded which may be different from + * the load address present in the vmlinux file. + * + * The extra kernel PT_LOAD program header results in a vmcore file + * which is larger than the size of the physical memory. This is + * because the memory for the kernel is present both in the kernel + * PT_LOAD program header and in the physical RAM program headers. + */ + + if (elf_info->kern_size && !xen_present()) { + sz += sizeof(PHDR); + } + + /* + * Make sure the ELF core header is aligned to at least 1024. + * We do this because the secondary kernel gets the ELF core + * header address on the kernel command line through the memmap= + * option, and this option requires 1k granularity. + */ + + if (align % ELF_CORE_HEADER_ALIGN) { + return -1; + } + + sz = _ALIGN(sz, align); + + bufp = xmalloc(sz); + memset(bufp, 0, sz); + + *buf = bufp; + *size = sz; + + /* Setup ELF Header*/ + elf = (EHDR *) bufp; + bufp += sizeof(EHDR); + memcpy(elf->e_ident, ELFMAG, SELFMAG); + elf->e_ident[EI_CLASS] = elf_info->class; + elf->e_ident[EI_DATA] = elf_info->data; + elf->e_ident[EI_VERSION]= EV_CURRENT; + elf->e_ident[EI_OSABI] = ELFOSABI_NONE; + memset(elf->e_ident+EI_PAD, 0, EI_NIDENT-EI_PAD); + elf->e_type = ET_CORE; + elf->e_machine = crash_architecture(elf_info); + elf->e_version = EV_CURRENT; + elf->e_entry = 0; + elf->e_phoff = sizeof(EHDR); + elf->e_shoff = 0; + elf->e_flags = 0; + elf->e_ehsize = sizeof(EHDR); + elf->e_phentsize= sizeof(PHDR); + elf->e_phnum = 0; + elf->e_shentsize= 0; + elf->e_shnum = 0; + elf->e_shstrndx = 0; + + /* Default way to get crash notes is by get_crash_notes_per_cpu() */ + + get_note_info = elf_info->get_note_info; + if (!get_note_info) + get_note_info = get_crash_notes_per_cpu; + + if (xen_present()) + get_note_info = xen_get_note; + + /* PT_NOTE program headers. One per cpu */ + + count_cpu = nr_cpus; + for (i = 0; count_cpu > 0; i++) { + int ret; + + ret = get_note_info(i, ¬es_addr, ¬es_len); + count_cpu--; + if (ret < 0) /* This cpu is not present. Skip it. */ + continue; + + phdr = (PHDR *) bufp; + bufp += sizeof(PHDR); + phdr->p_type = PT_NOTE; + phdr->p_flags = 0; + phdr->p_offset = phdr->p_paddr = notes_addr; + phdr->p_vaddr = 0; + phdr->p_filesz = phdr->p_memsz = notes_len; + /* Do we need any alignment of segments? */ + phdr->p_align = 0; + + /* Increment number of program headers. */ + (elf->e_phnum)++; + dbgprintf_phdr("Elf header", phdr); + } + + if (has_vmcoreinfo && !(info->kexec_flags & KEXEC_PRESERVE_CONTEXT)) { + phdr = (PHDR *) bufp; + bufp += sizeof(PHDR); + phdr->p_type = PT_NOTE; + phdr->p_flags = 0; + phdr->p_offset = phdr->p_paddr = vmcoreinfo_addr; + phdr->p_vaddr = 0; + phdr->p_filesz = phdr->p_memsz = vmcoreinfo_len; + /* Do we need any alignment of segments? */ + phdr->p_align = 0; + + (elf->e_phnum)++; + dbgprintf_phdr("vmcoreinfo header", phdr); + } + + /* Setup an PT_LOAD type program header for the region where + * Kernel is mapped if elf_info->kern_size is non-zero. + */ + + if (elf_info->kern_size && !xen_present()) { + phdr = (PHDR *) bufp; + bufp += sizeof(PHDR); + phdr->p_type = PT_LOAD; + phdr->p_flags = PF_R|PF_W|PF_X; + phdr->p_offset = phdr->p_paddr = elf_info->kern_paddr_start; + phdr->p_vaddr = elf_info->kern_vaddr_start; + phdr->p_filesz = phdr->p_memsz = elf_info->kern_size; + phdr->p_align = 0; + (elf->e_phnum)++; + dbgprintf_phdr("Kernel text Elf header", phdr); + } + + /* Setup PT_LOAD type program header for every system RAM chunk. + * A seprate program header for Backup Region*/ + for (i = 0; i < ranges; i++, range++) { + unsigned long long mstart, mend; + if (range->type != RANGE_RAM) + continue; + mstart = range->start; + mend = range->end; + if (!mstart && !mend) + continue; + phdr = (PHDR *) bufp; + bufp += sizeof(PHDR); + phdr->p_type = PT_LOAD; + phdr->p_flags = PF_R|PF_W|PF_X; + phdr->p_offset = mstart; + + if (mstart == info->backup_src_start + && (mend - mstart + 1) == info->backup_src_size) + phdr->p_offset = info->backup_start; + + /* We already prepared the header for kernel text. Map + * rest of the memory segments to kernel linearly mapped + * memory region. + */ + phdr->p_paddr = mstart; + phdr->p_vaddr = phys_to_virt(elf_info, mstart); + phdr->p_filesz = phdr->p_memsz = mend - mstart + 1; + /* Do we need any alignment of segments? */ + phdr->p_align = 0; + + /* HIGMEM has a virtual address of -1 */ + + if (elf_info->lowmem_limit + && (mend > (elf_info->lowmem_limit - 1))) + phdr->p_vaddr = -1; + + /* Increment number of program headers. */ + (elf->e_phnum)++; + dbgprintf_phdr("Elf header", phdr); + } + return 0; +} + +#undef dbgprintf_phdr diff --git a/kexec/crashdump-xen.c b/kexec/crashdump-xen.c new file mode 100644 index 0000000..3f59a0d --- /dev/null +++ b/kexec/crashdump-xen.c @@ -0,0 +1,256 @@ +#define _GNU_SOURCE +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <stdlib.h> +#include <elf.h> +#include <errno.h> +#include <limits.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> +#include <setjmp.h> +#include <signal.h> +#include "kexec.h" +#include "crashdump.h" +#include "kexec-syscall.h" +#include "config.h" +#include "kexec-xen.h" + +struct crash_note_info { + unsigned long base; + unsigned long length; +}; + +static int xen_phys_cpus; +static struct crash_note_info *xen_phys_notes; + +/* based on code from xen-detect.c */ +static int is_dom0; +#if defined(__i386__) || defined(__x86_64__) +static jmp_buf xen_sigill_jmp; +void xen_sigill_handler(int sig) +{ + longjmp(xen_sigill_jmp, 1); +} + +static void xen_cpuid(uint32_t idx, uint32_t *regs, int pv_context) +{ +#ifdef __i386__ + /* Use the stack to avoid reg constraint failures with some gcc flags */ + asm volatile ( + "push %%eax; push %%ebx; push %%ecx; push %%edx\n\t" + "test %1,%1 ; jz 1f ; ud2a ; .ascii \"xen\" ; 1: cpuid\n\t" + "mov %%eax,(%2); mov %%ebx,4(%2)\n\t" + "mov %%ecx,8(%2); mov %%edx,12(%2)\n\t" + "pop %%edx; pop %%ecx; pop %%ebx; pop %%eax\n\t" + : : "a" (idx), "c" (pv_context), "S" (regs) : "memory" ); +#else + asm volatile ( + "test %5,%5 ; jz 1f ; ud2a ; .ascii \"xen\" ; 1: cpuid\n\t" + : "=a" (regs[0]), "=b" (regs[1]), "=c" (regs[2]), "=d" (regs[3]) + : "0" (idx), "1" (pv_context), "2" (0) ); +#endif +} + +static int check_for_xen(int pv_context) +{ + uint32_t regs[4]; + char signature[13]; + uint32_t base; + + for (base = 0x40000000; base < 0x40010000; base += 0x100) + { + xen_cpuid(base, regs, pv_context); + + *(uint32_t *)(signature + 0) = regs[1]; + *(uint32_t *)(signature + 4) = regs[2]; + *(uint32_t *)(signature + 8) = regs[3]; + signature[12] = '\0'; + + if (strcmp("XenVMMXenVMM", signature) == 0 && regs[0] >= (base + 2)) + goto found; + } + + return 0; + +found: + xen_cpuid(base + 1, regs, pv_context); + return regs[0]; +} + +static int xen_detect_pv_guest(void) +{ + struct sigaction act, oldact; + int is_pv = -1; + + if (setjmp(xen_sigill_jmp)) + return is_pv; + + memset(&act, 0, sizeof(act)); + act.sa_handler = xen_sigill_handler; + sigemptyset (&act.sa_mask); + if (sigaction(SIGILL, &act, &oldact)) + return is_pv; + if (check_for_xen(1)) + is_pv = 1; + sigaction(SIGILL, &oldact, NULL); + return is_pv; +} +#else +static int xen_detect_pv_guest(void) +{ + return 1; +} +#endif + +/* + * Return 1 if its a PV guest. + * This includes dom0, which is the only PV guest where kexec/kdump works. + * HVM guests have to be handled as native hardware. + */ +int xen_present(void) +{ + if (!is_dom0) { + if (access("/proc/xen", F_OK) == 0) + is_dom0 = xen_detect_pv_guest(); + else + is_dom0 = -1; + } + return is_dom0 > 0; +} + +unsigned long xen_architecture(struct crash_elf_info *elf_info) +{ + unsigned long machine = elf_info->machine; +#ifdef HAVE_LIBXENCTRL + int rc; + xen_capabilities_info_t capabilities; + xc_interface *xc; + + if (!xen_present()) + goto out; + + memset(capabilities, '0', XEN_CAPABILITIES_INFO_LEN); + + xc = xc_interface_open(NULL, NULL, 0); + if ( !xc ) { + fprintf(stderr, "failed to open xen control interface.\n"); + goto out; + } + + rc = xc_version(xc, XENVER_capabilities, &capabilities[0]); + if ( rc == -1 ) { + fprintf(stderr, "failed to make Xen version hypercall.\n"); + goto out_close; + } + + if (strstr(capabilities, "xen-3.0-x86_64")) + machine = EM_X86_64; + else if (strstr(capabilities, "xen-3.0-x86_32")) + machine = EM_386; + + out_close: + xc_interface_close(xc); + + out: +#endif + return machine; +} + +#ifdef HAVE_LIBXENCTRL +int get_xen_vmcoreinfo(uint64_t *addr, uint64_t *len) +{ + uint64_t end; + int ret = 0; + + ret = xen_get_kexec_range(KEXEC_RANGE_MA_VMCOREINFO, addr, &end); + if (ret < 0) + return -1; + + *len = end - *addr + 1; + + return 0; +} + +int xen_get_nr_phys_cpus(void) +{ + xc_interface *xc; + int max_cpus; + int cpu = -1; + + if (xen_phys_cpus) + return xen_phys_cpus; + + xc = xc_interface_open(NULL, NULL, 0); + if (!xc) { + fprintf(stderr, "failed to open xen control interface.\n"); + return -1; + } + + max_cpus = xc_get_max_cpus(xc); + if (max_cpus <= 0) + goto out; + + xen_phys_notes = calloc(max_cpus, sizeof(*xen_phys_notes)); + if (xen_phys_notes == NULL) + goto out; + + for (cpu = 0; cpu < max_cpus; cpu++) { + uint64_t size, start; + int ret; + + ret = xc_kexec_get_range(xc, KEXEC_RANGE_MA_CPU, cpu, &size, &start); + if (ret < 0) + break; + + xen_phys_notes[cpu].base = start; + xen_phys_notes[cpu].length = size; + } + + xen_phys_cpus = cpu; + +out: + xc_interface_close(xc); + return cpu; +} +#else +int get_xen_vmcoreinfo(uint64_t *addr, uint64_t *len) +{ + return -1; +} + +int xen_get_nr_phys_cpus(void) +{ + return -1; +} +#endif + + +int xen_get_note(int cpu, uint64_t *addr, uint64_t *len) +{ + struct crash_note_info *note; + + if (xen_phys_cpus <= 0) + return -1; + + note = xen_phys_notes + cpu; + + *addr = note->base; + *len = note->length; + + return 0; +} + +#ifdef HAVE_LIBXENCTRL +int xen_get_crashkernel_region(uint64_t *start, uint64_t *end) +{ + return xen_get_kexec_range(KEXEC_RANGE_MA_CRASH, start, end); +} +#else +int xen_get_crashkernel_region(uint64_t *start, uint64_t *end) +{ + return -1; +} +#endif diff --git a/kexec/crashdump.c b/kexec/crashdump.c new file mode 100644 index 0000000..0b363c5 --- /dev/null +++ b/kexec/crashdump.c @@ -0,0 +1,159 @@ +/* + * crashdump.c: Architecture independent code for crashdump support. + * + * Created by: Vivek Goyal (vgoyal@in.ibm.com) + * Copyright (C) IBM Corporation, 2005. All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation (version 2 of the License). + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <limits.h> +#include <linux/limits.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <elf.h> +#include "kexec.h" +#include "crashdump.h" +#include "kexec-syscall.h" + +/* include "crashdump-elf.c" twice to create two functions from one */ + +#define ELF_WIDTH 64 +#define FUNC crash_create_elf64_headers +#define EHDR Elf64_Ehdr +#define PHDR Elf64_Phdr +#include "crashdump-elf.c" +#undef ELF_WIDTH +#undef PHDR +#undef EHDR +#undef FUNC + +#define ELF_WIDTH 32 +#define FUNC crash_create_elf32_headers +#define EHDR Elf32_Ehdr +#define PHDR Elf32_Phdr +#include "crashdump-elf.c" +#undef ELF_WIDTH +#undef PHDR +#undef EHDR +#undef FUNC + +unsigned long crash_architecture(struct crash_elf_info *elf_info) +{ + if (xen_present()) + return xen_architecture(elf_info); + else + return elf_info->machine; +} + +/* Returns the physical address of start of crash notes buffer for a cpu. */ +int get_crash_notes_per_cpu(int cpu, uint64_t *addr, uint64_t *len) +{ + char crash_notes[PATH_MAX]; + char crash_notes_size[PATH_MAX]; + char line[MAX_LINE]; + FILE *fp; + struct stat cpu_stat; + int count; + unsigned long long temp; + int fopen_errno; + int stat_errno; + + *addr = 0; + *len = 0; + + sprintf(crash_notes, "/sys/devices/system/cpu/cpu%d/crash_notes", cpu); + fp = fopen(crash_notes, "r"); + if (!fp) { + fopen_errno = errno; + if (fopen_errno != ENOENT) + die("Could not open \"%s\": %s\n", crash_notes, + strerror(fopen_errno)); + if (stat("/sys/devices", &cpu_stat)) { + stat_errno = errno; + if (stat_errno == ENOENT) + die("\"/sys/devices\" does not exist. " + "Sysfs does not seem to be mounted. " + "Try mounting sysfs.\n"); + die("Could not open \"/sys/devices\": %s\n", + strerror(stat_errno)); + } + /* CPU is not physically present.*/ + return -1; + } + if (!fgets(line, sizeof(line), fp)) + die("Cannot parse %s: %s\n", crash_notes, strerror(errno)); + count = sscanf(line, "%llx", &temp); + if (count != 1) + die("Cannot parse %s: %s\n", crash_notes, strerror(errno)); + *addr = (uint64_t) temp; + fclose(fp); + + *len = MAX_NOTE_BYTES; + sprintf(crash_notes_size, + "/sys/devices/system/cpu/cpu%d/crash_notes_size", cpu); + fp = fopen(crash_notes_size, "r"); + if (fp) { + if (!fgets(line, sizeof(line), fp)) + die("Cannot parse %s: %s\n", + crash_notes_size, strerror(errno)); + count = sscanf(line, "%llu", &temp); + if (count != 1) + die("Cannot parse %s: %s\n", + crash_notes_size, strerror(errno)); + *len = (uint64_t) temp; + fclose(fp); + } + + dbgprintf("%s: crash_notes addr = %llx, size = %llu\n", __FUNCTION__, + (unsigned long long)*addr, (unsigned long long)*len); + + return 0; +} + +static int get_vmcoreinfo(const char *kdump_info, uint64_t *addr, uint64_t *len) +{ + char line[MAX_LINE]; + int count; + FILE *fp; + unsigned long long temp, temp2; + + *addr = 0; + *len = 0; + + if (!(fp = fopen(kdump_info, "r"))) + return -1; + + if (!fgets(line, sizeof(line), fp)) + die("Cannot parse %s: %s\n", kdump_info, strerror(errno)); + count = sscanf(line, "%llx %llx", &temp, &temp2); + if (count != 2) + die("Cannot parse %s: %s\n", kdump_info, strerror(errno)); + + *addr = (uint64_t) temp; + *len = (uint64_t) temp2; + + fclose(fp); + return 0; +} + +/* Returns the physical address of start of crash notes buffer for a kernel. */ +int get_kernel_vmcoreinfo(uint64_t *addr, uint64_t *len) +{ + return get_vmcoreinfo("/sys/kernel/vmcoreinfo", addr, len); +} diff --git a/kexec/crashdump.h b/kexec/crashdump.h new file mode 100644 index 0000000..18bd691 --- /dev/null +++ b/kexec/crashdump.h @@ -0,0 +1,64 @@ +#ifndef CRASHDUMP_H +#define CRASHDUMP_H + +extern int get_crash_notes_per_cpu(int cpu, uint64_t *addr, uint64_t *len); +extern int get_kernel_vmcoreinfo(uint64_t *addr, uint64_t *len); +extern int get_xen_vmcoreinfo(uint64_t *addr, uint64_t *len); + +/* Need to find a better way to determine per cpu notes section size. */ +#define MAX_NOTE_BYTES 1024 +/* Expecting ELF headers to fit in 64K. Increase it if you need more. */ +#define KCORE_ELF_HEADERS_SIZE 65536 +/* The address of the ELF header is passed to the secondary kernel + * using the kernel command line option memmap=nnn. + * The smallest unit the kernel accepts is in kilobytes, + * so we need to make sure the ELF header is aligned to 1024. + */ +#define ELF_CORE_HEADER_ALIGN 1024 + +/* structure passed to crash_create_elf32/64_headers() */ + +struct crash_elf_info { + unsigned long class; + unsigned long data; + unsigned long machine; + + unsigned long long page_offset; + unsigned long long kern_vaddr_start; + unsigned long long kern_paddr_start; + unsigned long kern_size; + unsigned long lowmem_limit; + + int (*get_note_info)(int cpu, uint64_t *addr, uint64_t *len); +}; + +typedef int(*crash_create_elf_headers_func)(struct kexec_info *info, + struct crash_elf_info *elf_info, + struct memory_range *range, + int ranges, + void **buf, unsigned long *size, + unsigned long align); + +int crash_create_elf32_headers(struct kexec_info *info, + struct crash_elf_info *elf_info, + struct memory_range *range, int ranges, + void **buf, unsigned long *size, + unsigned long align); + +int crash_create_elf64_headers(struct kexec_info *info, + struct crash_elf_info *elf_info, + struct memory_range *range, int ranges, + void **buf, unsigned long *size, + unsigned long align); + +unsigned long crash_architecture(struct crash_elf_info *elf_info); + +unsigned long phys_to_virt(struct crash_elf_info *elf_info, + unsigned long long paddr); + +unsigned long xen_architecture(struct crash_elf_info *elf_info); +int xen_get_nr_phys_cpus(void); +int xen_get_note(int cpu, uint64_t *addr, uint64_t *len); +int xen_get_crashkernel_region(uint64_t *start, uint64_t *end); + +#endif /* CRASHDUMP_H */ diff --git a/kexec/dt-ops.c b/kexec/dt-ops.c new file mode 100644 index 0000000..0a96b75 --- /dev/null +++ b/kexec/dt-ops.c @@ -0,0 +1,176 @@ +#include <assert.h> +#include <errno.h> +#include <inttypes.h> +#include <libfdt.h> +#include <stdio.h> +#include <stdlib.h> + +#include "kexec.h" +#include "dt-ops.h" + +static const char n_chosen[] = "chosen"; + +static const char p_bootargs[] = "bootargs"; +static const char p_initrd_start[] = "linux,initrd-start"; +static const char p_initrd_end[] = "linux,initrd-end"; + +int dtb_set_initrd(char **dtb, off_t *dtb_size, off_t start, off_t end) +{ + int result; + uint64_t value; + + dbgprintf("%s: start %jd, end %jd, size %jd (%jd KiB)\n", + __func__, (intmax_t)start, (intmax_t)end, + (intmax_t)(end - start), + (intmax_t)(end - start) / 1024); + + value = cpu_to_fdt64(start); + + result = dtb_set_property(dtb, dtb_size, n_chosen, p_initrd_start, + &value, sizeof(value)); + + if (result) + return result; + + value = cpu_to_fdt64(end); + + result = dtb_set_property(dtb, dtb_size, n_chosen, p_initrd_end, + &value, sizeof(value)); + + if (result) { + dtb_delete_property(*dtb, n_chosen, p_initrd_start); + return result; + } + + return 0; +} + +void dtb_clear_initrd(char **dtb, off_t *dtb_size) +{ + dtb_delete_property(*dtb, n_chosen, p_initrd_start); + dtb_delete_property(*dtb, n_chosen, p_initrd_end); +} + +int dtb_set_bootargs(char **dtb, off_t *dtb_size, const char *command_line) +{ + return dtb_set_property(dtb, dtb_size, n_chosen, p_bootargs, + command_line, strlen(command_line) + 1); +} + +int dtb_set_property(char **dtb, off_t *dtb_size, const char *node, + const char *prop, const void *value, int value_len) +{ + int result; + int nodeoffset; + void *new_dtb; + int new_size; + char *new_node = NULL; + + value_len = FDT_TAGALIGN(value_len); + + new_size = FDT_TAGALIGN(*dtb_size + fdt_node_len(node) + + fdt_prop_len(prop, value_len)); + + new_dtb = malloc(new_size); + + if (!new_dtb) { + dbgprintf("%s: malloc failed\n", __func__); + return -ENOMEM; + } + + result = fdt_open_into(*dtb, new_dtb, new_size); + + if (result) { + dbgprintf("%s: fdt_open_into failed: %s\n", __func__, + fdt_strerror(result)); + goto on_error; + } + + new_node = malloc(strlen("/") + strlen(node) + 1); + if (!new_node) { + dbgprintf("%s: malloc failed\n", __func__); + result = -ENOMEM; + goto on_error; + } + + strcpy(new_node, "/"); + strcat(new_node, node); + + nodeoffset = fdt_path_offset(new_dtb, new_node); + + if (nodeoffset == -FDT_ERR_NOTFOUND) { + result = fdt_add_subnode(new_dtb, 0, node); + + if (result < 0) { + dbgprintf("%s: fdt_add_subnode failed: %s\n", __func__, + fdt_strerror(result)); + goto on_error; + } + nodeoffset = result; + } else if (nodeoffset < 0) { + dbgprintf("%s: fdt_path_offset failed: %s\n", __func__, + fdt_strerror(nodeoffset)); + goto on_error; + } + + result = fdt_setprop(new_dtb, nodeoffset, prop, value, value_len); + + if (result) { + dbgprintf("%s: fdt_setprop failed: %s\n", __func__, + fdt_strerror(result)); + goto on_error; + } + + /* + * Can't call free on dtb since dtb may have been mmaped by + * slurp_file(). + */ + + result = fdt_pack(new_dtb); + + if (result) + dbgprintf("%s: Unable to pack device tree: %s\n", __func__, + fdt_strerror(result)); + + *dtb = new_dtb; + *dtb_size = fdt_totalsize(*dtb); + + return 0; + +on_error: + free(new_dtb); + free(new_node); + return result; +} + +int dtb_delete_property(char *dtb, const char *node, const char *prop) +{ + int result, nodeoffset; + char *new_node = NULL; + + new_node = malloc(strlen("/") + strlen(node) + 1); + if (!new_node) { + dbgprintf("%s: malloc failed\n", __func__); + return -ENOMEM; + } + + strcpy(new_node, "/"); + strcat(new_node, node); + + nodeoffset = fdt_path_offset(dtb, new_node); + if (nodeoffset < 0) { + dbgprintf("%s: fdt_path_offset failed: %s\n", __func__, + fdt_strerror(nodeoffset)); + free(new_node); + return nodeoffset; + } + + result = fdt_delprop(dtb, nodeoffset, prop); + + if (result) + dbgprintf("%s: fdt_delprop failed: %s\n", __func__, + fdt_strerror(nodeoffset)); + + free(new_node); + return result; +} diff --git a/kexec/dt-ops.h b/kexec/dt-ops.h new file mode 100644 index 0000000..03659ce --- /dev/null +++ b/kexec/dt-ops.h @@ -0,0 +1,14 @@ +#if !defined(KEXEC_DT_OPS_H) +#define KEXEC_DT_OPS_H + +#include <sys/types.h> + +int dtb_set_initrd(char **dtb, off_t *dtb_size, off_t start, off_t end); +void dtb_clear_initrd(char **dtb, off_t *dtb_size); +int dtb_set_bootargs(char **dtb, off_t *dtb_size, const char *command_line); +int dtb_set_property(char **dtb, off_t *dtb_size, const char *node, + const char *prop, const void *value, int value_len); + +int dtb_delete_property(char *dtb, const char *node, const char *prop); + +#endif diff --git a/kexec/firmware_memmap.c b/kexec/firmware_memmap.c new file mode 100644 index 0000000..457c3dc --- /dev/null +++ b/kexec/firmware_memmap.c @@ -0,0 +1,302 @@ +/* + * firmware_memmap.c: Read /sys/firmware/memmap + * + * Created by: Bernhard Walle (bernhard.walle@gmx.de) + * Copyright (C) SUSE LINUX Products GmbH, 2008. All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation (version 2 of the License). + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#define _GNU_SOURCE /* for ULLONG_MAX without C99 */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <dirent.h> +#include <unistd.h> +#include <limits.h> +#include <errno.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include "firmware_memmap.h" +#include "kexec.h" + +/* + * If the system is too old for ULLONG_MAX or LLONG_MAX, define it here. + */ +#ifndef ULLONG_MAX +# define ULLONG_MAX (~0ULL) +#endif /* ULLONG_MAX */ + +#ifndef LLONG_MAX +# define LLONG_MAX (~0ULL >> 1) +#endif /* LLONG_MAX */ + + +/** + * The full path to the sysfs interface that provides the memory map. + */ +#define FIRMWARE_MEMMAP_DIR "/sys/firmware/memmap" + +/** + * Parses a file that only contains one number. Typical for sysfs files. + * + * @param[in] filename the name of the file that should be parsed + * @return the value that has been read or ULLONG_MAX on error. + */ +static unsigned long long parse_numeric_sysfs(const char *filename) +{ + FILE *fp; + char linebuffer[BUFSIZ]; + unsigned long long retval = ULLONG_MAX; + + fp = fopen(filename, "r"); + if (!fp) { + fprintf(stderr, "Opening \"%s\" failed: %s\n", + filename, strerror(errno)); + return ULLONG_MAX; + } + + if (!fgets(linebuffer, BUFSIZ, fp)) + goto err; + + linebuffer[BUFSIZ-1] = 0; + + /* let strtoll() detect the base */ + retval = strtoll(linebuffer, NULL, 0); + +err: + fclose(fp); + + return retval; +} + +/** + * Reads the contents of a one-line sysfs file to buffer. (This function is + * not threadsafe.) + * + * @param[in] filename the name of the file that should be read + * + * @return NULL on failure, a pointer to a static buffer (that should be copied + * with strdup() if the caller plans to use it after next function call) + */ +static char *parse_string_sysfs(const char *filename) +{ + FILE *fp; + static char linebuffer[BUFSIZ]; + char *end; + + fp = fopen(filename, "r"); + if (!fp) { + fprintf(stderr, "Opening \"%s\" failed: %s\n", + filename, strerror(errno)); + return NULL; + } + + if (!fgets(linebuffer, BUFSIZ, fp)) { + fclose(fp); + return NULL; + } + + linebuffer[BUFSIZ-1] = 0; + + /* truncate trailing newline(s) */ + end = linebuffer + strlen(linebuffer) - 1; + while (*end == '\n') + *end-- = 0; + + fclose(fp); + + return linebuffer; + +} + +static int parse_memmap_entry(const char *entry, struct memory_range *range) +{ + char filename[PATH_MAX]; + char *type; + int ret; + + /* + * entry/start + */ + ret = snprintf(filename, PATH_MAX, "%s/%s", entry, "start"); + if (ret < 0 || ret >= PATH_MAX) { + fprintf(stderr, "snprintf failed: %s\n", strerror(errno)); + return -1; + } + + filename[PATH_MAX-1] = 0; + + range->start = parse_numeric_sysfs(filename); + if (range->start == ULLONG_MAX) + return -1; + + /* + * entry/end + */ + ret = snprintf(filename, PATH_MAX, "%s/%s", entry, "end"); + if (ret < 0 || ret >= PATH_MAX) { + fprintf(stderr, "snprintf failed: %s\n", strerror(errno)); + return -1; + } + + filename[PATH_MAX-1] = 0; + + range->end = parse_numeric_sysfs(filename); + if (range->end == ULLONG_MAX) + return -1; + + /* + * entry/type + */ + ret = snprintf(filename, PATH_MAX, "%s/%s", entry, "type"); + if (ret < 0 || ret >= PATH_MAX) { + fprintf(stderr, "snprintf failed: %s\n", strerror(errno)); + return -1; + } + + filename[PATH_MAX-1] = 0; + + type = parse_string_sysfs(filename); + if (!type) + return -1; + + if (strcmp(type, "System RAM") == 0) + range->type = RANGE_RAM; + else if (strcmp(type, "ACPI Tables") == 0) + range->type = RANGE_ACPI; + else if (strcmp(type, "Unusable memory") == 0) + range->type = RANGE_RESERVED; + else if (strcmp(type, "reserved") == 0) + range->type = RANGE_RESERVED; + else if (strcmp(type, "Reserved") == 0) + range->type = RANGE_RESERVED; + else if (strcmp(type, "Unknown E820 type") == 0) + range->type = RANGE_RESERVED; + else if (strcmp(type, "ACPI Non-volatile Storage") == 0) + range->type = RANGE_ACPI_NVS; + else if (strcmp(type, "Uncached RAM") == 0) + range->type = RANGE_UNCACHED; + else if (strcmp(type, "Persistent Memory (legacy)") == 0) + range->type = RANGE_PRAM; + else if (strcmp(type, "Persistent Memory") == 0) + range->type = RANGE_PMEM; + else { + fprintf(stderr, "Unknown type (%s) while parsing %s. Please " + "report this as bug. Using RANGE_RESERVED now.\n", + type, filename); + range->type = RANGE_RESERVED; + } + + return 0; +} + +/* documentation: firmware_memmap.h */ +int compare_ranges(const void *first, const void *second) +{ + const struct memory_range *first_range = first; + const struct memory_range *second_range = second; + + /* + * don't use the "first_range->start - second_range->start" + * notation because unsigned long long might overflow + */ + if (first_range->start > second_range->start) + return 1; + else if (first_range->start < second_range->start) + return -1; + else /* first_range->start == second_range->start */ + return 0; +} + +/* documentation: firmware_memmap.h */ +int have_sys_firmware_memmap(void) +{ + int ret; + struct stat mystat; + + ret = stat(FIRMWARE_MEMMAP_DIR, &mystat); + if (ret != 0) + return 0; + + return S_ISDIR(mystat.st_mode); +} + +/* documentation: firmware_memmap.h */ +int get_firmware_memmap_ranges(struct memory_range *range, size_t *ranges) +{ + DIR *firmware_memmap_dir = NULL; + struct dirent *dirent; + int i = 0; + + /* argument checking */ + if (!range || !ranges) { + fprintf(stderr, "%s: Invalid arguments.\n", __FUNCTION__); + return -1; + } + + /* open the directory */ + firmware_memmap_dir = opendir(FIRMWARE_MEMMAP_DIR); + if (!firmware_memmap_dir) { + perror("Could not open \"" FIRMWARE_MEMMAP_DIR "\""); + goto error; + } + + /* parse the entries */ + while ((dirent = readdir(firmware_memmap_dir)) != NULL) { + int ret; + char full_path[PATH_MAX]; + + /* array overflow check */ + if ((size_t)i >= *ranges) { + fprintf(stderr, "The firmware provides more entries " + "allowed (%zd). Please report that as bug.\n", + *ranges); + goto error; + } + + /* exclude '.' and '..' */ + if (dirent->d_name[0] && dirent->d_name[0] == '.') { + continue; + } + + snprintf(full_path, PATH_MAX, "%s/%s", FIRMWARE_MEMMAP_DIR, + dirent->d_name); + full_path[PATH_MAX-1] = 0; + ret = parse_memmap_entry(full_path, &range[i]); + if (ret < 0) { + goto error; + } + + i++; + } + + /* close the dir as we don't need it any more */ + closedir(firmware_memmap_dir); + + /* update the number of ranges for the caller */ + *ranges = i; + + /* and finally sort the entries with qsort */ + qsort(range, *ranges, sizeof(struct memory_range), compare_ranges); + + return 0; + +error: + if (firmware_memmap_dir) { + closedir(firmware_memmap_dir); + } + return -1; +} + diff --git a/kexec/firmware_memmap.h b/kexec/firmware_memmap.h new file mode 100644 index 0000000..eac0ddb --- /dev/null +++ b/kexec/firmware_memmap.h @@ -0,0 +1,76 @@ +/* + * firmware_memmap.c: Read /sys/firmware/memmap + * + * Created by: Bernhard Walle (bernhard.walle@gmx.de) + * Copyright (C) SUSE LINUX Products GmbH, 2008. All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation (version 2 of the License). + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#ifndef FIRMWARE_MEMMAP_H +#define FIRMWARE_MEMMAP_H + +#include "kexec.h" + +/** + * Reads the /sys/firmware/memmap interface, documented in + * Documentation/ABI/testing/sysfs-firmware-memmap (kernel tree). + * + * The difference between /proc/iomem and /sys/firmware/memmap is that + * /sys/firmware/memmap provides the raw memory map, provided by the + * firmware of the system. That memory map should be passed to a kexec'd + * kernel because the behaviour should be the same as a normal booted kernel, + * so any limitation (e.g. by the user providing the mem command line option) + * should not be passed to the kexec'd kernel. + * + * The parsing of the code is independent of the architecture. However, the + * actual architecture-specific code might postprocess the code a bit, like + * x86 does. + */ + +/** + * Compares two memory ranges according to their start address. This function + * can be used with qsort() as @c compar function. + * + * @param[in] first a pointer to the first memory range + * @param[in] second a pointer to the second memory range + * @return 0 if @p first and @p second have the same start address, + * a value less then 0 if the start address of @p first is less than + * the start address of @p second, and a value greater than 0 if + * the opposite is in case. + */ +int compare_ranges(const void *first, const void *second); + +/** + * Checks if the kernel provides the /sys/firmware/memmap interface. + * It makes sense to use that function in advance before calling + * get_firmware_memmap_ranges() because the latter function prints an error + * if it cannot open the directory. If have_sys_firmware_memmap() returns + * false, then one can use the old /proc/iomem interface (for older kernels). + */ +int have_sys_firmware_memmap(void); + +/** + * Parses the /sys/firmware/memmap memory map. + * + * @param[out] range a pointer to an array of type struct memory_range with + * at least *range entries + * @param[in,out] ranges a pointer to an integer that holds the number of + * entries which range contains (at least). After successful + * return, the number of actual entries will be written. + * @return 0 on success, -1 on failure. + */ +int get_firmware_memmap_ranges(struct memory_range *range, size_t *ranges); + + +#endif /* FIRMWARE_MEMMAP_H */ diff --git a/kexec/fs2dt.c b/kexec/fs2dt.c new file mode 100644 index 0000000..993b73b --- /dev/null +++ b/kexec/fs2dt.c @@ -0,0 +1,852 @@ +/* + * fs2dt: creates a flattened device-tree + * + * Copyright (C) 2004,2005 Milton D Miller II, IBM Corporation + * Copyright (C) 2005 R Sharada (sharada@in.ibm.com), IBM Corporation + * + * 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 (version 2 of the License). + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#define _GNU_SOURCE + +#include <sys/types.h> +#include <sys/stat.h> + +#include <fcntl.h> +#include <dirent.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <stdio.h> +#include "kexec.h" +#include "fs2dt.h" +#include "libfdt/fdt.h" + +#define MAXPATH 1024 /* max path name length */ +#define NAMESPACE 16384 /* max bytes for property names */ +#define INIT_TREE_WORDS 65536 /* Initial num words for prop values */ +#define MEMRESERVE 256 /* max number of reserved memory blocks */ +#define MEM_RANGE_CHUNK_SZ 2048 /* Initial num dwords for mem ranges */ + +static char pathname[MAXPATH]; +static char propnames[NAMESPACE] = { 0 }; +static unsigned *dt_base, *dt; +static unsigned int dt_cur_size; +static unsigned long long mem_rsrv[2*MEMRESERVE] = { 0ULL, 0ULL }; + +static int crash_param = 0; +static char local_cmdline[COMMAND_LINE_SIZE] = { "" }; + +extern unsigned char reuse_initrd; + +/* Used for enabling printing message from purgatory code + * Only has implemented for PPC64 */ +int my_debug; +int dt_no_old_root; + +/* This provides the behaviour of hte existing ppc64 implementation */ +static void pad_structure_block(size_t len) { +#ifdef NEED_STRUCTURE_BLOCK_EXTRA_PAD + if ((len >= 8) && ((unsigned long)dt & 0x4)) + dt++; +#endif +} + +/* Before we add something to the dt, reserve N words using this. + * If there isn't enough room, it's realloced -- and you don't overflow and + * splat bits of your heap. + */ +static void dt_reserve(unsigned **dt_ptr, unsigned words) +{ + unsigned int sz = INIT_TREE_WORDS; + + if (sz < words) + sz = words; + + if (((*dt_ptr - dt_base) + words) >= dt_cur_size) { + int offset; + unsigned int new_size = dt_cur_size + sz; + unsigned *new_dt = realloc(dt_base, new_size*4); + + if (!new_dt) + die("unrecoverable error: Can't realloc %d bytes for " + "device tree\n", new_size*4); + offset = *dt_ptr - dt_base; + dt_base = new_dt; + dt_cur_size = new_size; + *dt_ptr = dt_base + offset; + memset(*dt_ptr, 0, (new_size - offset)*4); + } +} + +void reserve(unsigned long long where, unsigned long long length) +{ + size_t offset; + + for (offset = 0; be64_to_cpu(mem_rsrv[offset + 1]); offset += 2) + ; + + if (offset + 4 >= 2 * MEMRESERVE) + die("unrecoverable error: exhasuted reservation meta data\n"); + + mem_rsrv[offset] = cpu_to_be64(where); + mem_rsrv[offset + 1] = cpu_to_be64(length); + mem_rsrv[offset + 2] = mem_rsrv[offset + 3] = cpu_to_be64(0); +} + +/* look for properties we need to reserve memory space for */ +static void checkprop(char *name, unsigned *data, int len) +{ + static unsigned long long base, size, end; + + if ((data == NULL) && (base || size || end)) + die("unrecoverable error: no property data"); + else if (!strcmp(name, "linux,rtas-base")) + base = be32_to_cpu(*data); + else if (!strcmp(name, "opal-base-address")) + base = be64_to_cpu(*(unsigned long long *)data); + else if (!strcmp(name, "opal-runtime-size")) + size = be64_to_cpu(*(unsigned long long *)data); + else if (!strcmp(name, "linux,tce-base")) + base = be64_to_cpu(*(unsigned long long *) data); + else if (!strcmp(name, "rtas-size") || + !strcmp(name, "linux,tce-size")) + size = be32_to_cpu(*data); + else if (reuse_initrd && !strcmp(name, "linux,initrd-start")) { + if (len == 8) + base = be64_to_cpu(*(unsigned long long *) data); + else + base = be32_to_cpu(*data); + } else if (reuse_initrd && !strcmp(name, "linux,initrd-end")) { + if (len == 8) + end = be64_to_cpu(*(unsigned long long *) data); + else + end = be32_to_cpu(*data); + } + + if (size && end) + die("unrecoverable error: size and end set at same time\n"); + if (base && size) { + reserve(base, size); + base = size = 0; + } + if (base && end) { + reserve(base, end-base); + base = end = 0; + } +} + +/* + * return the property index for a property name, creating a new one + * if needed. + */ +static unsigned propnum(const char *name) +{ + unsigned offset = 0; + + while(propnames[offset]) + if (strcmp(name, propnames+offset)) + offset += strlen(propnames+offset)+1; + else + return offset; + + if (NAMESPACE - offset < strlen(name) + 1) + die("unrecoverable error: propnames overrun\n"); + + strcpy(propnames+offset, name); + + return offset; +} + +/* + * Add ranges by comparing 'base' and 'end' addresses with usable + * memory ranges. Returns the number of ranges added. Each range added + * increments 'idx' by 2. + */ +static uint64_t add_ranges(uint64_t **ranges, int *ranges_size, int idx, + uint64_t base, uint64_t end) +{ + uint64_t loc_base, loc_end, rngs_cnt = 0; + size_t range; + int add = 0; + + for (range = 0; range < usablemem_rgns.size; range++) { + loc_base = usablemem_rgns.ranges[range].start; + loc_end = usablemem_rgns.ranges[range].end; + if (loc_base >= base && loc_end <= end) { + add = 1; + } else if (base < loc_end && end > loc_base) { + if (loc_base < base) + loc_base = base; + if (loc_end > end) + loc_end = end; + add = 1; + } + + if (add) { + if (idx >= ((*ranges_size) - 2)) { + (*ranges_size) += MEM_RANGE_CHUNK_SZ; + *ranges = realloc(*ranges, (*ranges_size)*8); + if (!(*ranges)) + die("unrecoverable error: can't realloc" + "%d bytes for ranges.\n", + (*ranges_size)*8); + } + (*ranges)[idx++] = cpu_to_be64(loc_base); + (*ranges)[idx++] = cpu_to_be64(loc_end - loc_base); + + rngs_cnt++; + } + } + + return rngs_cnt; +} + +#ifdef HAVE_DYNAMIC_MEMORY +static void add_dyn_reconf_usable_mem_property__(int fd) +{ + char fname[MAXPATH], *bname; + char buf[32]; + uint32_t lmbs_in_set = 1; + uint64_t *ranges; + int ranges_size = MEM_RANGE_CHUNK_SZ; + uint64_t base, end, rngs_cnt; + size_t i, j; + int rlen = 0; + int tmp_indx; + + strcpy(fname, pathname); + bname = strrchr(fname, '/'); + bname[0] = '\0'; + bname = strrchr(fname, '/'); + if (strncmp(bname, "/ibm,dynamic-reconfiguration-memory", 36)) + return; + + if (lseek(fd, 4, SEEK_SET) < 0) + die("unrecoverable error: error seeking in \"%s\": %s\n", + pathname, strerror(errno)); + + ranges = malloc(ranges_size*8); + if (!ranges) + die("unrecoverable error: can't alloc %d bytes for ranges.\n", + ranges_size*8); + + rlen = 0; + for (i = 0; i < num_of_lmb_sets; i++) { + if (read(fd, buf, LMB_ENTRY_SIZE) < 0) + die("unrecoverable error: error reading \"%s\": %s\n", + pathname, strerror(errno)); + + /* + * If the property is ibm,dynamic-memory-v2, the first 4 bytes + * tell the number of sequential LMBs in this entry. Else, if + * the property is ibm,dynamic-memory, each entry represents + * one LMB. Make sure to add an entry for each LMB as kernel + * looks for a counter for every LMB. + */ + if (is_dyn_mem_v2) + lmbs_in_set = be32_to_cpu(((unsigned int *)buf)[0]); + + base = be64_to_cpu(*((uint64_t *)&buf[DRCONF_ADDR])); + for (j = 0; j < lmbs_in_set; j++) { + end = base + lmb_size; + if (~0ULL - base < end) { + die("unrecoverable error: mem property" + " overflow\n"); + } + + tmp_indx = rlen++; + + rngs_cnt = add_ranges(&ranges, &ranges_size, rlen, + base, end); + if (rngs_cnt == 0) { + /* We still need to add a counter for every LMB + * because the kernel parsing code is dumb. We + * just have a zero in this case, with no + * following base/len. + */ + ranges[tmp_indx] = 0; + + /* rlen is already just tmp_indx+1 as we didn't + * write anything. Check array size here, as we + * will probably go on writing zeros for a while + */ + if (rlen >= (ranges_size-1)) { + ranges_size += MEM_RANGE_CHUNK_SZ; + ranges = realloc(ranges, ranges_size*8); + if (!ranges) + die("unrecoverable error: can't" + " realloc %d bytes for" + " ranges.\n", + ranges_size*8); + } + } else { + /* Store the count of (base, size) duple */ + ranges[tmp_indx] = cpu_to_be64(rngs_cnt); + rlen += rngs_cnt * 2; + } + + base = end; + } + } + + rlen = rlen * sizeof(uint64_t); + /* + * Add linux,drconf-usable-memory property. + */ + dt_reserve(&dt, 4+((rlen + 3)/4)); + *dt++ = cpu_to_be32(FDT_PROP); + *dt++ = cpu_to_be32(rlen); + *dt++ = cpu_to_be32(propnum("linux,drconf-usable-memory")); + pad_structure_block(rlen); + memcpy(dt, ranges, rlen); + free(ranges); + dt += (rlen + 3)/4; +} + +static void add_dyn_reconf_usable_mem_property(struct dirent *dp, int fd) +{ + if ((!strcmp(dp->d_name, "ibm,dynamic-memory-v2") || + !strcmp(dp->d_name, "ibm,dynamic-memory")) && usablemem_rgns.size) + add_dyn_reconf_usable_mem_property__(fd); +} +#else +static void add_dyn_reconf_usable_mem_property(struct dirent *dp, int fd) {} +#endif + +static void add_usable_mem_property(int fd, size_t len) +{ + char fname[MAXPATH], *bname; + uint64_t buf[2]; + uint64_t *ranges; + int ranges_size = MEM_RANGE_CHUNK_SZ; + uint64_t base, end, rngs_cnt; + int rlen = 0; + + strcpy(fname, pathname); + bname = strrchr(fname,'/'); + bname[0] = '\0'; + bname = strrchr(fname,'/'); + if (strncmp(bname, "/memory@", 8) && strcmp(bname, "/memory")) + return; + + if (len < sizeof(buf)) + die("unrecoverable error: not enough data for mem property\n"); + + if (lseek(fd, 0, SEEK_SET) < 0) + die("unrecoverable error: error seeking in \"%s\": %s\n", + pathname, strerror(errno)); + if (read(fd, buf, sizeof(buf)) != sizeof(buf)) + die("unrecoverable error: error reading \"%s\": %s\n", + pathname, strerror(errno)); + + base = be64_to_cpu(buf[0]); + end = be64_to_cpu(buf[1]); + if (~0ULL - base < end) + die("unrecoverable error: mem property overflow\n"); + end += base; + + ranges = malloc(ranges_size * sizeof(*ranges)); + if (!ranges) + die("unrecoverable error: can't alloc %zu bytes for ranges.\n", + ranges_size * sizeof(*ranges)); + + rngs_cnt = add_ranges(&ranges, &ranges_size, rlen, base, end); + rlen += rngs_cnt * 2; + + if (!rlen) { + /* + * User did not pass any ranges for thsi region. Hence, write + * (0,0) duple in linux,usable-memory property such that + * this region will be ignored. + */ + ranges[rlen++] = 0; + ranges[rlen++] = 0; + } + + rlen = rlen * sizeof(*ranges); + /* + * No add linux,usable-memory property. + */ + dt_reserve(&dt, 4+((rlen + 3)/4)); + *dt++ = cpu_to_be32(FDT_PROP); + *dt++ = cpu_to_be32(rlen); + *dt++ = cpu_to_be32(propnum("linux,usable-memory")); + pad_structure_block(rlen); + memcpy(dt, ranges, rlen); + free(ranges); + dt += (rlen + 3)/4; +} + +/* put all properties (files) in the property structure */ +static void putprops(char *fn, struct dirent **nlist, int numlist) +{ + struct dirent *dp; + int i = 0, fd; + off_t len; + off_t slen; + struct stat statbuf; + + for (i = 0; i < numlist; i++) { + dp = nlist[i]; + strcpy(fn, dp->d_name); + + if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) + continue; + + /* Empirically, this seems to need to be ecluded. + * Observed on ARM with 3.6-rc2 kernel + */ + if (!strcmp(dp->d_name, "name")) + continue; + + if (lstat(pathname, &statbuf)) + die("unrecoverable error: could not stat \"%s\": %s\n", + pathname, strerror(errno)); + + if (!crash_param && !strcmp(fn,"linux,crashkernel-base")) + continue; + + if (!crash_param && !strcmp(fn,"linux,crashkernel-size")) + continue; + + /* + * This property will be created for each node during kexec + * boot. So, ignore it. + */ + if (!strcmp(dp->d_name, "linux,pci-domain") || + !strcmp(dp->d_name, "linux,htab-base") || + !strcmp(dp->d_name, "linux,htab-size") || + !strcmp(dp->d_name, "linux,kernel-end")) + continue; + + /* This property will be created/modified later in putnode() + * So ignore it, unless we are reusing the initrd. + */ + if ((!strcmp(dp->d_name, "linux,initrd-start") || + !strcmp(dp->d_name, "linux,initrd-end")) && + !reuse_initrd) + continue; + + /* This property will be created later in putnode() So + * ignore it now. + */ + if (!strcmp(dp->d_name, "bootargs")) + continue; + + if (! S_ISREG(statbuf.st_mode)) + continue; + + len = statbuf.st_size; + + dt_reserve(&dt, 4+((len + 3)/4)); + *dt++ = cpu_to_be32(FDT_PROP); + *dt++ = cpu_to_be32(len); + *dt++ = cpu_to_be32(propnum(fn)); + pad_structure_block(len); + + if (len) { + char *buf; + + buf = slurp_file_len(pathname, len, &slen); + if (slen != len) + die("unrecoverable error: short read from\"%s\"\n", + pathname); + + memcpy(dt, buf, slen); + free(buf); + } + + checkprop(fn, dt, len); + + dt += (len + 3)/4; + + fd = open(pathname, O_RDONLY); + if (fd == -1) + die("unrecoverable error: could not open \"%s\": %s\n", + pathname, strerror(errno)); + + if (!strcmp(dp->d_name, "reg") && usablemem_rgns.size) + add_usable_mem_property(fd, len); + add_dyn_reconf_usable_mem_property(dp, fd); + close(fd); + } + + fn[0] = '\0'; + checkprop(pathname, NULL, 0); +} + +/* + * Compare function used to sort the device-tree directories + * This function will be passed to scandir. + */ +static int comparefunc(const struct dirent **dentry1, + const struct dirent **dentry2) +{ + char *str1 = (*(struct dirent **)dentry1)->d_name; + char *str2 = (*(struct dirent **)dentry2)->d_name; + char *sep1 = strchr(str1, '@'); + char *sep2 = strchr(str2, '@'); + + /* + * strcmp scans from left to right and fails to idetify for some + * strings such as memory@10000000 and memory@f000000. + * Therefore, we get the wrong sorted order like memory@10000000 and + * memory@f000000. + */ + if (sep1 && sep2) { + int baselen1 = sep1 - str1; + int baselen2 = sep2 - str2; + int len1 = strlen(str1); + int len2 = strlen(str2); + + /* + * Check the base name matches, and the properties are + * different lengths. + */ + if ((baselen1 == baselen2) && (len1 != len2) && + !strncmp(str1, str2, baselen2)) + return (len1 > len2) - (len1 < len2); + } + + return strcmp(str1, str2); +} + +/* grab root= from the old command line */ +static void dt_copy_old_root_param(void) +{ + FILE *fp; + char filename[MAXPATH]; + char *last_cmdline = NULL; + char *p, *old_param; + size_t len = 0; + + strcpy(filename, pathname); + strcat(filename, "bootargs"); + fp = fopen(filename, "r"); + if (!fp) + return; + + if (getline(&last_cmdline, &len, fp) == -1) + die("unable to read %s\n", filename); + + p = strstr(last_cmdline, "root="); + if (p) { + old_param = strtok(p, " "); + len = strlen(local_cmdline); + if (len != 0) + strcat(local_cmdline, " "); + strcat(local_cmdline, old_param); + } + + if (last_cmdline) + free(last_cmdline); + + fclose(fp); +} + +/* + * put a node (directory) in the property structure. first properties + * then children. + */ +static void putnode(void) +{ + char *dn; + struct dirent *dp; + char *basename; + struct dirent **namelist; + int numlist, i; + struct stat statbuf; + int plen, ret; + + numlist = scandir(pathname, &namelist, 0, comparefunc); + if (numlist < 0) + die("unrecoverable error: could not scan \"%s\": %s\n", + pathname, strerror(errno)); + if (numlist == 0) + die("unrecoverable error: no directory entries in \"%s\"", + pathname); + + basename = strrchr(pathname,'/') + 1; + + plen = *basename ? strlen(basename) : 0; + /* Reserve space for string packed to words; e.g. string length 10 + * occupies 3 words, length 12 occupies 4 (for terminating \0s). + * So round up & include the \0: + */ + dt_reserve(&dt, 1+((plen + 4)/4)); + *dt++ = cpu_to_be32(FDT_BEGIN_NODE); + strcpy((void *)dt, *basename ? basename : ""); + dt += ((plen + 4)/4); + + if (*basename) + strcat(pathname, "/"); + dn = pathname + strlen(pathname); + + putprops(dn, namelist, numlist); + + /* Add initrd entries to the second kernel */ + if (initrd_base && initrd_size && !strcmp(basename,"chosen/")) { + int len = 8; + uint64_t bevalue; + + dt_reserve(&dt, 12); /* both props, of 6 words ea. */ + *dt++ = cpu_to_be32(FDT_PROP); + *dt++ = cpu_to_be32(len); + *dt++ = cpu_to_be32(propnum("linux,initrd-start")); + pad_structure_block(len); + + bevalue = cpu_to_be64(initrd_base); + memcpy(dt, &bevalue, len); + dt += (len + 3)/4; + + len = 8; + *dt++ = cpu_to_be32(FDT_PROP); + *dt++ = cpu_to_be32(len); + *dt++ = cpu_to_be32(propnum("linux,initrd-end")); + + bevalue = cpu_to_be64(initrd_base + initrd_size); + pad_structure_block(len); + + memcpy(dt, &bevalue, len); + dt += (len + 3)/4; + + reserve(initrd_base, initrd_size); + } + + /* + * Add cmdline to the second kernel. Use the old root= cmdline if there + * is no root= in the new command line and there's no --dt-no-old-root + * option being used. + */ + if (!strcmp(basename,"chosen/")) { + size_t result; + size_t cmd_len = 0; + char *param = NULL; + char filename[MAXPATH]; + char *buff; + int fd; + + cmd_len = strlen(local_cmdline); + if (cmd_len != 0) { + param = strstr(local_cmdline, "crashkernel="); + if (param) + crash_param = 1; + /* does the new cmdline have a root= ? ... */ + param = strstr(local_cmdline, "root="); + } + + if (!param && !dt_no_old_root) + dt_copy_old_root_param(); + + strcat(local_cmdline, " "); + cmd_len = strlen(local_cmdline); + cmd_len = cmd_len + 1; + + /* add new bootargs */ + dt_reserve(&dt, 4+((cmd_len+3)/4)); + *dt++ = cpu_to_be32(FDT_PROP); + *dt++ = cpu_to_be32(cmd_len); + *dt++ = cpu_to_be32(propnum("bootargs")); + pad_structure_block(cmd_len); + memcpy(dt, local_cmdline,cmd_len); + dt += (cmd_len + 3)/4; + + fprintf(stderr, "Modified cmdline:%s\n", local_cmdline); + + /* + * Determine the platform type/stdout type, so that purgatory + * code can print 'I'm in purgatory' message. Currently only + * pseries/hvcterminal is supported. + */ + ret = snprintf(filename, MAXPATH, "%sstdout-path", pathname); + if (ret < 0 || ret >= MAXPATH) { + fprintf(stderr, "snprintf failed: %s\n", strerror(errno)); + goto no_debug; + } + + fd = open(filename, O_RDONLY); + if (fd == -1) { + ret = snprintf(filename, MAXPATH, "%slinux,stdout-path", pathname); + if (ret < 0 || ret >= MAXPATH) { + fprintf(stderr, "snprintf failed: %s\n", strerror(errno)); + goto no_debug; + } + + fd = open(filename, O_RDONLY); + if (fd == -1) { + printf("Unable to find %s[linux,]stdout-path, printing from purgatory is disabled\n", + pathname); + goto no_debug; + } + } + if (fstat(fd, &statbuf)) { + printf("Unable to stat %s, printing from purgatory is disabled\n", + filename); + close(fd); + goto no_debug; + + } + + buff = malloc(statbuf.st_size); + if (!buff) { + printf("Can not allocate memory for buff\n"); + close(fd); + goto no_debug; + } + result = read(fd, buff, statbuf.st_size); + close(fd); + if (result <= 0) { + printf("Unable to read %s, printing from purgatory is disabled\n", + filename); + goto no_debug; + } + snprintf(filename, MAXPATH, "/proc/device-tree/%s/compatible", buff); + fd = open(filename, O_RDONLY); + if (fd == -1) { + printf("Unable to find %s printing from purgatory is disabled\n", + filename); + goto no_debug; + } + if (fstat(fd, &statbuf)) { + printf("Unable to stat %s printing from purgatory is disabled\n", + filename); + close(fd); + goto no_debug; + } + buff = realloc(buff, statbuf.st_size); + if (!buff) { + printf("Can not allocate memory for buff\n"); + close(fd); + goto no_debug; + } + result = read(fd, buff, statbuf.st_size); + if (result && (!strcmp(buff, "hvterm1") + || !strcmp(buff, "hvterm-protocol"))) + my_debug = 1; + close(fd); + free(buff); + } + +no_debug: + for (i=0; i < numlist; i++) { + dp = namelist[i]; + strcpy(dn, dp->d_name); + free(namelist[i]); + + if (!strcmp(dn, ".") || !strcmp(dn, "..")) + continue; + + if (lstat(pathname, &statbuf)) + die("unrecoverable error: could not stat \"%s\": %s\n", + pathname, strerror(errno)); + + if (S_ISDIR(statbuf.st_mode)) + putnode(); + } + + dt_reserve(&dt, 1); + *dt++ = cpu_to_be32(FDT_END_NODE); + dn[-1] = '\0'; + free(namelist); +} + +struct bootblock bb[1]; + +static void add_boot_block(char **bufp, off_t *sizep) +{ + unsigned long len; + unsigned long tlen, toff; + char *buf; + + len = _ALIGN(sizeof(bb[0]), 8); + + bb->off_mem_rsvmap = cpu_to_be32(len); + + for (len = 1; be64_to_cpu(mem_rsrv[len]); len += 2) + ; + len++; +#ifdef NEED_RESERVE_DTB + len+= 3; /* Leave space for totalsize reservation */ +#endif + len *= sizeof(mem_rsrv[0]); + + bb->off_dt_struct = cpu_to_be32(be32_to_cpu(bb->off_mem_rsvmap) + len); + + len = dt - dt_base; + len *= sizeof(unsigned); +#if (BOOT_BLOCK_VERSION >= 17) + bb->dt_struct_size = cpu_to_be32(len); +#endif + bb->off_dt_strings = cpu_to_be32(be32_to_cpu(bb->off_dt_struct) + len); + + len = propnum(""); + bb->dt_strings_size = cpu_to_be32(len); + len = _ALIGN(len, 4); + bb->totalsize = cpu_to_be32(be32_to_cpu(bb->off_dt_strings) + len); + + bb->magic = cpu_to_be32(FDT_MAGIC); + bb->version = cpu_to_be32(BOOT_BLOCK_VERSION); + bb->last_comp_version = cpu_to_be32(BOOT_BLOCK_LAST_COMP_VERSION); + +#ifdef NEED_RESERVE_DTB + reserve(0, be32_to_cpu(bb->totalsize)); /* patched later in kexec_load */ +#endif + + buf = malloc(be32_to_cpu(bb->totalsize)); + *bufp = buf; + + tlen = be32_to_cpu(bb->off_mem_rsvmap); + memcpy(buf, bb, tlen); + + toff = be32_to_cpu(bb->off_mem_rsvmap); + tlen = be32_to_cpu(bb->off_dt_struct) - be32_to_cpu(bb->off_mem_rsvmap); + memcpy(buf + toff, mem_rsrv, tlen); + + toff += be32_to_cpu(bb->off_dt_struct) - be32_to_cpu(bb->off_mem_rsvmap); + tlen = be32_to_cpu(bb->off_dt_strings) - be32_to_cpu(bb->off_dt_struct); + memcpy(buf + toff, dt_base, tlen); + + toff += be32_to_cpu(bb->off_dt_strings) - be32_to_cpu(bb->off_dt_struct); + tlen = be32_to_cpu(bb->totalsize) - be32_to_cpu(bb->off_dt_strings); + memcpy(buf + toff, propnames, tlen); + + *sizep = toff + be32_to_cpu(bb->totalsize) - + be32_to_cpu(bb->off_dt_strings); +} + +void create_flatten_tree(char **bufp, off_t *sizep, const char *cmdline) +{ + strcpy(pathname, "/proc/device-tree/"); + + dt_cur_size = INIT_TREE_WORDS; + dt_base = malloc(dt_cur_size*4); + if (!dt_base) { + die("Can't malloc %d bytes for dt struct!\n", dt_cur_size*4); + } + memset(dt_base, 0, dt_cur_size*4); + + dt = dt_base; + + if (cmdline) + strcpy(local_cmdline, cmdline); + + putnode(); + dt_reserve(&dt, 1); + *dt++ = cpu_to_be32(FDT_END); + + add_boot_block(bufp, sizep); + free(dt_base); +} diff --git a/kexec/fs2dt.h b/kexec/fs2dt.h new file mode 100644 index 0000000..fe24931 --- /dev/null +++ b/kexec/fs2dt.h @@ -0,0 +1,39 @@ +#ifndef FS2DT_H +#define FS2DT_H + +#if (BOOT_BLOCK_VERSION != 2 && BOOT_BLOCK_VERSION != 17) +#error Please add or correct definition of BOOT_BLOCK_VERSION +#endif + +/* boot block as defined by the linux kernel */ +struct bootblock { + unsigned magic; + unsigned totalsize; + unsigned off_dt_struct; + unsigned off_dt_strings; + unsigned off_mem_rsvmap; + unsigned version; + unsigned last_comp_version; +#if (BOOT_BLOCK_VERSION >= 2) + /* version 2 fields below */ + unsigned boot_physid; + /* version 3 fields below */ + unsigned dt_strings_size; +#if (BOOT_BLOCK_VERSION >= 17) + /* version 17 fields below */ + unsigned dt_struct_size; +#endif +#endif +}; + +extern struct bootblock bb[1]; + +/* Used for enabling printing message from purgatory code + * Only has implemented for PPC64 */ +extern int my_debug; +extern int dt_no_old_root; + +void reserve(unsigned long long where, unsigned long long length); +void create_flatten_tree(char **, off_t *, const char *); + +#endif /* KEXEC_H */ diff --git a/kexec/ifdown.c b/kexec/ifdown.c new file mode 100644 index 0000000..3ac19c1 --- /dev/null +++ b/kexec/ifdown.c @@ -0,0 +1,79 @@ +/* + * ifdown.c Find all network interfaces on the system and + * shut them down. + * + */ +char *v_ifdown = "@(#)ifdown.c 1.11 02-Jun-1998 miquels@cistron.nl"; + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <time.h> +#include <string.h> +#include <errno.h> + +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/time.h> + +#include <net/if.h> +#include <netinet/in.h> + +/* + * First, we find all shaper devices and down them. Then we + * down all real interfaces. This is because the comment in the + * shaper driver says "if you down the shaper device before the + * attached inerface your computer will follow". + */ +int ifdown(void) +{ + struct if_nameindex *ifa, *ifp; + struct ifreq ifr; + int fd, shaper; + + if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + fprintf(stderr, "ifdown: "); + perror("socket"); + goto error; + } + + if ((ifa = if_nameindex()) == NULL) { + fprintf(stderr, "ifdown: "); + perror("if_nameindex"); + goto error; + } + + for (shaper = 1; shaper >= 0; shaper--) { + for (ifp = ifa; ifp->if_index; ifp++) { + + if ((strncmp(ifp->if_name, "shaper", 6) == 0) + != shaper) continue; + if (strcmp(ifp->if_name, "lo") == 0) + continue; + if (strchr(ifp->if_name, ':') != NULL) + continue; + + strncpy(ifr.ifr_name, ifp->if_name, IFNAMSIZ-1); + ifr.ifr_name[IFNAMSIZ-1] = 0; + if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) { + fprintf(stderr, "ifdown: shutdown "); + perror(ifp->if_name); + goto error; + } + ifr.ifr_flags &= ~(IFF_UP); + if (ioctl(fd, SIOCSIFFLAGS, &ifr) < 0) { + fprintf(stderr, "ifdown: shutdown "); + perror(ifp->if_name); + goto error; + } + + } + } + + close(fd); + return 0; + +error: + close(fd); + return -1; +} diff --git a/kexec/kexec-elf-boot.c b/kexec/kexec-elf-boot.c new file mode 100644 index 0000000..38f9056 --- /dev/null +++ b/kexec/kexec-elf-boot.c @@ -0,0 +1,89 @@ +/* + * kexec: Linux boots Linux + * + * Copyright (C) 2003-2005 Eric Biederman (ebiederm@xmission.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation (version 2 of the License). + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#define _GNU_SOURCE +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <elf.h> +#include <boot/elf_boot.h> +#include <ip_checksum.h> +#include <x86/x86-linux.h> +#include "kexec.h" +#include "kexec-elf.h" +#include "kexec-elf-boot.h" + + +#define UPSZ(X) _ALIGN_UP(sizeof(X), 4) + +static struct boot_notes { + Elf_Bhdr hdr; + Elf_Nhdr bl_hdr; + unsigned char bl_desc[UPSZ(BOOTLOADER)]; + Elf_Nhdr blv_hdr; + unsigned char blv_desc[UPSZ(BOOTLOADER_VERSION)]; + Elf_Nhdr cmd_hdr; + unsigned char command_line[0]; +} boot_notes = { + .hdr = { + .b_signature = ELF_BOOT_MAGIC, + .b_size = sizeof(boot_notes), + .b_checksum = 0, + .b_records = 3, + }, + .bl_hdr = { + .n_namesz = 0, + .n_descsz = sizeof(BOOTLOADER), + .n_type = EBN_BOOTLOADER_NAME, + }, + .bl_desc = BOOTLOADER, + .blv_hdr = { + .n_namesz = 0, + .n_descsz = sizeof(BOOTLOADER_VERSION), + .n_type = EBN_BOOTLOADER_VERSION, + }, + .blv_desc = BOOTLOADER_VERSION, + .cmd_hdr = { + .n_namesz = 0, + .n_descsz = 0, + .n_type = EBN_COMMAND_LINE, + }, +}; + +unsigned long elf_boot_notes( + struct kexec_info *info, unsigned long max_addr, + const char *cmdline, int cmdline_len) +{ + unsigned long note_bytes; + unsigned long note_base; + struct boot_notes *notes; + note_bytes = sizeof(*notes) + _ALIGN(cmdline_len, 4); + notes = xmalloc(note_bytes); + memcpy(notes, &boot_notes, sizeof(boot_notes)); + memcpy(notes->command_line, cmdline, cmdline_len); + notes->hdr.b_size = note_bytes; + notes->cmd_hdr.n_descsz = cmdline_len; + notes->hdr.b_checksum = compute_ip_checksum(notes, note_bytes); + + note_base = add_buffer(info, notes, note_bytes, note_bytes, + 4, 0, max_addr, 1); + + return note_base; +} diff --git a/kexec/kexec-elf-boot.h b/kexec/kexec-elf-boot.h new file mode 100644 index 0000000..cff5c61 --- /dev/null +++ b/kexec/kexec-elf-boot.h @@ -0,0 +1,8 @@ +#ifndef KEXEC_ELF_BOOT_H +#define KEXEC_ELF_BOOT_H + +unsigned long elf_boot_notes( + struct kexec_info *info, unsigned long max_addr, + const char *cmdline, int cmdline_len); + +#endif /* KEXEC_ELF_BOOT_H */ diff --git a/kexec/kexec-elf-core.c b/kexec/kexec-elf-core.c new file mode 100644 index 0000000..a341fdb --- /dev/null +++ b/kexec/kexec-elf-core.c @@ -0,0 +1,29 @@ +#include <stdio.h> +#include <stdint.h> +#include <errno.h> +#include <stdlib.h> +#include "elf.h" +#include "kexec-elf.h" + + +int build_elf_core_info(const char *buf, off_t len, struct mem_ehdr *ehdr, + uint32_t flags) +{ + int result; + result = build_elf_info(buf, len, ehdr, flags); + if (result < 0) { + return result; + } + if ((ehdr->e_type != ET_CORE)) { + /* not an ELF Core */ + fprintf(stderr, "Not ELF type ET_CORE\n"); + return -1; + } + if (!ehdr->e_phdr) { + /* No program header */ + fprintf(stderr, "No ELF program header\n"); + return -1; + } + + return 0; +} diff --git a/kexec/kexec-elf-exec.c b/kexec/kexec-elf-exec.c new file mode 100644 index 0000000..bea7b3e --- /dev/null +++ b/kexec/kexec-elf-exec.c @@ -0,0 +1,228 @@ +#include <limits.h> +#include <stdint.h> +#include <string.h> +#include <stdio.h> +#include <errno.h> +#include <stdlib.h> +#include "elf.h" +#include <boot/elf_boot.h> +#include "kexec.h" +#include "kexec-elf.h" + +static const int probe_debug = 0; + +static void load_elf_segments(struct mem_ehdr *ehdr, struct kexec_info *info, unsigned long base) +{ + size_t i; + + /* Read in the PT_LOAD segments */ + for(i = 0; i < ehdr->e_phnum; i++) { + struct mem_phdr *phdr; + size_t size; + phdr = &ehdr->e_phdr[i]; + if (phdr->p_type != PT_LOAD) { + continue; + } + size = phdr->p_filesz; + if (size > phdr->p_memsz) { + size = phdr->p_memsz; + } + add_segment(info, phdr->p_data, size, + phdr->p_paddr + base, phdr->p_memsz); + } +} + +static int get_elf_exec_load_base(struct mem_ehdr *ehdr, struct kexec_info *info, + unsigned long min, unsigned long max, + unsigned long align, unsigned long *base) +{ + unsigned long first, last; + size_t i; + + /* Note on arm64: + * arm64's vmlinux has virtual address in physical address + * field of PT_LOAD segments. So the following validity check + * and relocation makes no sense on arm64. + */ + if (ehdr->e_machine == EM_AARCH64) + return 0; + + first = ULONG_MAX; + last = 0; + for(i = 0; i < ehdr->e_phnum; i++) { + unsigned long start, stop; + struct mem_phdr *phdr; + phdr = &ehdr->e_phdr[i]; + if ((phdr->p_type != PT_LOAD) || + (phdr->p_memsz == 0)) + { + continue; + } + start = phdr->p_paddr; + stop = start + phdr->p_memsz; + if (first > start) { + first = start; + } + if (last < stop) { + last = stop; + } + if (align < phdr->p_align) { + align = phdr->p_align; + } + } + + if ((max - min) < (last - first)) + return -1; + + if (!valid_memory_range(info, min > first ? min : first, max < last ? max : last)) { + unsigned long hole; + hole = locate_hole(info, last - first + 1, align, min, max, 1); + if (hole == ULONG_MAX) + return -1; + + /* Base is the value that when added + * to any virtual address in the file + * yields it's load virtual address. + */ + *base = hole - first; + } + return 0; +} + +int build_elf_exec_info(const char *buf, off_t len, struct mem_ehdr *ehdr, + uint32_t flags) +{ + struct mem_phdr *phdr, *end_phdr; + int result; + result = build_elf_info(buf, len, ehdr, flags); + if (result < 0) { + return result; + } + if ((ehdr->e_type != ET_EXEC) && (ehdr->e_type != ET_DYN) && + (ehdr->e_type != ET_CORE)) { + /* not an ELF executable */ + if (probe_debug) { + fprintf(stderr, "Not ELF type ET_EXEC or ET_DYN\n"); + } + return -1; + } + if (!ehdr->e_phdr) { + /* No program header */ + fprintf(stderr, "No ELF program header\n"); + return -1; + } + end_phdr = &ehdr->e_phdr[ehdr->e_phnum]; + for(phdr = ehdr->e_phdr; phdr != end_phdr; phdr++) { + /* Kexec does not support loading interpreters. + * In addition this check keeps us from attempting + * to kexec ordinay executables. + */ + if (phdr->p_type == PT_INTERP) { + fprintf(stderr, "Requires an ELF interpreter\n"); + return -1; + } + } + + return 0; +} + + +int elf_exec_load(struct mem_ehdr *ehdr, struct kexec_info *info) +{ + unsigned long base; + int result; + + if (!ehdr->e_phdr) { + fprintf(stderr, "No program header?\n"); + result = -1; + goto out; + } + + /* If I have a dynamic executable find it's size + * and then find a location for it in memory. + */ + base = 0; + if (ehdr->e_type == ET_DYN) { + result = get_elf_exec_load_base(ehdr, info, 0, elf_max_addr(ehdr), 0 /* align */, &base); + if (result < 0) + goto out; + } + + load_elf_segments(ehdr, info, base); + + /* Update entry point to reflect new load address*/ + ehdr->e_entry += base; + + result = 0; + out: + return result; +} + +int elf_exec_load_relocatable(struct mem_ehdr *ehdr, struct kexec_info *info, + unsigned long reloc_min, unsigned long reloc_max, + unsigned long align) +{ + unsigned long base; + int result; + + if (reloc_min > reloc_max) { + fprintf(stderr, "Bad relocation range, start=%lux > end=%lux.\n", reloc_min, reloc_max); + result = -1; + goto out; + } + if (!ehdr->e_phdr) { + fprintf(stderr, "No program header?\n"); + result = -1; + goto out; + } + + base = 0; + result = get_elf_exec_load_base(ehdr, info, reloc_min, reloc_max, align, &base); + if (result < 0) + goto out; + + load_elf_segments(ehdr, info, base); + + /* Update entry point to reflect new load address*/ + ehdr->e_entry += base; + + result = 0; + out: + return result; +} + +void elf_exec_build_load(struct kexec_info *info, struct mem_ehdr *ehdr, + const char *buf, off_t len, uint32_t flags) +{ + int result; + /* Parse the Elf file */ + result = build_elf_exec_info(buf, len, ehdr, flags); + if (result < 0) { + die("ELF exec parse failed\n"); + } + + /* Load the Elf data */ + result = elf_exec_load(ehdr, info); + if (result < 0) { + die("ELF exec load failed\n"); + } +} + +void elf_exec_build_load_relocatable(struct kexec_info *info, struct mem_ehdr *ehdr, + const char *buf, off_t len, uint32_t flags, + unsigned long reloc_min, unsigned long reloc_max, + unsigned long align) +{ + int result; + /* Parse the Elf file */ + result = build_elf_exec_info(buf, len, ehdr, flags); + if (result < 0) { + die("%s: ELF exec parse failed\n", __func__); + } + + /* Load the Elf data */ + result = elf_exec_load_relocatable(ehdr, info, reloc_min, reloc_max, align); + if (result < 0) { + die("%s: ELF exec load failed\n", __func__); + } +}
\ No newline at end of file diff --git a/kexec/kexec-elf-rel.c b/kexec/kexec-elf-rel.c new file mode 100644 index 0000000..0a8b4d2 --- /dev/null +++ b/kexec/kexec-elf-rel.c @@ -0,0 +1,553 @@ +#include <limits.h> +#include <stdint.h> +#include <string.h> +#include <stdio.h> +#include <errno.h> +#include <stdlib.h> +#include "elf.h" +#include <boot/elf_boot.h> +#include "kexec.h" +#include "kexec-elf.h" + +static const int probe_debug = 0; + +static size_t elf_sym_size(struct mem_ehdr *ehdr) +{ + size_t sym_size = 0; + if (ehdr->ei_class == ELFCLASS32) { + sym_size = sizeof(Elf32_Sym); + } + else if (ehdr->ei_class == ELFCLASS64) { + sym_size = sizeof(Elf64_Sym); + } + else { + die("Bad elf class"); + } + return sym_size; +} + +static size_t elf_rel_size(struct mem_ehdr *ehdr) +{ + size_t rel_size = 0; + if (ehdr->ei_class == ELFCLASS32) { + rel_size = sizeof(Elf32_Rel); + } + else if (ehdr->ei_class == ELFCLASS64) { + rel_size = sizeof(Elf64_Rel); + } + else { + die("Bad elf class"); + } + return rel_size; +} + +static size_t elf_rela_size(struct mem_ehdr *ehdr) +{ + size_t rel_size = 0; + if (ehdr->ei_class == ELFCLASS32) { + rel_size = sizeof(Elf32_Rela); + } + else if (ehdr->ei_class == ELFCLASS64) { + rel_size = sizeof(Elf64_Rela); + } + else { + die("Bad elf class"); + } + return rel_size; +} + +static struct mem_sym elf_sym(struct mem_ehdr *ehdr, const unsigned char *ptr) +{ + struct mem_sym sym = { 0, 0, 0, 0, 0, 0 }; + if (ehdr->ei_class == ELFCLASS32) { + Elf32_Sym lsym; + memcpy(&lsym, ptr, sizeof(lsym)); + sym.st_name = elf32_to_cpu(ehdr, lsym.st_name); + sym.st_value = elf32_to_cpu(ehdr, lsym.st_value); + sym.st_size = elf32_to_cpu(ehdr, lsym.st_size); + sym.st_info = lsym.st_info; + sym.st_other = lsym.st_other; + sym.st_shndx = elf16_to_cpu(ehdr, lsym.st_shndx); + } + else if (ehdr->ei_class == ELFCLASS64) { + Elf64_Sym lsym; + memcpy(&lsym, ptr, sizeof(lsym)); + sym.st_name = elf32_to_cpu(ehdr, lsym.st_name); + sym.st_value = elf64_to_cpu(ehdr, lsym.st_value); + sym.st_size = elf64_to_cpu(ehdr, lsym.st_size); + sym.st_info = lsym.st_info; + sym.st_other = lsym.st_other; + sym.st_shndx = elf16_to_cpu(ehdr, lsym.st_shndx); + } + else { + die("Bad elf class"); + } + return sym; +} + +static struct mem_rela elf_rel(struct mem_ehdr *ehdr, const unsigned char *ptr) +{ + struct mem_rela rela = { 0, 0, 0, 0 }; + if (ehdr->ei_class == ELFCLASS32) { + Elf32_Rel lrel; + memcpy(&lrel, ptr, sizeof(lrel)); + rela.r_offset = elf32_to_cpu(ehdr, lrel.r_offset); + rela.r_sym = ELF32_R_SYM(elf32_to_cpu(ehdr, lrel.r_info)); + rela.r_type = ELF32_R_TYPE(elf32_to_cpu(ehdr, lrel.r_info)); + rela.r_addend = 0; + } + else if (ehdr->ei_class == ELFCLASS64) { + Elf64_Rel lrel; + memcpy(&lrel, ptr, sizeof(lrel)); + rela.r_offset = elf64_to_cpu(ehdr, lrel.r_offset); + rela.r_sym = ELF64_R_SYM(elf64_to_cpu(ehdr, lrel.r_info)); + rela.r_type = ELF64_R_TYPE(elf64_to_cpu(ehdr, lrel.r_info)); + rela.r_addend = 0; + } + else { + die("Bad elf class"); + } + return rela; +} + +static struct mem_rela elf_rela(struct mem_ehdr *ehdr, const unsigned char *ptr) +{ + struct mem_rela rela = { 0, 0, 0, 0 }; + if (ehdr->ei_class == ELFCLASS32) { + Elf32_Rela lrela; + memcpy(&lrela, ptr, sizeof(lrela)); + rela.r_offset = elf32_to_cpu(ehdr, lrela.r_offset); + rela.r_sym = ELF32_R_SYM(elf32_to_cpu(ehdr, lrela.r_info)); + rela.r_type = ELF32_R_TYPE(elf32_to_cpu(ehdr, lrela.r_info)); + rela.r_addend = elf32_to_cpu(ehdr, lrela.r_addend); + } + else if (ehdr->ei_class == ELFCLASS64) { + Elf64_Rela lrela; + memcpy(&lrela, ptr, sizeof(lrela)); + rela.r_offset = elf64_to_cpu(ehdr, lrela.r_offset); + rela.r_sym = ELF64_R_SYM(elf64_to_cpu(ehdr, lrela.r_info)); + rela.r_type = ELF64_R_TYPE(elf64_to_cpu(ehdr, lrela.r_info)); + rela.r_addend = elf64_to_cpu(ehdr, lrela.r_addend); + } + else { + die("Bad elf class"); + } + return rela; +} + +int build_elf_rel_info(const char *buf, off_t len, struct mem_ehdr *ehdr, + uint32_t flags) +{ + int result; + result = build_elf_info(buf, len, ehdr, flags); + if (result < 0) { + return result; + } + if (ehdr->e_type != ET_REL) { + /* not an ELF relocate object */ + if (probe_debug) { + fprintf(stderr, "Not ELF type ET_REL\n"); + fprintf(stderr, "ELF Type: %x\n", ehdr->e_type); + } + return -1; + } + if (!ehdr->e_shdr) { + /* No section headers */ + if (probe_debug) { + fprintf(stderr, "No ELF section headers\n"); + } + return -1; + } + if (!machine_verify_elf_rel(ehdr)) { + /* It does not meant the native architecture constraints */ + if (probe_debug) { + fprintf(stderr, "ELF architecture constraint failure\n"); + } + return -1; + } + return 0; +} + +static unsigned long get_section_addralign(struct mem_shdr *shdr) +{ + return (shdr->sh_addralign == 0) ? 1 : shdr->sh_addralign; +} + +int elf_rel_load(struct mem_ehdr *ehdr, struct kexec_info *info, + unsigned long min, unsigned long max, int end) +{ + struct mem_shdr *shdr, *shdr_end, *entry_shdr; + unsigned long entry; + int result; + unsigned char *buf; + unsigned long buf_align, bufsz, bss_align, bsssz, bss_pad; + unsigned long buf_addr, data_addr, bss_addr; + + if (max > elf_max_addr(ehdr)) { + max = elf_max_addr(ehdr); + } + if (!ehdr->e_shdr) { + fprintf(stderr, "No section header?\n"); + result = -1; + goto out; + } + shdr_end = &ehdr->e_shdr[ehdr->e_shnum]; + + /* Find which section entry is in */ + entry_shdr = NULL; + entry = ehdr->e_entry; + for(shdr = ehdr->e_shdr; shdr != shdr_end; shdr++) { + if (!(shdr->sh_flags & SHF_ALLOC)) { + continue; + } + if (!(shdr->sh_flags & SHF_EXECINSTR)) { + continue; + } + /* Make entry section relative */ + if ((shdr->sh_addr <= ehdr->e_entry) && + ((shdr->sh_addr + shdr->sh_size) > ehdr->e_entry)) { + entry_shdr = shdr; + entry -= shdr->sh_addr; + break; + } + } + + /* Find the memory footprint of the relocatable object */ + buf_align = 1; + bss_align = 1; + bufsz = 0; + bsssz = 0; + for(shdr = ehdr->e_shdr; shdr != shdr_end; shdr++) { + if (!(shdr->sh_flags & SHF_ALLOC)) { + continue; + } + if (shdr->sh_type != SHT_NOBITS) { + unsigned long align; + align = get_section_addralign(shdr); + /* See if I need more alignment */ + if (buf_align < align) { + buf_align = align; + } + /* Now align bufsz */ + bufsz = _ALIGN(bufsz, align); + /* And now add our buffer */ + bufsz += shdr->sh_size; + } + else { + unsigned long align; + align = get_section_addralign(shdr); + /* See if I need more alignment */ + if (bss_align < align) { + bss_align = align; + } + /* Now align bsssz */ + bsssz = _ALIGN(bsssz, align); + /* And now add our buffer */ + bsssz += shdr->sh_size; + } + } + if (buf_align < bss_align) { + buf_align = bss_align; + } + bss_pad = 0; + if (bufsz & (bss_align - 1)) { + bss_pad = bss_align - (bufsz & (bss_align - 1)); + } + + /* Allocate where we will put the relocated object */ + buf = xmalloc(bufsz); + buf_addr = add_buffer(info, buf, bufsz, bufsz + bss_pad + bsssz, + buf_align, min, max, end); + ehdr->rel_addr = buf_addr; + ehdr->rel_size = bufsz + bss_pad + bsssz; + + /* Walk through and find an address for each SHF_ALLOC section */ + data_addr = buf_addr; + bss_addr = buf_addr + bufsz + bss_pad; + for(shdr = ehdr->e_shdr; shdr != shdr_end; shdr++) { + unsigned long align; + if (!(shdr->sh_flags & SHF_ALLOC)) { + continue; + } + align = get_section_addralign(shdr); + if (shdr->sh_type != SHT_NOBITS) { + unsigned long off; + /* Adjust the address */ + data_addr = _ALIGN(data_addr, align); + + /* Update the section */ + off = data_addr - buf_addr; + memcpy(buf + off, shdr->sh_data, shdr->sh_size); + shdr->sh_addr = data_addr; + shdr->sh_data = buf + off; + + /* Advance to the next address */ + data_addr += shdr->sh_size; + } else { + /* Adjust the address */ + bss_addr = _ALIGN(bss_addr, align); + + /* Update the section */ + shdr->sh_addr = bss_addr; + + /* Advance to the next address */ + bss_addr += shdr->sh_size; + } + } + /* Compute the relocated value for entry, and load it */ + if (entry_shdr) { + entry += entry_shdr->sh_addr; + ehdr->e_entry = entry; + } + info->entry = (void *)entry; + + /* Now that the load address is known apply relocations */ + for(shdr = ehdr->e_shdr; shdr != shdr_end; shdr++) { + struct mem_shdr *section, *symtab; + const unsigned char *strtab; + size_t rel_size; + const unsigned char *ptr, *rel_end; + if ((shdr->sh_type != SHT_RELA) && (shdr->sh_type != SHT_REL)) { + continue; + } + if ((shdr->sh_info > ehdr->e_shnum) || + (shdr->sh_link > ehdr->e_shnum)) + { + die("Invalid section number\n"); + } + section = &ehdr->e_shdr[shdr->sh_info]; + symtab = &ehdr->e_shdr[shdr->sh_link]; + + if (!(section->sh_flags & SHF_ALLOC)) { + continue; + } + + if (symtab->sh_link > ehdr->e_shnum) { + /* Invalid section number? */ + continue; + } + strtab = ehdr->e_shdr[symtab->sh_link].sh_data; + + rel_size = 0; + if (shdr->sh_type == SHT_REL) { + rel_size = elf_rel_size(ehdr); + } + else if (shdr->sh_type == SHT_RELA) { + rel_size = elf_rela_size(ehdr); + } + else { + die("Cannot find elf rel size\n"); + } + rel_end = shdr->sh_data + shdr->sh_size; + for(ptr = shdr->sh_data; ptr < rel_end; ptr += rel_size) { + struct mem_rela rel = {0}; + struct mem_sym sym; + const void *location; + const unsigned char *name; + unsigned long address, value, sec_base; + if (shdr->sh_type == SHT_REL) { + rel = elf_rel(ehdr, ptr); + } + else if (shdr->sh_type == SHT_RELA) { + rel = elf_rela(ehdr, ptr); + } + /* the location to change */ + location = section->sh_data + rel.r_offset; + + /* The final address of that location */ + address = section->sh_addr + rel.r_offset; + + /* The relevant symbol */ + sym = elf_sym(ehdr, symtab->sh_data + (rel.r_sym * elf_sym_size(ehdr))); + + if (sym.st_name) { + name = strtab + sym.st_name; + } + else { + name = ehdr->e_shdr[ehdr->e_shstrndx].sh_data; + name += ehdr->e_shdr[sym.st_shndx].sh_name; + } + + dbgprintf("sym: %10s info: %02x other: %02x shndx: %x value: %llx size: %llx\n", + name, + sym.st_info, + sym.st_other, + sym.st_shndx, + sym.st_value, + sym.st_size); + + if (sym.st_shndx == STN_UNDEF) { + /* + * NOTE: ppc64 elf .ro shows up a UNDEF section. + * From Elf 1.2 Spec: + * Relocation Entries: If the index is STN_UNDEF, + * the undefined symbol index, the relocation uses 0 + * as the "symbol value". + * TOC symbols appear as undefined but should be + * resolved as well. Their type is STT_NOTYPE so allow + * such symbols to be processed. + */ + if (ELF32_ST_TYPE(sym.st_info) != STT_NOTYPE) + die("Undefined symbol: %s\n", name); + } + sec_base = 0; + if (sym.st_shndx == SHN_COMMON) { + die("symbol: '%s' in common section\n", + name); + } + else if (sym.st_shndx == SHN_ABS) { + sec_base = 0; + } + else if (sym.st_shndx > ehdr->e_shnum) { + die("Invalid section: %d for symbol %s\n", + sym.st_shndx, name); + } + else { + sec_base = ehdr->e_shdr[sym.st_shndx].sh_addr; + } + value = sym.st_value; + value += sec_base; + value += rel.r_addend; + + dbgprintf("sym: %s value: %lx addr: %lx\n", + name, value, address); + + machine_apply_elf_rel(ehdr, &sym, rel.r_type, + (void *)location, address, value); + } + } + result = 0; + out: + return result; +} + +void elf_rel_build_load(struct kexec_info *info, struct mem_ehdr *ehdr, + const char *buf, off_t len, unsigned long min, unsigned long max, + int end, uint32_t flags) +{ + int result; + + /* Parse the Elf file */ + result = build_elf_rel_info(buf, len, ehdr, flags); + if (result < 0) { + die("ELF rel parse failed\n"); + } + /* Load the Elf data */ + result = elf_rel_load(ehdr, info, min, max, end); + if (result < 0) { + die("ELF rel load failed\n"); + } +} + +int elf_rel_find_symbol(struct mem_ehdr *ehdr, + const char *name, struct mem_sym *ret_sym) +{ + struct mem_shdr *shdr, *shdr_end; + + if (!ehdr->e_shdr) { + /* "No section header? */ + return -1; + } + /* Walk through the sections and find the symbol table */ + shdr_end = &ehdr->e_shdr[ehdr->e_shnum]; + for (shdr = ehdr->e_shdr; shdr != shdr_end; shdr++) { + const char *strtab; + size_t sym_size; + const unsigned char *ptr, *sym_end; + if (shdr->sh_type != SHT_SYMTAB) { + continue; + } + if (shdr->sh_link > ehdr->e_shnum) { + /* Invalid strtab section number? */ + continue; + } + strtab = (char *)ehdr->e_shdr[shdr->sh_link].sh_data; + /* Walk through the symbol table and find the symbol */ + sym_size = elf_sym_size(ehdr); + sym_end = shdr->sh_data + shdr->sh_size; + for(ptr = shdr->sh_data; ptr < sym_end; ptr += sym_size) { + struct mem_sym sym; + sym = elf_sym(ehdr, ptr); + if (ELF32_ST_BIND(sym.st_info) != STB_GLOBAL) { + continue; + } + if (strcmp(strtab + sym.st_name, name) != 0) { + continue; + } + if ((sym.st_shndx == STN_UNDEF) || + (sym.st_shndx > ehdr->e_shnum)) + { + die("Symbol: %s has Bad section index %d\n", + name, sym.st_shndx); + } + *ret_sym = sym; + return 0; + } + } + /* I did not find it :( */ + return -1; + +} + +unsigned long elf_rel_get_addr(struct mem_ehdr *ehdr, const char *name) +{ + struct mem_shdr *shdr; + struct mem_sym sym; + int result; + result = elf_rel_find_symbol(ehdr, name, &sym); + if (result < 0) { + die("Symbol: %s not found cannot retrive it's address\n", + name); + } + shdr = &ehdr->e_shdr[sym.st_shndx]; + return shdr->sh_addr + sym.st_value; +} + +void elf_rel_set_symbol(struct mem_ehdr *ehdr, + const char *name, const void *buf, size_t size) +{ + unsigned char *sym_buf; + struct mem_shdr *shdr; + struct mem_sym sym; + int result; + + result = elf_rel_find_symbol(ehdr, name, &sym); + if (result < 0) { + die("Symbol: %s not found cannot set\n", + name); + } + if (sym.st_size != size) { + die("Symbol: %s has size: %lld not %zd\n", + name, sym.st_size, size); + } + shdr = &ehdr->e_shdr[sym.st_shndx]; + if (shdr->sh_type == SHT_NOBITS) { + die("Symbol: %s is in a bss section cannot set\n", name); + } + sym_buf = (unsigned char *)(shdr->sh_data + sym.st_value); + memcpy(sym_buf, buf, size); +} + +void elf_rel_get_symbol(struct mem_ehdr *ehdr, + const char *name, void *buf, size_t size) +{ + const unsigned char *sym_buf; + struct mem_shdr *shdr; + struct mem_sym sym; + int result; + + result = elf_rel_find_symbol(ehdr, name, &sym); + if (result < 0) { + die("Symbol: %s not found cannot get\n", name); + } + if (sym.st_size != size) { + die("Symbol: %s has size: %lld not %zd\n", + name, sym.st_size, size); + } + shdr = &ehdr->e_shdr[sym.st_shndx]; + if (shdr->sh_type == SHT_NOBITS) { + die("Symbol: %s is in a bss section cannot set\n", name); + } + sym_buf = shdr->sh_data + sym.st_value; + memcpy(buf, sym_buf,size); +} diff --git a/kexec/kexec-elf.c b/kexec/kexec-elf.c new file mode 100644 index 0000000..be60bbd --- /dev/null +++ b/kexec/kexec-elf.c @@ -0,0 +1,787 @@ +#include <limits.h> +#include <stdint.h> +#include <string.h> +#include <stdio.h> +#include <errno.h> +#include <stdlib.h> +#include "elf.h" +#include <boot/elf_boot.h> +#include "kexec.h" +#include "kexec-elf.h" +#include "crashdump.h" + +static const int probe_debug = 0; + +uint16_t elf16_to_cpu(const struct mem_ehdr *ehdr, uint16_t value) +{ + if (ehdr->ei_data == ELFDATA2LSB) { + value = le16_to_cpu(value); + } + else if (ehdr->ei_data == ELFDATA2MSB) { + value = be16_to_cpu(value); + } + return value; +} + +uint32_t elf32_to_cpu(const struct mem_ehdr *ehdr, uint32_t value) +{ + if (ehdr->ei_data == ELFDATA2LSB) { + value = le32_to_cpu(value); + } + else if (ehdr->ei_data == ELFDATA2MSB) { + value = be32_to_cpu(value); + } + return value; +} + +uint64_t elf64_to_cpu(const struct mem_ehdr *ehdr, uint64_t value) +{ + if (ehdr->ei_data == ELFDATA2LSB) { + value = le64_to_cpu(value); + } + else if (ehdr->ei_data == ELFDATA2MSB) { + value = be64_to_cpu(value); + } + return value; +} + +uint16_t cpu_to_elf16(const struct mem_ehdr *ehdr, uint16_t value) +{ + if (ehdr->ei_data == ELFDATA2LSB) { + value = cpu_to_le16(value); + } + else if (ehdr->ei_data == ELFDATA2MSB) { + value = cpu_to_be16(value); + } + return value; +} + +uint32_t cpu_to_elf32(const struct mem_ehdr *ehdr, uint32_t value) +{ + if (ehdr->ei_data == ELFDATA2LSB) { + value = cpu_to_le32(value); + } + else if (ehdr->ei_data == ELFDATA2MSB) { + value = cpu_to_be32(value); + } + return value; +} + +uint64_t cpu_to_elf64(const struct mem_ehdr *ehdr, uint64_t value) +{ + if (ehdr->ei_data == ELFDATA2LSB) { + value = cpu_to_le64(value); + } + else if (ehdr->ei_data == ELFDATA2MSB) { + value = cpu_to_be64(value); + } + return value; +} + +#define ELF32_MAX 0xffffffff +#define ELF64_MAX 0xffffffffffffffff +#if ELF64_MAX > ULONG_MAX +#undef ELF64_MAX +#define ELF64_MAX ULONG_MAX +#endif + +unsigned long elf_max_addr(const struct mem_ehdr *ehdr) +{ + unsigned long max_addr = 0; + if (ehdr->ei_class == ELFCLASS32) { + max_addr = ELF32_MAX; + } + else if (ehdr->ei_class == ELFCLASS64) { + max_addr = ELF64_MAX; + } + return max_addr; +} +static int build_mem_elf32_ehdr(const char *buf, off_t len, struct mem_ehdr *ehdr) +{ + Elf32_Ehdr lehdr; + if ((uintmax_t)len < (uintmax_t)sizeof(lehdr)) { + /* Buffer is to small to be an elf executable */ + if (probe_debug) { + fprintf(stderr, "Buffer is to small to hold ELF header\n"); + } + return -1; + } + memcpy(&lehdr, buf, sizeof(lehdr)); + if (elf16_to_cpu(ehdr, lehdr.e_ehsize) != sizeof(Elf32_Ehdr)) { + /* Invalid Elf header size */ + if (probe_debug) { + fprintf(stderr, "Bad ELF header size\n"); + } + return -1; + } + if (elf32_to_cpu(ehdr, lehdr.e_entry) > UINT32_MAX) { + /* entry is to large */ + if (probe_debug) { + fprintf(stderr, "ELF e_entry to large\n"); + } + return -1; + } + if (elf32_to_cpu(ehdr, lehdr.e_phoff) > UINT32_MAX) { + /* phoff is to large */ + if (probe_debug) { + fprintf(stderr, "ELF e_phoff to large\n"); + } + return -1; + } + if (elf32_to_cpu(ehdr, lehdr.e_shoff) > UINT32_MAX) { + /* shoff is to large */ + if (probe_debug) { + fprintf(stderr, "ELF e_shoff to large\n"); + } + return -1; + } + ehdr->e_type = elf16_to_cpu(ehdr, lehdr.e_type); + ehdr->e_machine = elf16_to_cpu(ehdr, lehdr.e_machine); + ehdr->e_version = elf32_to_cpu(ehdr, lehdr.e_version); + ehdr->e_entry = elf32_to_cpu(ehdr, lehdr.e_entry); + ehdr->e_phoff = elf32_to_cpu(ehdr, lehdr.e_phoff); + ehdr->e_shoff = elf32_to_cpu(ehdr, lehdr.e_shoff); + ehdr->e_flags = elf32_to_cpu(ehdr, lehdr.e_flags); + ehdr->e_phnum = elf16_to_cpu(ehdr, lehdr.e_phnum); + ehdr->e_shnum = elf16_to_cpu(ehdr, lehdr.e_shnum); + ehdr->e_shstrndx = elf16_to_cpu(ehdr, lehdr.e_shstrndx); + + if ((ehdr->e_phnum > 0) && + (elf16_to_cpu(ehdr, lehdr.e_phentsize) != sizeof(Elf32_Phdr))) + { + /* Invalid program header size */ + if (probe_debug) { + fprintf(stderr, "ELF bad program header size\n"); + } + return -1; + } + if ((ehdr->e_shnum > 0) && + (elf16_to_cpu(ehdr, lehdr.e_shentsize) != sizeof(Elf32_Shdr))) + { + /* Invalid section header size */ + if (probe_debug) { + fprintf(stderr, "ELF bad section header size\n"); + } + return -1; + } + + return 0; +} + +static int build_mem_elf64_ehdr(const char *buf, off_t len, struct mem_ehdr *ehdr) +{ + Elf64_Ehdr lehdr; + if ((uintmax_t)len < (uintmax_t)sizeof(lehdr)) { + /* Buffer is to small to be an elf executable */ + if (probe_debug) { + fprintf(stderr, "Buffer is to small to hold ELF header\n"); + } + return -1; + } + memcpy(&lehdr, buf, sizeof(lehdr)); + if (elf16_to_cpu(ehdr, lehdr.e_ehsize) != sizeof(Elf64_Ehdr)) { + /* Invalid Elf header size */ + if (probe_debug) { + fprintf(stderr, "Bad ELF header size\n"); + } + return -1; + } + if (elf32_to_cpu(ehdr, lehdr.e_entry) > UINT32_MAX) { + /* entry is to large */ + if (probe_debug) { + fprintf(stderr, "ELF e_entry to large\n"); + } + return -1; + } + if (elf32_to_cpu(ehdr, lehdr.e_phoff) > UINT32_MAX) { + /* phoff is to large */ + if (probe_debug) { + fprintf(stderr, "ELF e_phoff to large\n"); + } + return -1; + } + if (elf32_to_cpu(ehdr, lehdr.e_shoff) > UINT32_MAX) { + /* shoff is to large */ + if (probe_debug) { + fprintf(stderr, "ELF e_shoff to large\n"); + } + return -1; + } + ehdr->e_type = elf16_to_cpu(ehdr, lehdr.e_type); + ehdr->e_machine = elf16_to_cpu(ehdr, lehdr.e_machine); + ehdr->e_version = elf32_to_cpu(ehdr, lehdr.e_version); + ehdr->e_entry = elf64_to_cpu(ehdr, lehdr.e_entry); + ehdr->e_phoff = elf64_to_cpu(ehdr, lehdr.e_phoff); + ehdr->e_shoff = elf64_to_cpu(ehdr, lehdr.e_shoff); + ehdr->e_flags = elf32_to_cpu(ehdr, lehdr.e_flags); + ehdr->e_phnum = elf16_to_cpu(ehdr, lehdr.e_phnum); + ehdr->e_shnum = elf16_to_cpu(ehdr, lehdr.e_shnum); + ehdr->e_shstrndx = elf16_to_cpu(ehdr, lehdr.e_shstrndx); + + if ((ehdr->e_phnum > 0) && + (elf16_to_cpu(ehdr, lehdr.e_phentsize) != sizeof(Elf64_Phdr))) + { + /* Invalid program header size */ + if (probe_debug) { + fprintf(stderr, "ELF bad program header size\n"); + } + return -1; + } + if ((ehdr->e_shnum > 0) && + (elf16_to_cpu(ehdr, lehdr.e_shentsize) != sizeof(Elf64_Shdr))) + { + /* Invalid section header size */ + if (probe_debug) { + fprintf(stderr, "ELF bad section header size\n"); + } + return -1; + } + + return 0; +} + +static int build_mem_ehdr(const char *buf, off_t len, struct mem_ehdr *ehdr) +{ + unsigned char e_ident[EI_NIDENT]; + int result; + memset(ehdr, 0, sizeof(*ehdr)); + if ((uintmax_t)len < (uintmax_t)sizeof(e_ident)) { + /* Buffer is to small to be an elf executable */ + if (probe_debug) { + fprintf(stderr, "Buffer is to small to hold ELF e_ident\n"); + } + return -1; + } + memcpy(e_ident, buf, sizeof(e_ident)); + if (memcmp(e_ident, ELFMAG, SELFMAG) != 0) { + /* No ELF header magic */ + if (probe_debug) { + fprintf(stderr, "NO ELF header magic\n"); + } + return -1; + } + ehdr->ei_class = e_ident[EI_CLASS]; + ehdr->ei_data = e_ident[EI_DATA]; + if ( (ehdr->ei_class != ELFCLASS32) && + (ehdr->ei_class != ELFCLASS64)) + { + /* Not a supported elf class */ + if (probe_debug) { + fprintf(stderr, "Not a supported ELF class\n"); + } + return -1; + } + if ( (ehdr->ei_data != ELFDATA2LSB) && + (ehdr->ei_data != ELFDATA2MSB)) + { + /* Not a supported elf data type */ + if (probe_debug) { + fprintf(stderr, "Not a supported ELF data format\n"); + } + return -1; + } + + result = -1; + if (ehdr->ei_class == ELFCLASS32) { + result = build_mem_elf32_ehdr(buf, len, ehdr); + } + else if (ehdr->ei_class == ELFCLASS64) { + result = build_mem_elf64_ehdr(buf, len, ehdr); + } + if (result < 0) { + return result; + } + if ((e_ident[EI_VERSION] != EV_CURRENT) || + (ehdr->e_version != EV_CURRENT)) + { + if (probe_debug) { + fprintf(stderr, "Unknown ELF version\n"); + } + /* Unknwon elf version */ + return -1; + } + return 0; +} + +static int build_mem_elf32_phdr(const char *buf, struct mem_ehdr *ehdr, int idx) +{ + struct mem_phdr *phdr; + const char *pbuf; + Elf32_Phdr lphdr; + pbuf = buf + ehdr->e_phoff + (idx * sizeof(lphdr)); + phdr = &ehdr->e_phdr[idx]; + memcpy(&lphdr, pbuf, sizeof(lphdr)); + + if ( (elf32_to_cpu(ehdr, lphdr.p_filesz) > UINT32_MAX) || + (elf32_to_cpu(ehdr, lphdr.p_memsz) > UINT32_MAX) || + (elf32_to_cpu(ehdr, lphdr.p_offset) > UINT32_MAX) || + (elf32_to_cpu(ehdr, lphdr.p_paddr) > UINT32_MAX) || + (elf32_to_cpu(ehdr, lphdr.p_vaddr) > UINT32_MAX) || + (elf32_to_cpu(ehdr, lphdr.p_align) > UINT32_MAX)) + { + fprintf(stderr, "Program segment size out of range\n"); + return -1; + } + + phdr->p_type = elf32_to_cpu(ehdr, lphdr.p_type); + phdr->p_paddr = elf32_to_cpu(ehdr, lphdr.p_paddr); + phdr->p_vaddr = elf32_to_cpu(ehdr, lphdr.p_vaddr); + phdr->p_filesz = elf32_to_cpu(ehdr, lphdr.p_filesz); + phdr->p_memsz = elf32_to_cpu(ehdr, lphdr.p_memsz); + phdr->p_offset = elf32_to_cpu(ehdr, lphdr.p_offset); + phdr->p_flags = elf32_to_cpu(ehdr, lphdr.p_flags); + phdr->p_align = elf32_to_cpu(ehdr, lphdr.p_align); + + return 0; +} + +static int build_mem_elf64_phdr(const char *buf, struct mem_ehdr *ehdr, int idx) +{ + struct mem_phdr *phdr; + const char *pbuf; + Elf64_Phdr lphdr; + pbuf = buf + ehdr->e_phoff + (idx * sizeof(lphdr)); + phdr = &ehdr->e_phdr[idx]; + memcpy(&lphdr, pbuf, sizeof(lphdr)); + + if ( (elf64_to_cpu(ehdr, lphdr.p_filesz) > UINT64_MAX) || + (elf64_to_cpu(ehdr, lphdr.p_memsz) > UINT64_MAX) || + (elf64_to_cpu(ehdr, lphdr.p_offset) > UINT64_MAX) || + (elf64_to_cpu(ehdr, lphdr.p_paddr) > UINT64_MAX) || + (elf64_to_cpu(ehdr, lphdr.p_vaddr) > UINT64_MAX) || + (elf64_to_cpu(ehdr, lphdr.p_align) > UINT64_MAX)) + { + fprintf(stderr, "Program segment size out of range\n"); + return -1; + } + + phdr->p_type = elf32_to_cpu(ehdr, lphdr.p_type); + phdr->p_paddr = elf64_to_cpu(ehdr, lphdr.p_paddr); + phdr->p_vaddr = elf64_to_cpu(ehdr, lphdr.p_vaddr); + phdr->p_filesz = elf64_to_cpu(ehdr, lphdr.p_filesz); + phdr->p_memsz = elf64_to_cpu(ehdr, lphdr.p_memsz); + phdr->p_offset = elf64_to_cpu(ehdr, lphdr.p_offset); + phdr->p_flags = elf32_to_cpu(ehdr, lphdr.p_flags); + phdr->p_align = elf64_to_cpu(ehdr, lphdr.p_align); + + return 0; +} + +static int build_mem_phdrs(const char *buf, off_t len, struct mem_ehdr *ehdr, + uint32_t flags) +{ + size_t phdr_size, mem_phdr_size, i; + + /* e_phnum is at most 65535 so calculating + * the size of the program header cannot overflow. + */ + /* Is the program header in the file buffer? */ + phdr_size = 0; + if (ehdr->ei_class == ELFCLASS32) { + phdr_size = sizeof(Elf32_Phdr); + } + else if (ehdr->ei_class == ELFCLASS64) { + phdr_size = sizeof(Elf64_Phdr); + } + else { + fprintf(stderr, "Invalid ei_class?\n"); + return -1; + } + phdr_size *= ehdr->e_phnum; + if ((uintmax_t)(ehdr->e_phoff + phdr_size) > (uintmax_t)len) { + /* The program header did not fit in the file buffer */ + if (probe_debug || (flags & ELF_SKIP_FILESZ_CHECK)) { + fprintf(stderr, "ELF program headers truncated" + " have %ju bytes need %ju bytes\n", + (uintmax_t)len, + (uintmax_t)(ehdr->e_phoff + phdr_size)); + } + return -1; + } + + /* Allocate the e_phdr array */ + mem_phdr_size = sizeof(ehdr->e_phdr[0]) * ehdr->e_phnum; + ehdr->e_phdr = xmalloc(mem_phdr_size); + + for(i = 0; i < ehdr->e_phnum; i++) { + struct mem_phdr *phdr; + int result; + result = -1; + if (ehdr->ei_class == ELFCLASS32) { + result = build_mem_elf32_phdr(buf, ehdr, i); + + } + else if (ehdr->ei_class == ELFCLASS64) { + result = build_mem_elf64_phdr(buf, ehdr, i); + } + if (result < 0) { + return result; + } + + /* Check the program headers to be certain + * they are safe to use. + * Skip the check if ELF_SKIP_FILESZ_CHECK is set. + */ + phdr = &ehdr->e_phdr[i]; + if (!(flags & ELF_SKIP_FILESZ_CHECK) + && (uintmax_t)(phdr->p_offset + phdr->p_filesz) > + (uintmax_t)len) { + /* The segment does not fit in the buffer */ + if (probe_debug) { + fprintf(stderr, "ELF segment not in file\n"); + } + return -1; + } + if (phdr->p_paddr != (unsigned long long)-1 && + (phdr->p_paddr + phdr->p_memsz) < phdr->p_paddr) { + /* The memory address wraps */ + if (probe_debug) { + fprintf(stderr, "ELF address wrap around\n"); + } + return -1; + } + /* Remember where the segment lives in the buffer */ + phdr->p_data = buf + phdr->p_offset; + } + return 0; +} + +static int build_mem_elf32_shdr(const char *buf, struct mem_ehdr *ehdr, int idx) +{ + struct mem_shdr *shdr; + const char *sbuf; + int size_ok; + Elf32_Shdr lshdr; + sbuf = buf + ehdr->e_shoff + (idx * sizeof(lshdr)); + shdr = &ehdr->e_shdr[idx]; + memcpy(&lshdr, sbuf, sizeof(lshdr)); + + if ( (elf32_to_cpu(ehdr, lshdr.sh_flags) > UINT32_MAX) || + (elf32_to_cpu(ehdr, lshdr.sh_addr) > UINT32_MAX) || + (elf32_to_cpu(ehdr, lshdr.sh_offset) > UINT32_MAX) || + (elf32_to_cpu(ehdr, lshdr.sh_size) > UINT32_MAX) || + (elf32_to_cpu(ehdr, lshdr.sh_addralign) > UINT32_MAX) || + (elf32_to_cpu(ehdr, lshdr.sh_entsize) > UINT32_MAX)) + { + fprintf(stderr, "Program section size out of range\n"); + return -1; + } + + shdr->sh_name = elf32_to_cpu(ehdr, lshdr.sh_name); + shdr->sh_type = elf32_to_cpu(ehdr, lshdr.sh_type); + shdr->sh_flags = elf32_to_cpu(ehdr, lshdr.sh_flags); + shdr->sh_addr = elf32_to_cpu(ehdr, lshdr.sh_addr); + shdr->sh_offset = elf32_to_cpu(ehdr, lshdr.sh_offset); + shdr->sh_size = elf32_to_cpu(ehdr, lshdr.sh_size); + shdr->sh_link = elf32_to_cpu(ehdr, lshdr.sh_link); + shdr->sh_info = elf32_to_cpu(ehdr, lshdr.sh_info); + shdr->sh_addralign = elf32_to_cpu(ehdr, lshdr.sh_addralign); + shdr->sh_entsize = elf32_to_cpu(ehdr, lshdr.sh_entsize); + + /* Now verify sh_entsize */ + size_ok = 0; + switch(shdr->sh_type) { + case SHT_SYMTAB: + size_ok = shdr->sh_entsize == sizeof(Elf32_Sym); + break; + case SHT_RELA: + size_ok = shdr->sh_entsize == sizeof(Elf32_Rela); + break; + case SHT_DYNAMIC: + size_ok = shdr->sh_entsize == sizeof(Elf32_Dyn); + break; + case SHT_REL: + size_ok = shdr->sh_entsize == sizeof(Elf32_Rel); + break; + case SHT_NOTE: + case SHT_NULL: + case SHT_PROGBITS: + case SHT_HASH: + case SHT_NOBITS: + default: + /* This is a section whose entsize requirements + * I don't care about. If I don't know about + * the section I can't care about it's entsize + * requirements. + */ + size_ok = 1; + break; + } + if (!size_ok) { + fprintf(stderr, "Bad section header(%x) entsize: %lld\n", + shdr->sh_type, shdr->sh_entsize); + return -1; + } + return 0; +} + +static int build_mem_elf64_shdr(const char *buf, struct mem_ehdr *ehdr, int idx) +{ + struct mem_shdr *shdr; + const char *sbuf; + int size_ok; + Elf64_Shdr lshdr; + sbuf = buf + ehdr->e_shoff + (idx * sizeof(lshdr)); + shdr = &ehdr->e_shdr[idx]; + memcpy(&lshdr, sbuf, sizeof(lshdr)); + + if ( (elf64_to_cpu(ehdr, lshdr.sh_flags) > UINT64_MAX) || + (elf64_to_cpu(ehdr, lshdr.sh_addr) > UINT64_MAX) || + (elf64_to_cpu(ehdr, lshdr.sh_offset) > UINT64_MAX) || + (elf64_to_cpu(ehdr, lshdr.sh_size) > UINT64_MAX) || + (elf64_to_cpu(ehdr, lshdr.sh_addralign) > UINT64_MAX) || + (elf64_to_cpu(ehdr, lshdr.sh_entsize) > UINT64_MAX)) + { + fprintf(stderr, "Program section size out of range\n"); + return -1; + } + + shdr->sh_name = elf32_to_cpu(ehdr, lshdr.sh_name); + shdr->sh_type = elf32_to_cpu(ehdr, lshdr.sh_type); + shdr->sh_flags = elf64_to_cpu(ehdr, lshdr.sh_flags); + shdr->sh_addr = elf64_to_cpu(ehdr, lshdr.sh_addr); + shdr->sh_offset = elf64_to_cpu(ehdr, lshdr.sh_offset); + shdr->sh_size = elf64_to_cpu(ehdr, lshdr.sh_size); + shdr->sh_link = elf32_to_cpu(ehdr, lshdr.sh_link); + shdr->sh_info = elf32_to_cpu(ehdr, lshdr.sh_info); + shdr->sh_addralign = elf64_to_cpu(ehdr, lshdr.sh_addralign); + shdr->sh_entsize = elf64_to_cpu(ehdr, lshdr.sh_entsize); + + /* Now verify sh_entsize */ + size_ok = 0; + switch(shdr->sh_type) { + case SHT_SYMTAB: + size_ok = shdr->sh_entsize == sizeof(Elf64_Sym); + break; + case SHT_RELA: + size_ok = shdr->sh_entsize == sizeof(Elf64_Rela); + break; + case SHT_DYNAMIC: + size_ok = shdr->sh_entsize == sizeof(Elf64_Dyn); + break; + case SHT_REL: + size_ok = shdr->sh_entsize == sizeof(Elf64_Rel); + break; + case SHT_NOTE: + case SHT_NULL: + case SHT_PROGBITS: + case SHT_HASH: + case SHT_NOBITS: + default: + /* This is a section whose entsize requirements + * I don't care about. If I don't know about + * the section I can't care about it's entsize + * requirements. + */ + size_ok = 1; + break; + } + if (!size_ok) { + fprintf(stderr, "Bad section header(%x) entsize: %lld\n", + shdr->sh_type, shdr->sh_entsize); + return -1; + } + return 0; +} + +static int build_mem_shdrs(const char *buf, off_t len, struct mem_ehdr *ehdr, + uint32_t flags) +{ + size_t shdr_size, mem_shdr_size, i; + + /* e_shnum is at most 65536 so calculating + * the size of the section header cannot overflow. + */ + /* Is the program header in the file buffer? */ + shdr_size = 0; + if (ehdr->ei_class == ELFCLASS32) { + shdr_size = sizeof(Elf32_Shdr); + } + else if (ehdr->ei_class == ELFCLASS64) { + shdr_size = sizeof(Elf64_Shdr); + } + else { + fprintf(stderr, "Invalid ei_class?\n"); + return -1; + } + shdr_size *= ehdr->e_shnum; + if ((uintmax_t)(ehdr->e_shoff + shdr_size) > (uintmax_t)len) { + /* The section header did not fit in the file buffer */ + if (probe_debug) { + fprintf(stderr, "ELF section header does not fit in file\n"); + } + return -1; + } + + /* Allocate the e_shdr array */ + mem_shdr_size = sizeof(ehdr->e_shdr[0]) * ehdr->e_shnum; + ehdr->e_shdr = xmalloc(mem_shdr_size); + + for(i = 0; i < ehdr->e_shnum; i++) { + struct mem_shdr *shdr; + int result; + result = -1; + if (ehdr->ei_class == ELFCLASS32) { + result = build_mem_elf32_shdr(buf, ehdr, i); + } + else if (ehdr->ei_class == ELFCLASS64) { + result = build_mem_elf64_shdr(buf, ehdr, i); + } + if (result < 0) { + return result; + } + /* Check the section headers to be certain + * they are safe to use. + * Skip the check if ELF_SKIP_FILESZ_CHECK is set. + */ + shdr = &ehdr->e_shdr[i]; + if (!(flags & ELF_SKIP_FILESZ_CHECK) + && (shdr->sh_type != SHT_NOBITS) + && (uintmax_t)(shdr->sh_offset + shdr->sh_size) > + (uintmax_t)len) { + /* The section does not fit in the buffer */ + if (probe_debug) { + fprintf(stderr, "ELF section %zd not in file\n", + i); + } + return -1; + } + if ((shdr->sh_addr + shdr->sh_size) < shdr->sh_addr) { + /* The memory address wraps */ + if (probe_debug) { + fprintf(stderr, "ELF address wrap around\n"); + } + return -1; + } + /* Remember where the section lives in the buffer */ + shdr->sh_data = (unsigned char *)(buf + shdr->sh_offset); + } + return 0; +} + +static void read_nhdr(const struct mem_ehdr *ehdr, + ElfNN_Nhdr *hdr, const unsigned char *note) +{ + memcpy(hdr, note, sizeof(*hdr)); + hdr->n_namesz = elf32_to_cpu(ehdr, hdr->n_namesz); + hdr->n_descsz = elf32_to_cpu(ehdr, hdr->n_descsz); + hdr->n_type = elf32_to_cpu(ehdr, hdr->n_type); + +} +static int build_mem_notes(struct mem_ehdr *ehdr) +{ + const unsigned char *note_start, *note_end, *note; + size_t note_size, i; + /* First find the note segment or section */ + note_start = note_end = NULL; + for(i = 0; !note_start && (i < ehdr->e_phnum); i++) { + struct mem_phdr *phdr = &ehdr->e_phdr[i]; + /* + * binutils <= 2.17 has a bug where it can create the + * PT_NOTE segment with an offset of 0. Therefore + * check p_offset > 0. + * + * See: http://sourceware.org/bugzilla/show_bug.cgi?id=594 + */ + if (phdr->p_type == PT_NOTE && phdr->p_offset) { + note_start = (unsigned char *)phdr->p_data; + note_end = note_start + phdr->p_filesz; + } + } + for(i = 0; !note_start && (i < ehdr->e_shnum); i++) { + struct mem_shdr *shdr = &ehdr->e_shdr[i]; + if (shdr->sh_type == SHT_NOTE) { + note_start = shdr->sh_data; + note_end = note_start + shdr->sh_size; + } + } + if (!note_start) { + return 0; + } + + /* Walk through and count the notes */ + ehdr->e_notenum = 0; + for(note = note_start; note < note_end; note+= note_size) { + ElfNN_Nhdr hdr; + read_nhdr(ehdr, &hdr, note); + note_size = sizeof(hdr); + note_size += _ALIGN(hdr.n_namesz, 4); + note_size += _ALIGN(hdr.n_descsz, 4); + ehdr->e_notenum += 1; + } + /* Now walk and normalize the notes */ + ehdr->e_note = xmalloc(sizeof(*ehdr->e_note) * ehdr->e_notenum); + for(i = 0, note = note_start; note < note_end; note+= note_size, i++) { + const unsigned char *name, *desc; + ElfNN_Nhdr hdr; + read_nhdr(ehdr, &hdr, note); + note_size = sizeof(hdr); + name = note + note_size; + note_size += _ALIGN(hdr.n_namesz, 4); + desc = note + note_size; + note_size += _ALIGN(hdr.n_descsz, 4); + + if (((note+note_size) > note_end) || + ((note+note_size) < note_start)) { + /* Something is very wrong here ! Most likely the note + * header is invalid */ + fprintf(stderr, "ELF Note corrupted !\n"); + return -1; + } + + if ((hdr.n_namesz != 0) && (name[hdr.n_namesz -1] != '\0')) { + /* If note name string is not null terminated, just + * warn user about it and continue processing. This + * allows us to parse /proc/kcore on older kernels + * where /proc/kcore elf notes were not null + * terminated. It has been fixed in 2.6.19. + */ + fprintf(stderr, "Warning: Elf Note name is not null " + "terminated\n"); + } + ehdr->e_note[i].n_type = hdr.n_type; + ehdr->e_note[i].n_name = (char *)name; + ehdr->e_note[i].n_desc = desc; + ehdr->e_note[i].n_descsz = hdr.n_descsz; + + } + return 0; +} + +void free_elf_info(struct mem_ehdr *ehdr) +{ + free(ehdr->e_phdr); + free(ehdr->e_shdr); + memset(ehdr, 0, sizeof(*ehdr)); +} + +int build_elf_info(const char *buf, off_t len, struct mem_ehdr *ehdr, + uint32_t flags) +{ + int result; + result = build_mem_ehdr(buf, len, ehdr); + if (result < 0) { + return result; + } + if ((ehdr->e_phoff > 0) && (ehdr->e_phnum > 0)) { + result = build_mem_phdrs(buf, len, ehdr, flags); + if (result < 0) { + free_elf_info(ehdr); + return result; + } + } + if ((ehdr->e_shoff > 0) && (ehdr->e_shnum > 0)) { + result = build_mem_shdrs(buf, len, ehdr, flags); + if (result < 0) { + free_elf_info(ehdr); + return result; + } + } + result = build_mem_notes(ehdr); + if (result < 0) { + free_elf_info(ehdr); + return result; + } + return 0; +} + diff --git a/kexec/kexec-elf.h b/kexec/kexec-elf.h new file mode 100644 index 0000000..1e512c8 --- /dev/null +++ b/kexec/kexec-elf.h @@ -0,0 +1,143 @@ +#ifndef KEXEC_ELF_H +#define KEXEC_ELF_H + +#include <stdint.h> +#include <sys/types.h> + +struct kexec_info; + +struct mem_ehdr { + unsigned ei_class; + unsigned ei_data; + unsigned e_type; + unsigned e_machine; + unsigned e_version; + unsigned e_flags; + unsigned e_phnum; + unsigned e_shnum; + unsigned e_shstrndx; + unsigned long long e_entry; + unsigned long long e_phoff; + unsigned long long e_shoff; + unsigned e_notenum; + struct mem_phdr *e_phdr; + struct mem_shdr *e_shdr; + struct mem_note *e_note; + unsigned long rel_addr, rel_size; +}; + +struct mem_phdr { + unsigned long long p_paddr; + unsigned long long p_vaddr; + unsigned long long p_filesz; + unsigned long long p_memsz; + unsigned long long p_offset; + const char *p_data; + unsigned p_type; + unsigned p_flags; + unsigned long long p_align; +}; + +struct mem_shdr { + unsigned sh_name; + unsigned sh_type; + unsigned long long sh_flags; + unsigned long long sh_addr; + unsigned long long sh_offset; + unsigned long long sh_size; + unsigned sh_link; + unsigned sh_info; + unsigned long long sh_addralign; + unsigned long long sh_entsize; + const unsigned char *sh_data; +}; + +struct mem_sym { + unsigned long st_name; /* Symbol name (string tbl index) */ + unsigned char st_info; /* No defined meaning, 0 */ + unsigned char st_other; /* Symbol type and binding */ + unsigned st_shndx; /* Section index */ + unsigned long long st_value; /* Symbol value */ + unsigned long long st_size; /* Symbol size */ +}; + +struct mem_rela { + unsigned long long r_offset; + unsigned r_sym; + unsigned r_type; + unsigned long long r_addend; +}; + +struct mem_note { + unsigned n_type; + unsigned n_descsz; + const char *n_name; + const void *n_desc; +}; + +/* The definition of an ELF note does not vary depending + * on ELFCLASS. + */ +typedef struct +{ + uint32_t n_namesz; /* Length of the note's name. */ + uint32_t n_descsz; /* Length of the note's descriptor. */ + uint32_t n_type; /* Type of the note. */ +} ElfNN_Nhdr; + +/* Misc flags */ + +#define ELF_SKIP_FILESZ_CHECK 0x00000001 + +extern void free_elf_info(struct mem_ehdr *ehdr); +extern int build_elf_info(const char *buf, off_t len, struct mem_ehdr *ehdr, + uint32_t flags); +extern int build_elf_exec_info(const char *buf, off_t len, + struct mem_ehdr *ehdr, uint32_t flags); +extern int build_elf_rel_info(const char *buf, off_t len, struct mem_ehdr *ehdr, + uint32_t flags); + +extern int build_elf_core_info(const char *buf, off_t len, + struct mem_ehdr *ehdr, uint32_t flags); +extern int elf_exec_load(struct mem_ehdr *ehdr, struct kexec_info *info); +extern int elf_exec_load_relocatable(struct mem_ehdr *ehdr, struct kexec_info *info, + unsigned long reloc_min, unsigned long reloc_max, + unsigned long align); +extern int elf_rel_load(struct mem_ehdr *ehdr, struct kexec_info *info, + unsigned long min, unsigned long max, int end); + +extern void elf_exec_build_load(struct kexec_info *info, struct mem_ehdr *ehdr, + const char *buf, off_t len, uint32_t flags); +extern void elf_exec_build_load_relocatable(struct kexec_info *info, struct mem_ehdr *ehdr, + const char *buf, off_t len, uint32_t flags, + unsigned long reloc_min, unsigned long reloc_max, + unsigned long align); +extern void elf_rel_build_load(struct kexec_info *info, struct mem_ehdr *ehdr, + const char *buf, off_t len, unsigned long min, unsigned long max, + int end, uint32_t flags); + +extern int elf_rel_find_symbol(struct mem_ehdr *ehdr, + const char *name, struct mem_sym *ret_sym); +extern unsigned long elf_rel_get_addr(struct mem_ehdr *ehdr, const char *name); +extern void elf_rel_set_symbol(struct mem_ehdr *ehdr, + const char *name, const void *buf, size_t size); +extern void elf_rel_get_symbol(struct mem_ehdr *ehdr, + const char *name, void *buf, size_t size); + +uint16_t elf16_to_cpu(const struct mem_ehdr *ehdr, uint16_t value); +uint32_t elf32_to_cpu(const struct mem_ehdr *ehdr, uint32_t value); +uint64_t elf64_to_cpu(const struct mem_ehdr *ehdr, uint64_t value); + +uint16_t cpu_to_elf16(const struct mem_ehdr *ehdr, uint16_t value); +uint32_t cpu_to_elf32(const struct mem_ehdr *ehdr, uint32_t value); +uint64_t cpu_to_elf64(const struct mem_ehdr *ehdr, uint64_t value); + +unsigned long elf_max_addr(const struct mem_ehdr *ehdr); + +/* Architecture specific helper functions */ +extern int machine_verify_elf_rel(struct mem_ehdr *ehdr); +extern void machine_apply_elf_rel(struct mem_ehdr *ehdr, struct mem_sym *sym, + unsigned long r_type, void *location, unsigned long address, + unsigned long value); +#endif /* KEXEC_ELF_H */ + diff --git a/kexec/kexec-iomem.c b/kexec/kexec-iomem.c new file mode 100644 index 0000000..d00b6b6 --- /dev/null +++ b/kexec/kexec-iomem.c @@ -0,0 +1,109 @@ +#define _GNU_SOURCE +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <limits.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> +#include "kexec.h" +#include "crashdump.h" + +/* + * kexec_iomem_for_each_line() + * + * Iterate over each line in the file returned by proc_iomem(). If match is + * NULL or if the line matches with our match-pattern then call the + * callback if non-NULL. + * If match is NULL, callback should return a negative if error. + * Otherwise the interation goes on, incrementing nr but only if callback + * returns 0 (matched). + * + * Return the number of lines matched. + */ + +int kexec_iomem_for_each_line(char *match, + int (*callback)(void *data, + int nr, + char *str, + unsigned long long base, + unsigned long long length), + void *data) +{ + const char *iomem = proc_iomem(); + char line[MAX_LINE]; + FILE *fp; + unsigned long long start, end, size; + char *str; + int consumed; + int count; + int nr = 0, ret; + + if (!callback) + return nr; + + fp = fopen(iomem, "r"); + if (!fp) + die("Cannot open %s\n", iomem); + + while(fgets(line, sizeof(line), fp) != 0) { + count = sscanf(line, "%llx-%llx : %n", &start, &end, &consumed); + if (count != 2) + continue; + str = line + consumed; + size = end - start + 1; + if (!match || memcmp(str, match, strlen(match)) == 0) { + ret = callback(data, nr, str, start, size); + if (ret < 0) + break; + else if (ret == 0) + nr++; + } + } + + fclose(fp); + + return nr; +} + +static int kexec_iomem_single_callback(void *data, int nr, + char *UNUSED(str), + unsigned long long base, + unsigned long long length) +{ + struct memory_range *range = data; + + if (nr == 0) { + range->start = base; + range->end = base + length - 1; + } + + return 0; +} + +int parse_iomem_single(char *str, uint64_t *start, uint64_t *end) +{ + struct memory_range range; + int ret; + + memset(&range, 0, sizeof(range)); + + ret = kexec_iomem_for_each_line(str, + kexec_iomem_single_callback, &range); + + if (ret == 1) { + if (start) + *start = range.start; + if (end) + *end = range.end; + + ret = 0; + } + else + ret = -1; + + return ret; +} diff --git a/kexec/kexec-lzma.h b/kexec/kexec-lzma.h new file mode 100644 index 0000000..d3b751a --- /dev/null +++ b/kexec/kexec-lzma.h @@ -0,0 +1,8 @@ +#ifndef __KEXEC_LZMA_H +#define __KEXEC_LZMA_H + +#include <sys/types.h> + +char *lzma_decompress_file(const char *filename, off_t *r_size); + +#endif /* __KEXEC_LZMA_H */ diff --git a/kexec/kexec-pe-zboot.c b/kexec/kexec-pe-zboot.c new file mode 100644 index 0000000..2f2e052 --- /dev/null +++ b/kexec/kexec-pe-zboot.c @@ -0,0 +1,131 @@ +/* + * Generic PE compressed Image (vmlinuz, ZBOOT) support. + * + * Several distros use 'make zinstall' with CONFIG_ZBOOT + * enabled to create UEFI PE images that contain + * a decompressor and a compressed kernel image. + * + * Currently we cannot use kexec_file_load() to load vmlinuz + * PE images that self decompress. + * + * To support ZBOOT, we should: + * a). Copy the compressed contents of vmlinuz to a temporary file. + * b). Decompress (gunzip-decompress) the contents inside the + * temporary file. + * c). Validate the resulting image and write it back to the + * temporary file. + * d). Pass the 'fd' of the temporary file to the kernel space. + * + * This module contains the arch independent code for the above, + * arch specific PE and image checks should wrap calls + * to functions in this module. + */ + +#define _GNU_SOURCE +#include <stdlib.h> +#include <stdint.h> +#include <sys/types.h> +#include <unistd.h> +#include <fcntl.h> +#include "kexec.h" +#include <kexec-pe-zboot.h> + +#define FILENAME_IMAGE "/tmp/ImageXXXXXX" + +/* + * Returns -1 : in case of error/invalid format (not a valid PE+compressed ZBOOT format. + * + * crude_buf: the content, which is read from the kernel file without any processing + */ +int pez_prepare(const char *crude_buf, off_t buf_sz, int *kernel_fd) +{ + int ret = -1; + int fd = 0; + char *fname = NULL; + char *kernel_uncompressed_buf = NULL; + off_t decompressed_size = 0; + const struct linux_pe_zboot_header *z; + + z = (const struct linux_pe_zboot_header *)(crude_buf); + + if (memcmp(&z->image_type, "zimg", sizeof(z->image_type))) { + dbgprintf("%s: PE doesn't contain a compressed kernel.\n", __func__); + return -1; + } + + /* + * At the moment its possible to create images with more compression + * algorithms than are supported here, error out if we detect that. + */ + if (memcmp(&z->compress_type, "gzip", 4) && + memcmp(&z->compress_type, "lzma", 4)) { + dbgprintf("%s: kexec can only decompress gziped and lzma images.\n", __func__); + return -1; + } + + if (buf_sz < z->payload_offset + z->payload_size) { + dbgprintf("%s: PE too small to contain complete payload.\n", __func__); + return -1; + } + + if (!(fname = strdup(FILENAME_IMAGE))) { + dbgprintf("%s: Can't duplicate strings\n", __func__); + return -1; + } + + if ((fd = mkstemp(fname)) < 0) { + dbgprintf("%s: Can't open file %s\n", __func__, fname); + ret = -1; + goto fail_mkstemp; + } + + if (write(fd, &crude_buf[z->payload_offset], + z->payload_size) != z->payload_size) { + dbgprintf("%s: Can't write the compressed file %s\n", + __func__, fname); + ret = -1; + goto fail_write; + } + + kernel_uncompressed_buf = slurp_decompress_file(fname, + &decompressed_size); + + dbgprintf("%s: decompressed size %ld\n", __func__, decompressed_size); + + lseek(fd, 0, SEEK_SET); + + if (write(fd, kernel_uncompressed_buf, + decompressed_size) != decompressed_size) { + dbgprintf("%s: Can't write the decompressed file %s\n", + __func__, fname); + ret = -1; + goto fail_bad_header; + } + + *kernel_fd = open(fname, O_RDONLY); + if (*kernel_fd == -1) { + dbgprintf("%s: Failed to open file %s\n", + __func__, fname); + ret = -1; + goto fail_bad_header; + } + + dbgprintf("%s: done\n", __func__); + + ret = 0; + goto fail_write; + +fail_bad_header: + free(kernel_uncompressed_buf); + +fail_write: + if (fd >= 0) + close(fd); + + unlink(fname); + +fail_mkstemp: + free(fname); + + return ret; +} diff --git a/kexec/kexec-sha256.h b/kexec/kexec-sha256.h new file mode 100644 index 0000000..b4bb856 --- /dev/null +++ b/kexec/kexec-sha256.h @@ -0,0 +1,11 @@ +#ifndef KEXEC_SHA256_H +#define KEXEC_SHA256_H + +struct sha256_region { + uint64_t start; + uint64_t len; +}; + +#define SHA256_REGIONS 16 + +#endif /* KEXEC_SHA256_H */ diff --git a/kexec/kexec-syscall.h b/kexec/kexec-syscall.h new file mode 100644 index 0000000..1e2d12f --- /dev/null +++ b/kexec/kexec-syscall.h @@ -0,0 +1,188 @@ +#ifndef KEXEC_SYSCALL_H +#define KEXEC_SYSCALL_H + +#define __LIBRARY__ +#include <sys/syscall.h> +#include <unistd.h> + +#define LINUX_REBOOT_CMD_KEXEC_OLD 0x81726354 +#define LINUX_REBOOT_CMD_KEXEC_OLD2 0x18263645 +#define LINUX_REBOOT_CMD_KEXEC 0x45584543 + +#ifndef __NR_kexec_load +#ifdef __i386__ +#define __NR_kexec_load 283 +#endif +#ifdef __sh__ +#define __NR_kexec_load 283 +#endif +#ifdef __cris__ +#ifndef __NR_kexec_load +#define __NR_kexec_load 283 +#endif +#endif +#ifdef __ia64__ +#define __NR_kexec_load 1268 +#endif +#ifdef __powerpc64__ +#define __NR_kexec_load 268 +#endif +#ifdef __powerpc__ +#define __NR_kexec_load 268 +#endif +#ifdef __x86_64__ +#define __NR_kexec_load 246 +#endif +#ifdef __s390x__ +#define __NR_kexec_load 277 +#endif +#ifdef __s390__ +#define __NR_kexec_load 277 +#endif +#ifdef __loongarch__ +#define __NR_kexec_load 104 +#endif +#if defined(__arm__) || defined(__arm64__) +#define __NR_kexec_load __NR_SYSCALL_BASE + 347 +#endif +#if defined(__mips__) +#define __NR_kexec_load 4311 +#endif +#ifdef __m68k__ +#define __NR_kexec_load 313 +#endif +#ifdef __alpha__ +#define __NR_kexec_load 448 +#endif +#ifndef __NR_kexec_load +#error Unknown processor architecture. Needs a kexec_load syscall number. +#endif +#endif /*ifndef __NR_kexec_load*/ + +#if defined(__arm__) || defined(__loongarch__) +#undef __NR_kexec_file_load +#endif + +#ifndef __NR_kexec_file_load + +#ifdef __x86_64__ +#define __NR_kexec_file_load 320 +#endif +#ifdef __powerpc64__ +#define __NR_kexec_file_load 382 +#endif +#ifdef __s390x__ +#define __NR_kexec_file_load 381 +#endif +#ifdef __aarch64__ +#define __NR_kexec_file_load 294 +#endif +#ifdef __hppa__ +#define __NR_kexec_file_load 355 +#endif + +#ifndef __NR_kexec_file_load +/* system call not available for the arch */ +#define __NR_kexec_file_load 0xffffffff /* system call not available */ +#endif + +#endif /*ifndef __NR_kexec_file_load*/ + +struct kexec_segment; + +static inline long kexec_load(void *entry, unsigned long nr_segments, + struct kexec_segment *segments, unsigned long flags) +{ + return (long) syscall(__NR_kexec_load, entry, nr_segments, segments, flags); +} + +static inline int is_kexec_file_load_implemented(void) { + if (__NR_kexec_file_load != 0xffffffff) + return 1; + return 0; +} + +static inline long kexec_file_load(int kernel_fd, int initrd_fd, + unsigned long cmdline_len, const char *cmdline_ptr, + unsigned long flags) +{ + return (long) syscall(__NR_kexec_file_load, kernel_fd, initrd_fd, + cmdline_len, cmdline_ptr, flags); +} + +#define KEXEC_ON_CRASH 0x00000001 +#define KEXEC_PRESERVE_CONTEXT 0x00000002 +#define KEXEC_ARCH_MASK 0xffff0000 + +/* Flags for kexec file based system call */ +#define KEXEC_FILE_UNLOAD 0x00000001 +#define KEXEC_FILE_ON_CRASH 0x00000002 +#define KEXEC_FILE_NO_INITRAMFS 0x00000004 + +/* These values match the ELF architecture values. + * Unless there is a good reason that should continue to be the case. + */ +#define KEXEC_ARCH_DEFAULT ( 0 << 16) +#define KEXEC_ARCH_386 ( 3 << 16) +#define KEXEC_ARCH_68K ( 4 << 16) +#define KEXEC_ARCH_HPPA (15 << 16) +#define KEXEC_ARCH_X86_64 (62 << 16) +#define KEXEC_ARCH_PPC (20 << 16) +#define KEXEC_ARCH_PPC64 (21 << 16) +#define KEXEC_ARCH_IA_64 (50 << 16) +#define KEXEC_ARCH_ARM (40 << 16) +#define KEXEC_ARCH_ARM64 (183 << 16) +#define KEXEC_ARCH_S390 (22 << 16) +#define KEXEC_ARCH_SH (42 << 16) +#define KEXEC_ARCH_MIPS_LE (10 << 16) +#define KEXEC_ARCH_MIPS ( 8 << 16) +#define KEXEC_ARCH_CRIS (76 << 16) +#define KEXEC_ARCH_LOONGARCH (258 << 16) + +#define KEXEC_MAX_SEGMENTS 16 + +#ifdef __i386__ +#define KEXEC_ARCH_NATIVE KEXEC_ARCH_386 +#endif +#ifdef __sh__ +#define KEXEC_ARCH_NATIVE KEXEC_ARCH_SH +#endif +#ifdef __cris__ +#define KEXEC_ARCH_NATIVE KEXEC_ARCH_CRIS +#endif +#ifdef __ia64__ +#define KEXEC_ARCH_NATIVE KEXEC_ARCH_IA_64 +#endif +#ifdef __powerpc64__ +#define KEXEC_ARCH_NATIVE KEXEC_ARCH_PPC64 +#else + #ifdef __powerpc__ + #define KEXEC_ARCH_NATIVE KEXEC_ARCH_PPC + #endif +#endif +#ifdef __x86_64__ +#define KEXEC_ARCH_NATIVE KEXEC_ARCH_X86_64 +#endif +#ifdef __s390x__ +#define KEXEC_ARCH_NATIVE KEXEC_ARCH_S390 +#endif +#ifdef __s390__ +#define KEXEC_ARCH_NATIVE KEXEC_ARCH_S390 +#endif +#ifdef __arm__ +#define KEXEC_ARCH_NATIVE KEXEC_ARCH_ARM +#endif +#if defined(__mips__) +#define KEXEC_ARCH_NATIVE KEXEC_ARCH_MIPS +#endif +#ifdef __m68k__ +#define KEXEC_ARCH_NATIVE KEXEC_ARCH_68K +#endif +#if defined(__arm64__) +#define KEXEC_ARCH_NATIVE KEXEC_ARCH_ARM64 +#endif +#if defined(__loongarch__) +#define KEXEC_ARCH_NATIVE KEXEC_ARCH_LOONGARCH +#endif + +#endif /* KEXEC_SYSCALL_H */ diff --git a/kexec/kexec-uImage.c b/kexec/kexec-uImage.c new file mode 100644 index 0000000..016be10 --- /dev/null +++ b/kexec/kexec-uImage.c @@ -0,0 +1,283 @@ +#define _GNU_SOURCE +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <sys/types.h> +#include <image.h> +#include <getopt.h> +#include <arch/options.h> +#include "kexec.h" +#include <kexec-uImage.h> + +#ifdef HAVE_LIBZ +#include <zlib.h> +#endif +/* + * Basic uImage loader. Not rocket science. + */ + +/* + * Returns the image type if everything goes well. This would + * allow the user to decide if the image is of their interest. + * + * Returns -1 on a corrupted image + * + * Returns 0 if this is not a uImage + */ +int uImage_probe(const char *buf, off_t len, unsigned int arch) +{ + struct image_header header; +#ifdef HAVE_LIBZ + unsigned int crc; + unsigned int hcrc; +#endif + + if ((uintmax_t)len < (uintmax_t)sizeof(header)) + return -1; + + memcpy(&header, buf, sizeof(header)); + if (be32_to_cpu(header.ih_magic) != IH_MAGIC) + return 0; +#ifdef HAVE_LIBZ + hcrc = be32_to_cpu(header.ih_hcrc); + header.ih_hcrc = 0; + crc = crc32(0, (void *)&header, sizeof(header)); + if (crc != hcrc) { + printf("Header checksum of the uImage does not match\n"); + return -1; + } +#endif + switch (header.ih_type) { + case IH_TYPE_KERNEL: + case IH_TYPE_KERNEL_NOLOAD: + break; + case IH_TYPE_RAMDISK: + break; + default: + printf("uImage type %d unsupported\n", header.ih_type); + return -1; + } + + if (header.ih_os != IH_OS_LINUX) { + printf("uImage os %d unsupported\n", header.ih_os); + return -1; + } + + if (header.ih_arch != arch) { + printf("uImage arch %d unsupported\n", header.ih_arch); + return -1; + } + + switch (header.ih_comp) { + case IH_COMP_NONE: +#ifdef HAVE_LIBZ + case IH_COMP_GZIP: +#endif + break; + default: + printf("uImage uses unsupported compression method\n"); + return -1; + } + + if (be32_to_cpu(header.ih_size) > len - sizeof(header)) { + printf("uImage header claims that image has %d bytes\n", + be32_to_cpu(header.ih_size)); + printf("we read only %lld bytes.\n", + (long long)len - sizeof(header)); + return -1; + } +#ifdef HAVE_LIBZ + crc = crc32(0, (void *)buf + sizeof(header), be32_to_cpu(header.ih_size)); + if (crc != be32_to_cpu(header.ih_dcrc)) { + printf("uImage: The data CRC does not match. Computed: %08x " + "expected %08x\n", crc, + be32_to_cpu(header.ih_dcrc)); + return -1; + } +#endif + return (int)header.ih_type; +} + +/* + * To conform to the 'probe' routine in file_type struct, + * we return : + * 0 - If the image is valid 'type' image. + * + * Now, we have to pass on the 'errors' in the image. So, + * + * -1 - If the image is corrupted. + * 1 - If the image is not a uImage. + */ + +int uImage_probe_kernel(const char *buf, off_t len, unsigned int arch) +{ + int type = uImage_probe(buf, len, arch); + if (type < 0) + return -1; + + return !(type == IH_TYPE_KERNEL || type == IH_TYPE_KERNEL_NOLOAD); +} + +int uImage_probe_ramdisk(const char *buf, off_t len, unsigned int arch) +{ + int type = uImage_probe(buf, len, arch); + + if (type < 0) + return -1; + return !(type == IH_TYPE_RAMDISK); +} + +#ifdef HAVE_LIBZ +/* gzip flag byte */ +#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ +#define HEAD_CRC 0x02 /* bit 1 set: header CRC present */ +#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ +#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ +#define COMMENT 0x10 /* bit 4 set: file comment present */ +#define RESERVED 0xE0 /* bits 5..7: reserved */ + +static int uImage_gz_load(const char *buf, off_t len, + struct Image_info *image) +{ + int ret; + z_stream strm; + unsigned int skip; + unsigned int flags; + unsigned char *uncomp_buf; + unsigned int mem_alloc; + + mem_alloc = 10 * 1024 * 1024; + uncomp_buf = malloc(mem_alloc); + if (!uncomp_buf) + return -1; + + memset(&strm, 0, sizeof(strm)); + + /* Skip magic, method, time, flags, os code ... */ + skip = 10; + + /* check GZ magic */ + if (buf[0] != 0x1f || buf[1] != 0x8b) { + free(uncomp_buf); + return -1; + } + + flags = buf[3]; + if (buf[2] != Z_DEFLATED || (flags & RESERVED) != 0) { + puts ("Error: Bad gzipped data\n"); + free(uncomp_buf); + return -1; + } + + if (flags & EXTRA_FIELD) { + skip += 2; + skip += buf[10]; + skip += buf[11] << 8; + } + if (flags & ORIG_NAME) { + while (buf[skip++]) + ; + } + if (flags & COMMENT) { + while (buf[skip++]) + ; + } + if (flags & HEAD_CRC) + skip += 2; + + strm.avail_in = len - skip; + strm.next_in = (void *)buf + skip; + + /* - activates parsing gz headers */ + ret = inflateInit2(&strm, -MAX_WBITS); + if (ret != Z_OK) { + free(uncomp_buf); + return -1; + } + + strm.next_out = uncomp_buf; + strm.avail_out = mem_alloc; + + do { + ret = inflate(&strm, Z_FINISH); + if (ret == Z_STREAM_END) + break; + + if (ret == Z_OK || ret == Z_BUF_ERROR) { + void *new_buf; + int inc_buf = 5 * 1024 * 1024; + + mem_alloc += inc_buf; + new_buf = realloc(uncomp_buf, mem_alloc); + if (!new_buf) { + inflateEnd(&strm); + free(uncomp_buf); + return -1; + } + + uncomp_buf = new_buf; + strm.next_out = uncomp_buf + mem_alloc - inc_buf; + strm.avail_out = inc_buf; + } else { + free(uncomp_buf); + printf("Error during decompression %d\n", ret); + return -1; + } + } while (1); + + inflateEnd(&strm); + image->buf = (char *)uncomp_buf; + image->len = mem_alloc - strm.avail_out; + return 0; +} +#else +static int uImage_gz_load(const char *UNUSED(buf), off_t UNUSED(len), + struct Image_info *UNUSED(image)) +{ + return -1; +} +#endif + +int uImage_load(const char *buf, off_t len, struct Image_info *image) +{ + const struct image_header *header = (const struct image_header *)buf; + const char *img_buf = buf + sizeof(struct image_header); + off_t img_len = be32_to_cpu(header->ih_size); + + /* + * Prevent loading a modified image. + * CRC check is perfomed only when zlib is compiled + * in. This check will help us to detect + * size related vulnerabilities. + */ + if (img_len != (len - sizeof(struct image_header))) { + printf("Image size doesn't match the header\n"); + return -1; + } + + image->base = cpu_to_be32(header->ih_load); + image->ep = cpu_to_be32(header->ih_ep); + switch (header->ih_comp) { + case IH_COMP_NONE: + image->buf = img_buf; + image->len = img_len; + return 0; + break; + + case IH_COMP_GZIP: + /* + * uboot doesn't decompress the RAMDISK images. + * Comply to the uboot behaviour. + */ + if (header->ih_type == IH_TYPE_RAMDISK) { + image->buf = img_buf; + image->len = img_len; + return 0; + } else + return uImage_gz_load(img_buf, img_len, image); + break; + + default: + return -1; + } +} diff --git a/kexec/kexec-xen.c b/kexec/kexec-xen.c new file mode 100644 index 0000000..a7c8933 --- /dev/null +++ b/kexec/kexec-xen.c @@ -0,0 +1,297 @@ +#define _GNU_SOURCE +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <elf.h> +#include "kexec.h" +#include "kexec-syscall.h" +#include "crashdump.h" + +#include "config.h" + +#ifdef HAVE_LIBXENCTRL +#include "kexec-xen.h" + +#include "crashdump.h" + +#ifdef CONFIG_LIBXENCTRL_DL +#include <dlfcn.h> + +/* The handle from dlopen(), needed by dlsym(), dlclose() */ +static void *xc_dlhandle; +xc_hypercall_buffer_t XC__HYPERCALL_BUFFER_NAME(HYPERCALL_BUFFER_NULL); + +void *__xc_dlsym(const char *symbol) +{ + return dlsym(xc_dlhandle, symbol); +} + +xc_interface *__xc_interface_open(xentoollog_logger *logger, + xentoollog_logger *dombuild_logger, + unsigned open_flags) +{ + xc_interface *xch = NULL; + + if (!xc_dlhandle) + xc_dlhandle = dlopen("libxenctrl.so", RTLD_NOW | RTLD_NODELETE); + + if (xc_dlhandle) { + typedef xc_interface *(*func_t)(xentoollog_logger *logger, + xentoollog_logger *dombuild_logger, + unsigned open_flags); + + func_t func = (func_t)dlsym(xc_dlhandle, "xc_interface_open"); + xch = func(logger, dombuild_logger, open_flags); + } + + return xch; +} + +int __xc_interface_close(xc_interface *xch) +{ + int rc = -1; + + if (xc_dlhandle) { + typedef int (*func_t)(xc_interface *xch); + + func_t func = (func_t)dlsym(xc_dlhandle, "xc_interface_close"); + rc = func(xch); + dlclose(xc_dlhandle); + xc_dlhandle = NULL; + } + + return rc; +} +#endif /* CONFIG_LIBXENCTRL_DL */ + +int xen_get_kexec_range(int range, uint64_t *start, uint64_t *end) +{ + uint64_t size; + xc_interface *xc; + int rc = -1; + + xc = xc_interface_open(NULL, NULL, 0); + if (!xc) { + fprintf(stderr, "failed to open xen control interface.\n"); + goto out; + } + + rc = xc_kexec_get_range(xc, range, 0, &size, start); + if (rc < 0) { + fprintf(stderr, "failed to get range=%d from hypervisor.\n", range); + goto out_close; + } + + *end = *start + size - 1; + +out_close: + xc_interface_close(xc); + +out: + return rc; +} + +static uint8_t xen_get_kexec_type(unsigned long kexec_flags) +{ + if (kexec_flags & KEXEC_ON_CRASH) + return KEXEC_TYPE_CRASH; + + if (kexec_flags & KEXEC_LIVE_UPDATE) + return KEXEC_TYPE_LIVE_UPDATE; + + return KEXEC_TYPE_DEFAULT; +} + +#define IDENTMAP_1MiB (1024 * 1024) + +int xen_kexec_load(struct kexec_info *info) +{ + uint32_t nr_segments = info->nr_segments, nr_low_segments = 0; + struct kexec_segment *segments = info->segment; + uint64_t low_watermark = 0; + xc_interface *xch; + xc_hypercall_buffer_array_t *array = NULL; + uint8_t type; + uint8_t arch; + xen_kexec_segment_t *xen_segs, *seg; + int s; + int ret = -1; + + xch = xc_interface_open(NULL, NULL, 0); + if (!xch) + return -1; + + /* + * Ensure 0 - 1 MiB is mapped and accessible by the image. + * This allows access to the VGA memory and the region + * purgatory copies in the crash case. + * + * First, count the number of additional segments which will + * need to be added in between the ones in segments[]. + * + * The segments are already sorted. + */ + for (s = 0; s < nr_segments && (uint64_t)segments[s].mem <= IDENTMAP_1MiB; s++) { + if ((uint64_t)segments[s].mem > low_watermark) + nr_low_segments++; + + low_watermark = (uint64_t)segments[s].mem + segments[s].memsz; + } + if (low_watermark < IDENTMAP_1MiB) + nr_low_segments++; + + low_watermark = 0; + + xen_segs = calloc(nr_segments + nr_low_segments, sizeof(*xen_segs)); + if (!xen_segs) + goto out; + + array = xc_hypercall_buffer_array_create(xch, nr_segments); + if (array == NULL) + goto out; + + seg = xen_segs; + for (s = 0; s < nr_segments; s++) { + DECLARE_HYPERCALL_BUFFER(void, seg_buf); + + if (low_watermark < IDENTMAP_1MiB && (uint64_t)segments[s].mem > low_watermark) { + set_xen_guest_handle(seg->buf.h, HYPERCALL_BUFFER_NULL); + seg->buf_size = 0; + seg->dest_maddr = low_watermark; + low_watermark = (uint64_t)segments[s].mem; + if (low_watermark > IDENTMAP_1MiB) + low_watermark = IDENTMAP_1MiB; + seg->dest_size = low_watermark - seg->dest_maddr; + seg++; + } + + seg_buf = xc_hypercall_buffer_array_alloc(xch, array, s, + seg_buf, segments[s].bufsz); + if (seg_buf == NULL) + goto out; + memcpy(seg_buf, segments[s].buf, segments[s].bufsz); + + set_xen_guest_handle(seg->buf.h, seg_buf); + seg->buf_size = segments[s].bufsz; + seg->dest_maddr = (uint64_t)segments[s].mem; + seg->dest_size = segments[s].memsz; + seg++; + + low_watermark = (uint64_t)segments[s].mem + segments[s].memsz; + } + + if ((uint64_t)low_watermark < IDENTMAP_1MiB) { + set_xen_guest_handle(seg->buf.h, HYPERCALL_BUFFER_NULL); + seg->buf_size = 0; + seg->dest_maddr = low_watermark; + seg->dest_size = IDENTMAP_1MiB - low_watermark; + seg++; + } + + type = xen_get_kexec_type(info->kexec_flags); + + arch = (info->kexec_flags & KEXEC_ARCH_MASK) >> 16; +#if defined(__i386__) || defined(__x86_64__) + if (!arch) + arch = EM_386; +#endif + + ret = xc_kexec_load(xch, type, arch, (uint64_t)info->entry, + nr_segments + nr_low_segments, xen_segs); + +out: + xc_hypercall_buffer_array_destroy(xch, array); + free(xen_segs); + xc_interface_close(xch); + + return ret; +} + +int xen_kexec_unload(uint64_t kexec_flags) +{ + xc_interface *xch; + uint8_t type; + int ret; + + xch = xc_interface_open(NULL, NULL, 0); + if (!xch) + return -1; + + type = xen_get_kexec_type(kexec_flags); + + ret = xc_kexec_unload(xch, type); + + xc_interface_close(xch); + + return ret; +} + +int xen_kexec_status(uint64_t kexec_flags) +{ + xc_interface *xch; + uint8_t type; + int ret = -1; + +#ifdef HAVE_KEXEC_CMD_STATUS + xch = xc_interface_open(NULL, NULL, 0); + if (!xch) + return -1; + + type = xen_get_kexec_type(kexec_flags); + + ret = xc_kexec_status(xch, type); + + xc_interface_close(xch); +#endif + + return ret; +} + +int xen_kexec_exec(uint64_t kexec_flags) +{ + xc_interface *xch; + uint8_t type = KEXEC_TYPE_DEFAULT; + int ret; + + xch = xc_interface_open(NULL, NULL, 0); + if (!xch) + return -1; + + if (kexec_flags & KEXEC_LIVE_UPDATE) + type = KEXEC_TYPE_LIVE_UPDATE; + + ret = xc_kexec_exec(xch, type); + + xc_interface_close(xch); + + return ret; +} + +#else /* ! HAVE_LIBXENCTRL */ + +int xen_get_kexec_range(int range, uint64_t *start, uint64_t *end) +{ + return -1; +} + +int xen_kexec_load(struct kexec_info *UNUSED(info)) +{ + return -1; +} + +int xen_kexec_unload(uint64_t kexec_flags) +{ + return -1; +} + +int xen_kexec_status(uint64_t kexec_flags) +{ + return -1; +} + +int xen_kexec_exec(uint64_t kexec_flags) +{ + return -1; +} + +#endif diff --git a/kexec/kexec-xen.h b/kexec/kexec-xen.h new file mode 100644 index 0000000..70fb576 --- /dev/null +++ b/kexec/kexec-xen.h @@ -0,0 +1,87 @@ +#ifndef KEXEC_XEN_H +#define KEXEC_XEN_H + +#ifdef HAVE_LIBXENCTRL +#include <xenctrl.h> + +#ifdef CONFIG_LIBXENCTRL_DL +/* Lookup symbols in libxenctrl.so */ +extern void *__xc_dlsym(const char *symbol); + +/* Wrappers around xc_interface_open/close() to insert dlopen/dlclose() */ +extern xc_interface *__xc_interface_open(xentoollog_logger *logger, + xentoollog_logger *dombuild_logger, + unsigned open_flags); +extern int __xc_interface_close(xc_interface *xch); + +/* GCC expression statements for evaluating dlsym() */ +#define __xc_call(dtype, name, args...) \ +( \ + { dtype value; \ + typedef dtype (*func_t)(xc_interface *, ...); \ + func_t func = __xc_dlsym(#name); \ + value = func(args); \ + value; } \ +) +#define __xc_data(dtype, name) \ +( \ + { dtype *value = (dtype *)__xc_dlsym(#name); value; } \ +) + +/* The wrappers around utilized xenctrl.h functions */ +#define xc_interface_open(a, b, c) \ + __xc_interface_open(a, b, c) +#define xc_interface_close(a) \ + __xc_interface_close(a) +#define xc_version(args...) \ + __xc_call(int, xc_version, args) +#define xc_get_max_cpus(args...) \ + __xc_call(int, xc_get_max_cpus, args) +#define xc_get_machine_memory_map(args...) \ + __xc_call(int, xc_get_machine_memory_map, args) +#define xc_kexec_get_range(args...) \ + __xc_call(int, xc_kexec_get_range, args) +#define xc_kexec_load(args...) \ + __xc_call(int, xc_kexec_load, args) +#define xc_kexec_unload(args...) \ + __xc_call(int, xc_kexec_unload, args) +#define xc_kexec_status(args...) \ + __xc_call(int, xc_kexec_status, args) +#define xc_kexec_exec(args...) \ + __xc_call(int, xc_kexec_exec, args) +#define xc_hypercall_buffer_array_create(args...) \ + __xc_call(xc_hypercall_buffer_array_t *, xc_hypercall_buffer_array_create, args) +#define xc__hypercall_buffer_alloc_pages(args...) \ + __xc_call(void *, xc__hypercall_buffer_alloc_pages, args) +#define xc__hypercall_buffer_free_pages(args...) \ + __xc_call(void , xc__hypercall_buffer_free_pages, args) +#define xc__hypercall_buffer_array_alloc(args...) \ + __xc_call(void *, xc__hypercall_buffer_array_alloc, args) +#define xc__hypercall_buffer_array_get(args...) \ + __xc_call(void *, xc__hypercall_buffer_array_get, args) +#define xc_hypercall_buffer_array_destroy(args...) \ + __xc_call(void *, xc_hypercall_buffer_array_destroy, args) + +#endif /* CONFIG_LIBXENCTRL_DL */ + +#endif /* HAVE_LIBXENCTRL */ + +#ifndef KEXEC_RANGE_MA_XEN +#define KEXEC_RANGE_MA_XEN 1 +#endif + +#ifndef KEXEC_RANGE_MA_LIVEUPDATE +#define KEXEC_RANGE_MA_LIVEUPDATE 7 +#endif + +#ifndef KEXEC_TYPE_LIVE_UPDATE +#define KEXEC_TYPE_LIVE_UPDATE 2 +#endif + +#ifndef KEXEC_LIVE_UPDATE +#define KEXEC_LIVE_UPDATE 0x00000004 +#endif + +int xen_get_kexec_range(int range, uint64_t *start, uint64_t *end); + +#endif /* KEXEC_XEN_H */ diff --git a/kexec/kexec-zlib.h b/kexec/kexec-zlib.h new file mode 100644 index 0000000..16300f2 --- /dev/null +++ b/kexec/kexec-zlib.h @@ -0,0 +1,11 @@ +#ifndef __KEXEC_ZLIB_H +#define __KEXEC_ZLIB_H + +#include <stdio.h> +#include <sys/types.h> + +#include "config.h" + +int is_zlib_file(const char *filename, off_t *r_size); +char *zlib_decompress_file(const char *filename, off_t *r_size); +#endif /* __KEXEC_ZLIB_H */ diff --git a/kexec/kexec.8 b/kexec/kexec.8 new file mode 100644 index 0000000..3a344c5 --- /dev/null +++ b/kexec/kexec.8 @@ -0,0 +1,399 @@ +.\" Process this file with +.\" groff -man -Tascii kexec.8 +.\" +.TH kexec 8 "April 2006" Linux "User Manuals" +.SH NAME +kexec \- directly boot into a new kernel +.SH SYNOPSIS +.B /sbin/kexec +.B [-v (\-\-version)] [-f (\-\-force)] [-x (\-\-no-ifdown)] [-y (\-\-no-sync)] [-l (\-\-load)] [-p (\-\-load-panic)] [-u (\-\-unload)] [-e (\-\-exec)] [-t (\-\-type)] +.BI [\-\-mem\-min= addr ] +.BI [\-\-mem\-max= addr ] + +.SH DESCRIPTION +.B kexec +is a system call that enables you to load and boot into another +kernel from the currently running kernel. +.B kexec +performs the function of the boot loader from within the kernel. The +primary difference between a standard system boot and a +.B kexec +boot is that the hardware initialization normally performed by the BIOS +or firmware (depending on architecture) is not performed during a +.B kexec +boot. This has the effect of reducing the time required for a reboot. +.PP +Make sure you have selected +.B CONFIG_KEXEC=y +when configuring the kernel. The +.B CONFIG_KEXEC +option enables the +.B kexec +system call. +.SH USAGE +Using +.B kexec +consists of + +.RS +(1) loading the kernel to be rebooted to into memory, and +.RE +.RS +(2) actually rebooting to the pre-loaded kernel. +.RE +.PP +To load a kernel, the syntax is as follows: + +.RS +.B kexec +.RI \-l\ kernel-image +.RI "\-\-append=" command\-line\-options +.RI "\-\-initrd=" initrd\-image +.RE + +where +.I kernel\-image +is the kernel file that you intend to reboot to. +.PP +Insert the command-line parameters that must be passed to the new +kernel into +.IR command\-line\-options . +Passing the exact contents of /proc/cmdline into +.I command\-line\-options +is the safest way to ensure that correct values are passed to the +rebooting kernel. +.PP +The optional +.I initrd-image +is the initrd image to be used during boot. +.PP +It's also possible to invoke +.B kexec +without an option parameter. In that case, kexec loads the specified +kernel and then invokes +.BR shutdown (8). +If the shutdown scripts of your Linux distribution support +kexec-based rebooting, they then call +.B kexec +.I -e +just before actually rebooting the machine. That way, the machine does +a clean shutdown including all shutdown scripts. + +.SH EXAMPLE +.PP +For example, if the kernel image you want to reboot to is +.BR /boot/vmlinux , +the contents of /proc/cmdline is +.BR "root\=/dev/hda1" , +and the path to the initrd is +.BR /boot/initrd , +then you would use the following command to load the kernel: + +.RS +.B kexec +.RB \-l\ /boot/vmlinux +.RB "\-\-append=" "root=/dev/hda1" "\ \-\-initrd=" /boot/initrd +.RE +.PP +After this kernel is loaded, it can be booted to at any time using the +command: + +.RS +.BR kexec \ \-e +.RE + +.SH OPTIONS +.TP +.B \-d\ (\-\-debug) +Enable debugging messages. +.TP +.B \-S\ (\-\-status) +Return 1 if the type (by default crash) is loaded, 0 if not. Can be used in +conjuction with -l or -p to toggle the type. Note this option supersedes other +options and it will +.BR not\ load\ or\ unload\ the\ kernel. +.TP +.B \-e\ (\-\-exec) +Run the currently loaded kernel. Note that it will reboot into the loaded kernel without calling shutdown(8). +.TP +.B \-f\ (\-\-force) +Force an immediate +.B kexec +call, do not call +.BR shutdown (8) +(contrary to the default action without any option parameter). This option +performs the same actions like executing +.IR -l +and +.IR -e +in one call. +.TP +.B \-h\ (\-\-help) +Open a help file for +.BR kexec . +.TP +.B \-i\ (\-\-no-checks) +Fast reboot, no memory integrity checks. +.TP +.BI \-l\ (\-\-load) \ kernel +Load the specified +.I kernel +into the current kernel. +.TP +.B \-p\ (\-\-load\-panic) +Load the new kernel for use on panic. +.TP +.BI \-t\ (\-\-type= type ) +Specify that the new kernel is of this +.I type. +.TP +.BI \-s\ (\-\-kexec-file-syscall) +Specify that the new KEXEC_FILE_LOAD syscall should be used exclusively. +.TP +.BI \-c\ (\-\-kexec-syscall) +Specify that the old KEXEC_LOAD syscall should be used exclusively. +.TP +.BI \-a\ (\-\-kexec-syscall-auto) +Try the new KEXEC_FILE_LOAD syscall first and when it is not supported or the +kernel does not understand the supplied image fall back to the old KEXEC_LOAD +interface. + +There is no one single interface that always works, so this is the default. + +KEXEC_FILE_LOAD is required on systems that use locked-down secure boot to +verify the kernel signature. KEXEC_LOAD may be also disabled in the kernel +configuration. + +KEXEC_LOAD is required for some kernel image formats and on architectures that +do not implement KEXEC_FILE_LOAD. +.TP +.B \-u\ (\-\-unload) +Unload the current +.B kexec +target kernel. If a capture kernel is being unloaded then specify -p with -u. +.TP +.B \-v\ (\-\-version) +Return the version number of the installed utility. +.TP +.B \-x\ (\-\-no\-ifdown) +Shut down the running kernel, but restore the interface on reload. +.TP +.B \-y\ (\-\-no\-sync) +Shut down the running kernel, but skip syncing the filesystems. +.TP +.BI \-\-mem\-min= addr +Specify the lowest memory address +.I addr +to load code into. +.TP +.BI \-\-mem\-max= addr +Specify the highest memory address +.I addr +to load code into. +.TP +.BI \-\-entry= addr +Specify the jump back address. (0 means it's not jump back or preserve context) +.TP +.BI \-\-load\-preserve\-context +Load the new kernel and preserve context of current kernel during kexec. +.TP +.BI \-\-load\-jump\-back\-helper +Load a helper image to jump back to original kernel. +.TP +.BI \-\-reuseinitrd +Reuse initrd from first boot. +.TP +.BI \-\-print-ckr-size +Print crash kernel region size, if available. + + +.SH SUPPORTED KERNEL FILE TYPES AND OPTIONS +.B Beoboot-x86 +.RS +.TP +.B \-\-args\-elf +Pass ELF boot notes. +.TP +.B \-\-args\-linux +Pass Linux kernel style options. +.TP +.B \-\-real\-mode +Use the kernel's real mode entry point. +.RE +.PP +.B elf-x86 +.RS +.TP +.BI \-\-append= string +Append +.I string +to the kernel command line. +.TP +.BI \-\-command\-line= string +Set the kernel command line to +.IR string . +.TP +.BI \-\-reuse-cmdline +Use the command line from the running system. When a panic kernel is loaded, it +strips the +.I +crashkernel +parameter automatically. The +.I BOOT_IMAGE +parameter is also stripped. +.TP +.BI \-\-initrd= file +Use +.I file +as the kernel's initial ramdisk. +.TP +.BI \-\-ramdisk= file +Use +.I file +as the kernel's initial ramdisk. +.RE +.PP +.B bzImage-x86 +.RS +.TP +.BI \-\-append= string +Append +.I string +to the kernel command line. +.TP +.BI \-\-command\-line= string +Set the kernel command line to +.IR string . +.TP +.BI \-\-reuse-cmdline +Use the command line from the running system. When a panic kernel is loaded, it +strips the +.I +crashkernel +parameter automatically. The +.I BOOT_IMAGE +parameter is also stripped. +.TP +.BI \-\-initrd= file +Use +.I file +as the kernel's initial ramdisk. +.TP +.BI \-\-ramdisk= file +Use +.I file +as the kernel's initial ramdisk. +.TP +.BI \-\-real-mode +Use real-mode entry point. +.RE +.PP +.B multiboot-x86 +.RS +.TP +.BI \-\-command\-line= string +Set the kernel command line to +.IR string . +.TP +.BI \-\-reuse-cmdline +Use the command line from the running system. When a panic kernel is loaded, it +strips the +.I +crashkernel +parameter automatically. The +.I BOOT_IMAGE +parameter is also stripped. +.TP +.BI \-\-module= "mod arg1 arg2 ..." +Load module +.I mod +with command-line arguments +.I "arg1 arg2 ..." +This parameter can be specified multiple times. +.RE +.PP +.B multiboot2-x86 +.RS +.TP +.BI \-\-command\-line= string +Set the kernel command line to +.IR string . +.TP +.BI \-\-reuse-cmdline +Use the command line from the running system. When a panic kernel is loaded, it +strips the +.I +crashkernel +parameter automatically. The +.I BOOT_IMAGE +parameter is also stripped. +.TP +.BI \-\-module= "mod arg1 arg2 ..." +Load module +.I mod +with command-line arguments +.I "arg1 arg2 ..." +This parameter can be specified multiple times. +.RE +.PP +.B elf-ppc64 +.RS +.TP +.BI \-\-reuse-cmdline +Use the kernel command line from the running system. +.TP +.BI \-\-command\-line= string +Set the kernel command line to +.IR string. +.TP +.BI \-\-append= string +Set the kernel command line to +.IR string. +.TP +.BI \-\-ramdisk= file +Use +.IR file +as the initial RAM disk. +.TP +.BI \-\-initrd= file +Use +.IR file +as the initial RAM disk. +.TP +.BI \-\-devicetreeblob= file +Specify device tree blob file. Not applicable while using --kexec-file-syscall. +.TP +.BI \-\-dtb= file +Specify device tree blob file. Not applicable while using --kexec-file-syscall. +.RE + +.SH ARCHITECTURE OPTIONS +.TP +.B \-\-console\-serial +Enable the serial console. +.TP +.B \-\-console\-vga +Enable the VGA console. +.TP +.B \-\-elf32\-core\-headers +Prepare core headers in ELF32 format. +.TP +.B \-\-elf64\-core\-headers +Prepare core headers in ELF64 format. +.TP +.B \-\-reset\-vga +Attempt to reset a standard VGA device. +.TP +.BI \-\-serial= port +Specify the serial +.I port +for debug output. +.TP +.BI \-\-serial\-baud= baud_rate +Specify the +.I baud rate +of the serial port. +.TP +.BI \-\-dt\-no\-old\-root +Do not reuse old kernel root=<device> +param while creating flatten device tree. diff --git a/kexec/kexec.c b/kexec/kexec.c new file mode 100644 index 0000000..c3b182e --- /dev/null +++ b/kexec/kexec.c @@ -0,0 +1,1712 @@ +/* + * kexec: Linux boots Linux + * + * Copyright (C) 2003-2005 Eric Biederman (ebiederm@xmission.com) + * + * Modified (2007-05-15) by Francesco Chiechi to rudely handle mips platform + * + * 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 (version 2 of the License). + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#define _GNU_SOURCE +#include <inttypes.h> +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <limits.h> +#include <sys/ioctl.h> +#include <sys/mount.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/reboot.h> +#include <sys/mman.h> +#include <unistd.h> +#include <fcntl.h> +#ifndef _O_BINARY +#define _O_BINARY 0 +#endif +#include <getopt.h> +#include <ctype.h> + +#include "config.h" + +#include <sha256.h> +#include "kexec.h" +#include "kexec-syscall.h" +#include "kexec-elf.h" +#include "kexec-xen.h" +#include "kexec-sha256.h" +#include "kexec-zlib.h" +#include "kexec-lzma.h" +#include <arch/options.h> + +#define KEXEC_LOADED_PATH "/sys/kernel/kexec_loaded" +#define KEXEC_CRASH_LOADED_PATH "/sys/kernel/kexec_crash_loaded" + +unsigned long long mem_min = 0; +unsigned long long mem_max = ULONG_MAX; +static unsigned long kexec_flags = 0; +/* Flags for kexec file (fd) based syscall */ +static unsigned long kexec_file_flags = 0; +int kexec_debug = 0; + +void dbgprint_mem_range(const char *prefix, struct memory_range *mr, int nr_mr) +{ + int i; + dbgprintf("%s\n", prefix); + for (i = 0; i < nr_mr; i++) { + dbgprintf("%016llx-%016llx (%d)\n", mr[i].start, + mr[i].end, mr[i].type); + } +} + +void die(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + fflush(stdout); + fflush(stderr); + exit(1); +} + +static char *xstrdup(const char *str) +{ + char *new = strdup(str); + if (!new) + die("Cannot strdup \"%s\": %s\n", + str, strerror(errno)); + return new; +} + +void *xmalloc(size_t size) +{ + void *buf; + if (!size) + return NULL; + buf = malloc(size); + if (!buf) { + die("Cannot malloc %ld bytes: %s\n", + size + 0UL, strerror(errno)); + } + return buf; +} + +void *xrealloc(void *ptr, size_t size) +{ + void *buf; + buf = realloc(ptr, size); + if (!buf) { + die("Cannot realloc %ld bytes: %s\n", + size + 0UL, strerror(errno)); + } + return buf; +} + +int valid_memory_range(struct kexec_info *info, + unsigned long sstart, unsigned long send) +{ + int i; + if (sstart > send) { + return 0; + } + if ((send > mem_max) || (sstart < mem_min)) { + return 0; + } + for (i = 0; i < info->memory_ranges; i++) { + unsigned long mstart, mend; + /* Only consider memory ranges */ + if (info->memory_range[i].type != RANGE_RAM) + continue; + mstart = info->memory_range[i].start; + mend = info->memory_range[i].end; + if (i < info->memory_ranges - 1 + && mend == info->memory_range[i+1].start + && info->memory_range[i+1].type == RANGE_RAM) + mend = info->memory_range[i+1].end; + + /* Check to see if we are fully contained */ + if ((mstart <= sstart) && (mend >= send)) { + return 1; + } + } + return 0; +} + +static int valid_memory_segment(struct kexec_info *info, + struct kexec_segment *segment) +{ + unsigned long sstart, send; + sstart = (unsigned long)segment->mem; + send = sstart + segment->memsz - 1; + + return valid_memory_range(info, sstart, send); +} + +void print_segments(FILE *f, struct kexec_info *info) +{ + int i; + + fprintf(f, "nr_segments = %d\n", info->nr_segments); + for (i = 0; i < info->nr_segments; i++) { + fprintf(f, "segment[%d].buf = %p\n", i, + info->segment[i].buf); + fprintf(f, "segment[%d].bufsz = 0x%zx\n", i, + info->segment[i].bufsz); + fprintf(f, "segment[%d].mem = %p\n", i, + info->segment[i].mem); + fprintf(f, "segment[%d].memsz = 0x%zx\n", i, + info->segment[i].memsz); + } +} + +int sort_segments(struct kexec_info *info) +{ + int i, j; + void *end; + + /* Do a stupid insertion sort... */ + for (i = 0; i < info->nr_segments; i++) { + int tidx; + struct kexec_segment temp; + tidx = i; + for (j = i +1; j < info->nr_segments; j++) { + if (info->segment[j].mem < info->segment[tidx].mem) { + tidx = j; + } + } + if (tidx != i) { + temp = info->segment[tidx]; + info->segment[tidx] = info->segment[i]; + info->segment[i] = temp; + } + } + /* Now see if any of the segments overlap */ + end = 0; + for (i = 0; i < info->nr_segments; i++) { + if (end > info->segment[i].mem) { + fprintf(stderr, "Overlapping memory segments at %p\n", + end); + return -1; + } + end = ((char *)info->segment[i].mem) + info->segment[i].memsz; + } + return 0; +} + +unsigned long locate_hole(struct kexec_info *info, + unsigned long hole_size, unsigned long hole_align, + unsigned long hole_min, unsigned long hole_max, + int hole_end) +{ + int i, j; + struct memory_range *mem_range; + int max_mem_ranges, mem_ranges; + unsigned long hole_base; + + if (hole_end == 0) { + die("Invalid hole end argument of 0 specified to locate_hole"); + } + + /* Set an initial invalid value for the hole base */ + hole_base = ULONG_MAX; + + /* Align everything to at least a page size boundary */ + if (hole_align < (unsigned long)getpagesize()) { + hole_align = getpagesize(); + } + + /* Compute the free memory ranges */ + max_mem_ranges = info->memory_ranges + info->nr_segments; + mem_range = xmalloc(max_mem_ranges *sizeof(struct memory_range)); + mem_ranges = 0; + + /* Perform a merge on the 2 sorted lists of memory ranges */ + for (j = 0, i = 0; i < info->memory_ranges; i++) { + unsigned long long sstart, send; + unsigned long long mstart, mend; + mstart = info->memory_range[i].start; + mend = info->memory_range[i].end; + if (info->memory_range[i].type != RANGE_RAM) + continue; + while ((j < info->nr_segments) && + (((unsigned long)info->segment[j].mem) <= mend)) { + sstart = (unsigned long)info->segment[j].mem; + send = sstart + info->segment[j].memsz -1; + if (mstart < sstart) { + mem_range[mem_ranges].start = mstart; + mem_range[mem_ranges].end = sstart -1; + mem_range[mem_ranges].type = RANGE_RAM; + mem_ranges++; + } + mstart = send +1; + j++; + } + if (mstart < mend) { + mem_range[mem_ranges].start = mstart; + mem_range[mem_ranges].end = mend; + mem_range[mem_ranges].type = RANGE_RAM; + mem_ranges++; + } + } + /* Now find the end of the last memory_range I can use */ + for (i = 0; i < mem_ranges; i++) { + unsigned long long start, end, size; + start = mem_range[i].start; + end = mem_range[i].end; + /* First filter the range start and end values + * through the lens of mem_min, mem_max and hole_align. + */ + if (start < mem_min) { + start = mem_min; + } + if (start < hole_min) { + start = hole_min; + } + start = _ALIGN(start, hole_align); + if (end > mem_max) { + end = mem_max; + } + if (end > hole_max) { + end = hole_max; + } + /* Is this still a valid memory range? */ + if ((start >= end) || (start >= mem_max) || (end <= mem_min)) { + continue; + } + /* Is there enough space left so we can use it? */ + size = end - start; + if (!hole_size || size >= hole_size - 1) { + if (hole_end > 0) { + hole_base = start; + break; + } else { + hole_base = _ALIGN_DOWN(end - hole_size + 1, + hole_align); + } + } + } + free(mem_range); + if (hole_base == ULONG_MAX) { + fprintf(stderr, "Could not find a free area of memory of " + "0x%lx bytes...\n", hole_size); + return ULONG_MAX; + } + if (hole_size && (hole_base + hole_size - 1) > hole_max) { + fprintf(stderr, "Could not find a free area of memory below: " + "0x%lx...\n", hole_max); + return ULONG_MAX; + } + return hole_base; +} + +void add_segment_phys_virt(struct kexec_info *info, + const void *buf, size_t bufsz, + unsigned long base, size_t memsz, int phys) +{ + unsigned long last; + size_t size; + int pagesize; + + if (bufsz > memsz) { + bufsz = memsz; + } + /* Forget empty segments */ + if (memsz == 0) { + return; + } + + /* Round memsz up to a multiple of pagesize */ + pagesize = getpagesize(); + memsz = _ALIGN(memsz, pagesize); + + /* Verify base is pagesize aligned. + * Finding a way to cope with this problem + * is important but for now error so at least + * we are not surprised by the code doing the wrong + * thing. + */ + if (base & (pagesize -1)) { + die("Base address: 0x%lx is not page aligned\n", base); + } + + if (phys) + base = virt_to_phys(base); + + last = base + memsz -1; + if (!valid_memory_range(info, base, last)) { + die("Invalid memory segment %p - %p\n", + (void *)base, (void *)last); + } + + size = (info->nr_segments + 1) * sizeof(info->segment[0]); + info->segment = xrealloc(info->segment, size); + info->segment[info->nr_segments].buf = buf; + info->segment[info->nr_segments].bufsz = bufsz; + info->segment[info->nr_segments].mem = (void *)base; + info->segment[info->nr_segments].memsz = memsz; + info->nr_segments++; + if (info->nr_segments > KEXEC_MAX_SEGMENTS) { + fprintf(stderr, "Warning: kernel segment limit reached. " + "This will likely fail\n"); + } +} + +unsigned long add_buffer_phys_virt(struct kexec_info *info, + const void *buf, unsigned long bufsz, unsigned long memsz, + unsigned long buf_align, unsigned long buf_min, unsigned long buf_max, + int buf_end, int phys) +{ + unsigned long base; + int result; + int pagesize; + + result = sort_segments(info); + if (result < 0) { + die("sort_segments failed\n"); + } + + /* Round memsz up to a multiple of pagesize */ + pagesize = getpagesize(); + memsz = _ALIGN(memsz, pagesize); + + base = locate_hole(info, memsz, buf_align, buf_min, buf_max, buf_end); + if (base == ULONG_MAX) { + die("locate_hole failed\n"); + } + + add_segment_phys_virt(info, buf, bufsz, base, memsz, phys); + return base; +} + +unsigned long add_buffer_virt(struct kexec_info *info, const void *buf, + unsigned long bufsz, unsigned long memsz, + unsigned long buf_align, unsigned long buf_min, + unsigned long buf_max, int buf_end) +{ + return add_buffer_phys_virt(info, buf, bufsz, memsz, buf_align, + buf_min, buf_max, buf_end, 0); +} + +static int find_memory_range(struct kexec_info *info, + unsigned long *base, unsigned long *size) +{ + int i; + unsigned long start, end; + + for (i = 0; i < info->memory_ranges; i++) { + if (info->memory_range[i].type != RANGE_RAM) + continue; + start = info->memory_range[i].start; + end = info->memory_range[i].end; + if (end > *base && start < *base + *size) { + if (start > *base) { + *size = *base + *size - start; + *base = start; + } + if (end < *base + *size) + *size = end - *base; + return 1; + } + } + return 0; +} + +static int find_segment_hole(struct kexec_info *info, + unsigned long *base, unsigned long *size) +{ + int i; + unsigned long seg_base, seg_size; + + for (i = 0; i < info->nr_segments; i++) { + seg_base = (unsigned long)info->segment[i].mem; + seg_size = info->segment[i].memsz; + + if (seg_base + seg_size <= *base) + continue; + else if (seg_base >= *base + *size) + break; + else if (*base < seg_base) { + *size = seg_base - *base; + break; + } else if (seg_base + seg_size < *base + *size) { + *size = *base + *size - (seg_base + seg_size); + *base = seg_base + seg_size; + } else { + *size = 0; + break; + } + } + return *size; +} + +static int add_backup_segments(struct kexec_info *info, + unsigned long backup_base, + unsigned long backup_size) +{ + unsigned long mem_base, mem_size, bkseg_base, bkseg_size, start, end; + unsigned long pagesize; + + pagesize = getpagesize(); + while (backup_size) { + mem_base = backup_base; + mem_size = backup_size; + if (!find_memory_range(info, &mem_base, &mem_size)) + break; + backup_size = backup_base + backup_size - \ + (mem_base + mem_size); + backup_base = mem_base + mem_size; + while (mem_size) { + bkseg_base = mem_base; + bkseg_size = mem_size; + if (sort_segments(info) < 0) + return -1; + if (!find_segment_hole(info, &bkseg_base, &bkseg_size)) + break; + start = _ALIGN(bkseg_base, pagesize); + end = _ALIGN_DOWN(bkseg_base + bkseg_size, pagesize); + add_segment_phys_virt(info, NULL, 0, + start, end-start, 0); + mem_size = mem_base + mem_size - \ + (bkseg_base + bkseg_size); + mem_base = bkseg_base + bkseg_size; + } + } + return 0; +} + +static char *slurp_fd(int fd, const char *filename, off_t size, off_t *nread) +{ + char *buf; + off_t progress; + ssize_t result; + + buf = xmalloc(size); + progress = 0; + while (progress < size) { + result = read(fd, buf + progress, size - progress); + if (result < 0) { + if ((errno == EINTR) || (errno == EAGAIN)) + continue; + fprintf(stderr, "Read on %s failed: %s\n", filename, + strerror(errno)); + free(buf); + close(fd); + return NULL; + } + if (result == 0) + /* EOF */ + break; + progress += result; + } + result = close(fd); + if (result < 0) + die("Close of %s failed: %s\n", filename, strerror(errno)); + + if (nread) + *nread = progress; + return buf; +} + +static char *slurp_file_generic(const char *filename, off_t *r_size, + int use_mmap) +{ + int fd; + char *buf; + off_t size, err, nread; + ssize_t result; + struct stat stats; + + if (!filename) { + *r_size = 0; + return 0; + } + fd = open(filename, O_RDONLY | _O_BINARY); + if (fd < 0) { + die("Cannot open `%s': %s\n", + filename, strerror(errno)); + } + result = fstat(fd, &stats); + if (result < 0) { + die("Cannot stat: %s: %s\n", + filename, strerror(errno)); + } + /* + * Seek in case the kernel is a character node like /dev/ubi0_0. + * This does not work on regular files which live in /proc and + * we need this for some /proc/device-tree entries + */ + if (S_ISCHR(stats.st_mode)) { + + size = lseek(fd, 0, SEEK_END); + if (size < 0) + die("Can not seek file %s: %s\n", filename, + strerror(errno)); + + err = lseek(fd, 0, SEEK_SET); + if (err < 0) + die("Can not seek to the begin of file %s: %s\n", + filename, strerror(errno)); + buf = slurp_fd(fd, filename, size, &nread); + } else if (S_ISBLK(stats.st_mode)) { + err = ioctl(fd, BLKGETSIZE64, &size); + if (err < 0) + die("Can't retrieve size of block device %s: %s\n", + filename, strerror(errno)); + buf = slurp_fd(fd, filename, size, &nread); + } else { + size = stats.st_size; + if (use_mmap) { + buf = mmap(NULL, size, PROT_READ|PROT_WRITE, + MAP_PRIVATE, fd, 0); + nread = size; + } else { + buf = slurp_fd(fd, filename, size, &nread); + } + } + if ((use_mmap && (buf == MAP_FAILED)) || (!use_mmap && (buf == NULL))) + die("Cannot read %s", filename); + + if (nread != size) + die("Read on %s ended before stat said it should\n", filename); + + *r_size = size; + close(fd); + return buf; +} + +/* + * Read file into malloced buffer. + */ +char *slurp_file(const char *filename, off_t *r_size) +{ + return slurp_file_generic(filename, r_size, 0); +} + +/* + * Map "normal" file or read "character device" into malloced buffer. + * You must not use free, realloc, etc. for the returned buffer. + */ +char *slurp_file_mmap(const char *filename, off_t *r_size) +{ + return slurp_file_generic(filename, r_size, 1); +} + +/* This functions reads either specified number of bytes from the file or + lesser if EOF is met. */ + +char *slurp_file_len(const char *filename, off_t size, off_t *nread) +{ + int fd; + + if (!filename) + return 0; + fd = open(filename, O_RDONLY | _O_BINARY); + if (fd < 0) { + fprintf(stderr, "Cannot open %s: %s\n", filename, + strerror(errno)); + return 0; + } + + return slurp_fd(fd, filename, size, nread); +} + +char *slurp_decompress_file(const char *filename, off_t *r_size) +{ + char *kernel_buf; + + kernel_buf = zlib_decompress_file(filename, r_size); + if (!kernel_buf) { + kernel_buf = lzma_decompress_file(filename, r_size); + if (!kernel_buf) + return slurp_file(filename, r_size); + } + return kernel_buf; +} + +static int copybuf_memfd(const char *kernel_buf, size_t size) +{ + int fd, count; + + fd = memfd_create("kernel", MFD_ALLOW_SEALING); + if (fd == -1) + return fd; + + count = write(fd, kernel_buf, size); + if (count < 0) + return -1; + + return fd; +} + +static void update_purgatory(struct kexec_info *info) +{ + static const uint8_t null_buf[256]; + sha256_context ctx; + sha256_digest_t digest; + struct sha256_region region[SHA256_REGIONS]; + int i, j; + /* Don't do anything if we are not using purgatory */ + if (!info->rhdr.e_shdr) { + return; + } + arch_update_purgatory(info); + + if (info->skip_checks) { + unsigned int tmp = 1; + + elf_rel_set_symbol(&info->rhdr, "skip_checks", &tmp, + sizeof(tmp)); + return; + } + + memset(region, 0, sizeof(region)); + sha256_starts(&ctx); + /* Compute a hash of the loaded kernel */ + for(j = i = 0; i < info->nr_segments; i++) { + unsigned long nullsz; + /* Don't include purgatory in the checksum. The stack + * in the bss will definitely change, and the .data section + * will also change when we poke the sha256_digest in there. + * A very clever/careful person could probably improve this. + */ + if (info->segment[i].mem == (void *)info->rhdr.rel_addr) { + continue; + } + sha256_update(&ctx, info->segment[i].buf, + info->segment[i].bufsz); + nullsz = info->segment[i].memsz - info->segment[i].bufsz; + while(nullsz) { + unsigned long bytes = nullsz; + if (bytes > sizeof(null_buf)) { + bytes = sizeof(null_buf); + } + sha256_update(&ctx, null_buf, bytes); + nullsz -= bytes; + } + region[j].start = (unsigned long) info->segment[i].mem; + region[j].len = info->segment[i].memsz; + j++; + } + sha256_finish(&ctx, digest); + elf_rel_set_symbol(&info->rhdr, "sha256_regions", ®ion, + sizeof(region)); + elf_rel_set_symbol(&info->rhdr, "sha256_digest", &digest, + sizeof(digest)); +} + +/* + * Load the new kernel + */ +static int my_load(const char *type, int fileind, int argc, char **argv, + unsigned long kexec_flags, int skip_checks, void *entry) +{ + char *kernel; + char *kernel_buf; + off_t kernel_size; + int i = 0; + int result; + struct kexec_info info; + long native_arch; + int guess_only = 0; + + memset(&info, 0, sizeof(info)); + info.kexec_flags = kexec_flags; + info.skip_checks = skip_checks; + + result = 0; + if (argc - fileind <= 0) { + fprintf(stderr, "No kernel specified\n"); + usage(); + return -1; + } + kernel = argv[fileind]; + /* slurp in the input kernel */ + kernel_buf = slurp_decompress_file(kernel, &kernel_size); + + dbgprintf("kernel: %p kernel_size: %#llx\n", + kernel_buf, (unsigned long long)kernel_size); + + if (get_memory_ranges(&info.memory_range, &info.memory_ranges, + info.kexec_flags) < 0 || info.memory_ranges == 0) { + fprintf(stderr, "Could not get memory layout\n"); + return -1; + } + /* if a kernel type was specified, try to honor it */ + if (type) { + for (i = 0; i < file_types; i++) { + if (strcmp(type, file_type[i].name) == 0) + break; + } + if (i == file_types) { + fprintf(stderr, "Unsupported kernel type %s\n", type); + return -1; + } else { + /* make sure our file is really of that type */ + if (file_type[i].probe(kernel_buf, kernel_size) < 0) + guess_only = 1; + } + } + if (!type || guess_only) { + for (i = 0; i < file_types; i++) { + if (file_type[i].probe(kernel_buf, kernel_size) == 0) + break; + } + if (i == file_types) { + fprintf(stderr, "Cannot determine the file type " + "of %s\n", kernel); + return -1; + } else { + if (guess_only) { + fprintf(stderr, "Wrong file type %s, " + "file matches type %s\n", + type, file_type[i].name); + return -1; + } + } + } + /* Figure out our native architecture before load */ + native_arch = physical_arch(); + if (native_arch < 0) { + return -1; + } + info.kexec_flags |= native_arch; + + result = file_type[i].load(argc, argv, kernel_buf, kernel_size, &info); + if (result < 0) { + switch (result) { + case ENOCRASHKERNEL: + fprintf(stderr, + "No crash kernel segment found in /proc/iomem\n" + "Please check the crashkernel= boot parameter.\n"); + break; + case EFAILED: + default: + fprintf(stderr, "Cannot load %s\n", kernel); + break; + } + return result; + } + /* If we are not in native mode setup an appropriate trampoline */ + if (arch_compat_trampoline(&info) < 0) { + return -1; + } + if (info.kexec_flags & KEXEC_PRESERVE_CONTEXT) { + add_backup_segments(&info, mem_min, mem_max - mem_min + 1); + } + /* Verify all of the segments load to a valid location in memory */ + for (i = 0; i < info.nr_segments; i++) { + if (!valid_memory_segment(&info, info.segment +i)) { + fprintf(stderr, "Invalid memory segment %p - %p\n", + info.segment[i].mem, + ((char *)info.segment[i].mem) + + info.segment[i].memsz); + return -1; + } + } + /* Sort the segments and verify we don't have overlaps */ + if (sort_segments(&info) < 0) { + return -1; + } + /* if purgatory is loaded update it */ + update_purgatory(&info); + if (entry) + info.entry = entry; + + dbgprintf("kexec_load: entry = %p flags = 0x%lx\n", + info.entry, info.kexec_flags); + if (kexec_debug) + print_segments(stderr, &info); + + if (xen_present()) + result = xen_kexec_load(&info); + else + result = kexec_load(info.entry, + info.nr_segments, info.segment, + info.kexec_flags); + if (result != 0) { + /* The load failed, print some debugging information */ + fprintf(stderr, "kexec_load failed: %s\n", + strerror(errno)); + fprintf(stderr, "entry = %p flags = 0x%lx\n", + info.entry, info.kexec_flags); + print_segments(stderr, &info); + } + return result; +} + +static int kexec_file_unload(unsigned long kexec_file_flags) +{ + int ret = 0; + + if (!is_kexec_file_load_implemented()) + return EFALLBACK; + + ret = kexec_file_load(-1, -1, 0, NULL, kexec_file_flags); + if (ret != 0) { + if (errno == ENOSYS) { + ret = EFALLBACK; + } else { + /* + * The unload failed, print some debugging + * information */ + fprintf(stderr, "kexec_file_load(unload) failed: %s\n", + strerror(errno)); + ret = EFAILED; + } + } + return ret; +} + +static int k_unload (unsigned long kexec_flags) +{ + int result; + long native_arch; + + /* set the arch */ + native_arch = physical_arch(); + if (native_arch < 0) { + return -1; + } + kexec_flags |= native_arch; + + if (xen_present()) + result = xen_kexec_unload(kexec_flags); + else + result = kexec_load(NULL, 0, NULL, kexec_flags); + if (result != 0) { + /* The unload failed, print some debugging information */ + fprintf(stderr, "kexec unload failed: %s\n", + strerror(errno)); + } + return result; +} + +/* + * Start a reboot. + */ +static int my_shutdown(void) +{ + char *args[] = { + "shutdown", + "-r", + "now", + NULL + }; + + execv("/sbin/shutdown", args); + execv("/etc/shutdown", args); + execv("/bin/shutdown", args); + + perror("shutdown"); + return -1; +} + +/* + * Exec the new kernel. If successful, this triggers an immediate reboot + * and does not return, but Xen Live Update is an exception (more on this + * below). + */ +static int my_exec(void) +{ + if (xen_present()) { + int ret; + + /* + * There are two cases in which the Xen hypercall may return: + * 1) An error occurred, e.g. the kexec image was not loaded. + * The exact error is indicated by errno. + * 2) Live Update was successfully scheduled. Note that unlike + * a normal kexec, Live Update happens asynchronously, i.e. + * the hypercall merely schedules the kexec operation and + * returns immediately. + */ + ret = xen_kexec_exec(kexec_flags); + if ((kexec_flags & KEXEC_LIVE_UPDATE) && !ret) + return 0; + } else + reboot(LINUX_REBOOT_CMD_KEXEC); + /* I have failed if I make it here */ + fprintf(stderr, "kexec failed: %s\n", + strerror(errno)); + return -1; +} + +static int load_jump_back_helper_image(unsigned long kexec_flags, void *entry) +{ + int result; + struct kexec_segment seg; + + memset(&seg, 0, sizeof(seg)); + result = kexec_load(entry, 1, &seg, kexec_flags); + return result; +} + +static int kexec_loaded(const char *file) +{ + long ret = -1; + FILE *fp; + char *p; + char line[3]; + + /* No way to tell if an image is loaded under Xen, assume it is. */ + if (xen_present()) + return 1; + + fp = fopen(file, "r"); + if (fp == NULL) + return -1; + + p = fgets(line, sizeof(line), fp); + fclose(fp); + + if (p == NULL) + return -1; + + ret = strtol(line, &p, 10); + + /* Too long */ + if (ret > INT_MAX) + return -1; + + /* No digits were found */ + if (p == line) + return -1; + + return (int)ret; +} + +/* + * Jump back to the original kernel + */ +static int my_load_jump_back_helper(unsigned long kexec_flags, void *entry) +{ + int result; + + if (kexec_loaded(KEXEC_LOADED_PATH)) { + fprintf(stderr, "There is kexec kernel loaded, make sure " + "you are in kexeced kernel.\n"); + return -1; + } + if (!entry) { + fprintf(stderr, "Please specify jump back entry " + "in command line\n"); + return -1; + } + result = load_jump_back_helper_image(kexec_flags, entry); + if (result) { + fprintf(stderr, "load jump back kernel failed: %s\n", + strerror(errno)); + return result; + } + return result; +} + +static void version(void) +{ + printf(PACKAGE_STRING "\n"); +} + +void usage(void) +{ + int i; + + version(); + printf("Usage: kexec [OPTION]... [kernel]\n" + "Directly reboot into a new kernel\n" + "\n" + " -h, --help Print this help.\n" + " -v, --version Print the version of kexec.\n" + " -f, --force Force an immediate kexec,\n" + " don't call shutdown.\n" + " -i, --no-checks Fast reboot, no memory integrity checks.\n" + " -x, --no-ifdown Don't bring down network interfaces.\n" + " -y, --no-sync Don't sync filesystems before kexec.\n" + " -l, --load Load the new kernel into the\n" + " current kernel.\n" + " -p, --load-panic Load the new kernel for use on panic.\n" + " -u, --unload Unload the current kexec target kernel.\n" + " If capture kernel is being unloaded\n" + " specify -p with -u.\n" + " -e, --exec Execute a currently loaded kernel.\n" + " --exec-live-update Execute a currently loaded xen image after\n" + "storing the state required to live update.\n" + " -t, --type=TYPE Specify the new kernel is of this type.\n" + " --mem-min=<addr> Specify the lowest memory address to\n" + " load code into.\n" + " --mem-max=<addr> Specify the highest memory address to\n" + " load code into.\n" + " --reuseinitrd Reuse initrd from first boot.\n" + " --print-ckr-size Print crash kernel region size.\n" + " --load-preserve-context Load the new kernel and preserve\n" + " context of current kernel during kexec.\n" + " --load-jump-back-helper Load a helper image to jump back\n" + " to original kernel.\n" + " --load-live-update Load the new kernel to overwrite the\n" + " running kernel.\n" + " --entry=<addr> Specify jump back address.\n" + " (0 means it's not jump back or\n" + " preserve context)\n" + " to original kernel.\n" + " -s, --kexec-file-syscall Use file based syscall for kexec operation\n" + " -c, --kexec-syscall Use the kexec_load syscall for for compatibility\n" + " with systems that don't support -s\n" + " -a, --kexec-syscall-auto Use file based syscall for kexec and fall\n" + " back to the compatibility syscall when file based\n" + " syscall is not supported or the kernel did not\n" + " understand the image (default)\n" + " -d, --debug Enable debugging to help spot a failure.\n" + " -S, --status Return 1 if the type (by default crash) is loaded,\n" + " 0 if not.\n" + "\n" + "Supported kernel file types and options: \n"); + for (i = 0; i < file_types; i++) { + printf("%s\n", file_type[i].name); + file_type[i].usage(); + } + printf( "Architecture options: \n"); + arch_usage(); + printf("\n"); +} + +static int k_status(unsigned long kexec_flags) +{ + int result; + long native_arch; + + /* set the arch */ + native_arch = physical_arch(); + if (native_arch < 0) { + return -1; + } + kexec_flags |= native_arch; + + if (xen_present()) + result = xen_kexec_status(kexec_flags); + else { + if (kexec_flags & KEXEC_ON_CRASH) + result = kexec_loaded(KEXEC_CRASH_LOADED_PATH); + else + result = kexec_loaded(KEXEC_LOADED_PATH); + } + return result; +} + + +/* + * Remove parameter from a kernel command line. Helper function by get_command_line(). + */ +void remove_parameter(char *line, const char *param_name) +{ + char *start, *end; + + start = strstr(line, param_name); + + /* parameter not found */ + if (!start) + return; + + /* + * check if that's really the start of a parameter and not in + * the middle of the word + */ + if (start != line && !isspace(*(start-1))) + return; + + end = strstr(start, " "); + if (!end) + *start = 0; + else { + memmove(start, end+1, strlen(end)); + *(end + strlen(end)) = 0; + } +} + +static ssize_t _read(int fd, void *buf, size_t count) +{ + ssize_t ret, offset = 0; + + do { + ret = read(fd, buf + offset, count - offset); + if (ret < 0) { + if ((errno == EINTR) || (errno == EAGAIN)) + continue; + return ret; + } + offset += ret; + } while (ret && offset < count); + + return offset; +} + +static char *slurp_proc_file(const char *filename, size_t *len) +{ + ssize_t ret, startpos = 0; + unsigned int size = 64; + char *buf = NULL, *tmp; + int fd; + + fd = open(filename, O_RDONLY); + if (fd == -1) + return NULL; + + do { + size *= 2; + tmp = realloc(buf, size); + if (!tmp) { + free(buf); + return NULL; + } + buf = tmp; + + ret = _read(fd, buf + startpos, size - startpos); + if (ret < 0) { + free(buf); + return NULL; + } + + startpos += ret; + + } while(ret); + + *len = startpos; + return buf; +} + +/* + * Returns the contents of the current command line to be used with + * --reuse-cmdline option. The function gets called from architecture specific + * code. If we load a panic kernel, that function will strip the + * "crashkernel=" option because it does not make sense that the crashkernel + * reserves memory for a crashkernel (well, it would not boot since the + * amount is exactly the same as the crashkernel has overall memory). Also, + * remove the BOOT_IMAGE from lilo (and others) since that doesn't make + * sense here any more. The kernel could be different even if we reuse the + * commandline. + * + * The function returns dynamically allocated memory. + */ +char *get_command_line(void) +{ + char *p, *line; + size_t size; + + line = slurp_proc_file("/proc/cmdline", &size); + if (!line || !size) + die("Failed to read /proc/cmdline\n"); + + /* strip newline */ + line[size-1] = '\0'; + + p = strpbrk(line, "\r\n"); + if (p) + *p = '\0'; + + remove_parameter(line, "BOOT_IMAGE"); + if (kexec_flags & KEXEC_ON_CRASH) + remove_parameter(line, "crashkernel"); + + return line; +} + +/* check we retained the initrd */ +static void check_reuse_initrd(void) +{ + char *str = NULL; + char *line = get_command_line(); + + str = strstr(line, "retain_initrd"); + free(line); + + if (str == NULL) + die("unrecoverable error: current boot didn't " + "retain the initrd for reuse.\n"); +} + +char *concat_cmdline(const char *base, const char *append) +{ + char *cmdline; + if (!base && !append) + return NULL; + if (append && !base) + return xstrdup(append); + if (base && !append) + return xstrdup(base); + cmdline = xmalloc(strlen(base) + 1 + strlen(append) + 1); + strcpy(cmdline, base); + strcat(cmdline, " "); + strcat(cmdline, append); + return cmdline; +} + +void cmdline_add_liveupdate(char **base) +{ + uint64_t lu_start, lu_end, lu_sizeM; + char *str; + char buf[64]; + size_t len; + + if ( !xen_present() ) + return; + + xen_get_kexec_range(KEXEC_RANGE_MA_LIVEUPDATE, &lu_start, &lu_end); + lu_sizeM = (lu_end - lu_start) / (1024 * 1024) + 1; + sprintf(buf, " liveupdate=%lluM@0x%llx", (unsigned long long)lu_sizeM, + (unsigned long long)lu_start); + len = strlen(*base) + strlen(buf) + 1; + str = xmalloc(len); + sprintf(str, "%s%s", *base, buf); + *base = str; +} + +/* New file based kexec system call related code */ +static int do_kexec_file_load(int fileind, int argc, char **argv, + unsigned long flags) { + + char *kernel; + int kernel_fd, i; + struct kexec_info info; + int ret = 0; + char *kernel_buf; + off_t kernel_size; + + memset(&info, 0, sizeof(info)); + info.segment = NULL; + info.nr_segments = 0; + info.entry = NULL; + info.backup_start = 0; + info.kexec_flags = flags; + + info.file_mode = 1; + info.kernel_fd = -1; + info.initrd_fd = -1; + + if (!is_kexec_file_load_implemented()) + return EFALLBACK; + + if (argc - fileind <= 0) { + fprintf(stderr, "No kernel specified\n"); + usage(); + return EFAILED; + } + + kernel = argv[fileind]; + + /* slurp in the input kernel */ + kernel_buf = slurp_decompress_file(kernel, &kernel_size); + if (!kernel_buf) { + fprintf(stderr, "Failed to decompress file %s:%s\n", kernel, + strerror(errno)); + return EFAILED; + } + kernel_fd = copybuf_memfd(kernel_buf, kernel_size); + if (kernel_fd < 0) { + fprintf(stderr, "Failed to copy decompressed buf\n"); + return EFAILED; + } + + for (i = 0; i < file_types; i++) { + if (file_type[i].probe(kernel_buf, kernel_size) >= 0) + break; + } + + if (i == file_types) { + fprintf(stderr, "Cannot determine the file type " "of %s\n", + kernel); + close(kernel_fd); + return EFAILED; + } + + ret = file_type[i].load(argc, argv, kernel_buf, kernel_size, &info); + if (ret < 0) { + fprintf(stderr, "Cannot load %s\n", kernel); + close(kernel_fd); + return ret; + } + + /* + * image type specific load functioin detect the capsule kernel type + * and create another fd for file load. For example the zboot kernel. + */ + if (info.kernel_fd != -1) + kernel_fd = info.kernel_fd; + + /* + * If there is no initramfs, set KEXEC_FILE_NO_INITRAMFS flag so that + * kernel does not return error with negative initrd_fd. + */ + if (info.initrd_fd == -1) + info.kexec_flags |= KEXEC_FILE_NO_INITRAMFS; + + ret = kexec_file_load(kernel_fd, info.initrd_fd, info.command_line_len, + info.command_line, info.kexec_flags); + if (ret != 0) { + switch (errno) { + /* + * Something failed with signature verification. + * Reject the image. + */ + case ELIBBAD: + case EKEYREJECTED: + case ENOPKG: + case ENOKEY: + case EBADMSG: + case EMSGSIZE: + /* Reject by default. */ + default: + fprintf(stderr, "kexec_file_load failed: %s\n", strerror(errno)); + ret = EFAILED; + break; + + /* Not implemented. */ + case ENOSYS: + /* + * Parsing image or other options failed + * The image may be invalid or image + * type may not supported by kernel so + * retry parsing in kexec-tools. + */ + case EINVAL: + case ENOEXEC: + /* + * ENOTSUP can be unsupported image + * type or unsupported PE signature + * wrapper type, duh. + */ + case ENOTSUP: + ret = EFALLBACK; + break; + } + } + + close(kernel_fd); + return ret; +} + +static void print_crashkernel_region_size(void) +{ + uint64_t start = 0, end = 0; + + if (is_crashkernel_mem_reserved() && + get_crash_kernel_load_range(&start, &end)) { + fprintf(stderr, "get_crash_kernel_load_range() failed.\n"); + return; + } + + printf("%" PRIu64 "\n", (start != end) ? (end - start + 1) : 0UL); +} + +int main(int argc, char *argv[]) +{ + int has_opt_load = 0; + int do_load = 1; + int do_exec = 0; + int do_load_jump_back_helper = 0; + int do_shutdown = 1; + int do_sync = 1, skip_sync = 0; + int do_ifdown = 0, skip_ifdown = 0; + int do_unload = 0; + int do_reuse_initrd = 0; + int do_kexec_file_syscall = 1; + int do_kexec_fallback = 1; + int skip_checks = 0; + int do_status = 0; + void *entry = 0; + char *type = 0; + char *endptr; + int opt; + int result = 0; + int fileind; + static const struct option options[] = { + KEXEC_ALL_OPTIONS + { 0, 0, 0, 0}, + }; + static const char short_options[] = KEXEC_ALL_OPT_STR; + + /* Reset getopt for the next pass. */ + opterr = 1; + optind = 1; + + while ((opt = getopt_long(argc, argv, short_options, + options, 0)) != -1) { + switch(opt) { + case '?': + usage(); + return 1; + case OPT_HELP: + usage(); + return 0; + case OPT_VERSION: + version(); + return 0; + case OPT_DEBUG: + kexec_debug = 1; + break; + case OPT_NOIFDOWN: + skip_ifdown = 1; + break; + case OPT_NOSYNC: + skip_sync = 1; + break; + case OPT_FORCE: + do_load = 1; + do_shutdown = 0; + do_sync = 1; + do_ifdown = 1; + do_exec = 1; + break; + case OPT_LOAD: + has_opt_load = 1; + do_load = 1; + do_exec = 0; + do_shutdown = 0; + break; + case OPT_UNLOAD: + do_load = 0; + do_shutdown = 0; + do_sync = 0; + do_unload = 1; + kexec_file_flags |= KEXEC_FILE_UNLOAD; + break; + case OPT_EXEC_LIVE_UPDATE: + if ( !xen_present() ) { + fprintf(stderr, "--exec-live-update only works under xen.\n"); + return 1; + } + kexec_flags |= KEXEC_LIVE_UPDATE; + /* fallthrough */ + case OPT_EXEC: + do_load = 0; + do_shutdown = 0; + do_sync = 1; + do_ifdown = 1; + do_exec = 1; + break; + case OPT_LOAD_JUMP_BACK_HELPER: + do_load = 0; + do_shutdown = 0; + do_sync = 1; + do_ifdown = 1; + do_exec = 0; + do_load_jump_back_helper = 1; + kexec_flags = KEXEC_PRESERVE_CONTEXT; + break; + case OPT_ENTRY: + entry = (void *)strtoul(optarg, &endptr, 0); + if (*endptr) { + fprintf(stderr, + "Bad option value in --entry=%s\n", + optarg); + usage(); + return 1; + } + break; + case OPT_LOAD_PRESERVE_CONTEXT: + case OPT_LOAD_LIVE_UPDATE: + do_load = 1; + do_exec = 0; + do_shutdown = 0; + do_sync = 1; + kexec_flags = (opt == OPT_LOAD_PRESERVE_CONTEXT) ? + KEXEC_PRESERVE_CONTEXT : KEXEC_LIVE_UPDATE; + break; + case OPT_TYPE: + type = optarg; + break; + case OPT_PANIC: + do_load = 1; + do_exec = 0; + do_shutdown = 0; + do_sync = 0; + kexec_file_flags |= KEXEC_FILE_ON_CRASH; + kexec_flags = KEXEC_ON_CRASH; + break; + case OPT_MEM_MIN: + mem_min = strtoul(optarg, &endptr, 0); + if (*endptr) { + fprintf(stderr, + "Bad option value in --mem-min=%s\n", + optarg); + usage(); + return 1; + } + break; + case OPT_MEM_MAX: + mem_max = strtoul(optarg, &endptr, 0); + if (*endptr) { + fprintf(stderr, + "Bad option value in --mem-max=%s\n", + optarg); + usage(); + return 1; + } + break; + case OPT_REUSE_INITRD: + do_reuse_initrd = 1; + break; + case OPT_KEXEC_FILE_SYSCALL: + do_kexec_file_syscall = 1; + do_kexec_fallback = 0; + break; + case OPT_KEXEC_SYSCALL: + do_kexec_file_syscall = 0; + do_kexec_fallback = 0; + break; + case OPT_KEXEC_SYSCALL_AUTO: + do_kexec_file_syscall = 1; + do_kexec_fallback = 1; + break; + case OPT_NOCHECKS: + skip_checks = 1; + break; + case OPT_STATUS: + do_status = 1; + break; + case OPT_PRINT_CKR_SIZE: + print_crashkernel_region_size(); + return 0; + default: + break; + } + } + + if (skip_ifdown) + do_ifdown = 0; + if (skip_sync) + do_sync = 0; + + if (do_status) { + if (kexec_flags == 0 && !has_opt_load) + kexec_flags = KEXEC_ON_CRASH; + do_load = 0; + do_reuse_initrd = 0; + do_unload = 0; + do_load = 0; + do_shutdown = 0; + do_sync = 0; + do_ifdown = 0; + do_exec = 0; + do_load_jump_back_helper = 0; + } + + if (do_load && + ((kexec_flags & KEXEC_ON_CRASH) || + (kexec_file_flags & KEXEC_FILE_ON_CRASH)) && + !is_crashkernel_mem_reserved()) { + die("Memory for crashkernel is not reserved\n" + "Please reserve memory by passing" + "\"crashkernel=Y@X\" parameter to kernel\n" + "Then try to loading kdump kernel\n"); + } + + if (do_load && (kexec_flags & KEXEC_PRESERVE_CONTEXT) && + mem_max == ULONG_MAX) { + die("Please specify memory range used by kexeced kernel\n" + "to preserve the context of original kernel with \n" + "\"--mem-max\" parameter\n"); + } + + if (do_load && (kexec_flags & KEXEC_LIVE_UPDATE) && + !xen_present()) { + die("--load-live-update can only be used with xen\n"); + } + + fileind = optind; + /* Reset getopt for the next pass; called in other source modules */ + opterr = 1; + optind = 1; + + result = arch_process_options(argc, argv); + + /* Check for bogus options */ + if (!do_load) { + while((opt = getopt_long(argc, argv, short_options, + options, 0)) != -1) { + if ((opt == '?') || (opt >= OPT_ARCH_MAX)) { + usage(); + return 1; + } + } + } + if (do_kexec_file_syscall) { + if (do_load_jump_back_helper && !do_kexec_fallback) + die("--load-jump-back-helper not supported with kexec_file_load\n"); + if (kexec_flags & KEXEC_PRESERVE_CONTEXT) + die("--load-preserve-context not supported with kexec_file_load\n"); + } + + if (do_reuse_initrd){ + check_reuse_initrd(); + arch_reuse_initrd(); + } + if (do_status) { + result = k_status(kexec_flags); + } + if (do_unload) { + if (do_kexec_file_syscall) { + result = kexec_file_unload(kexec_file_flags); + if (result == EFALLBACK && do_kexec_fallback) { + /* Reset getopt for fallback */ + opterr = 1; + optind = 1; + do_kexec_file_syscall = 0; + } + } + if (!do_kexec_file_syscall) + result = k_unload(kexec_flags); + } + if (do_load && (result == 0)) { + if (do_kexec_file_syscall) { + result = do_kexec_file_load(fileind, argc, argv, + kexec_file_flags); + if (result == EFALLBACK && do_kexec_fallback) { + /* Reset getopt for fallback */ + opterr = 1; + optind = 1; + do_kexec_file_syscall = 0; + } + } + if (!do_kexec_file_syscall) + result = my_load(type, fileind, argc, argv, + kexec_flags, skip_checks, entry); + } + /* Don't shutdown unless there is something to reboot to! */ + if ((result == 0) && (do_shutdown || do_exec) && !kexec_loaded(KEXEC_LOADED_PATH)) { + die("Nothing has been loaded!\n"); + } + if ((result == 0) && do_shutdown) { + result = my_shutdown(); + } + if ((result == 0) && do_sync) { + sync(); + } + if ((result == 0) && do_ifdown) { + ifdown(); + } + if ((result == 0) && do_exec) { + result = my_exec(); + } + if ((result == 0) && do_load_jump_back_helper) { + result = my_load_jump_back_helper(kexec_flags, entry); + } + if (result == EFALLBACK) + fputs("syscall kexec_file_load not available.\n", stderr); + + fflush(stdout); + fflush(stderr); + return result; +} diff --git a/kexec/kexec.h b/kexec/kexec.h new file mode 100644 index 0000000..ed3b499 --- /dev/null +++ b/kexec/kexec.h @@ -0,0 +1,350 @@ +#ifndef KEXEC_H +#define KEXEC_H + +#include "config.h" + +#include <sys/types.h> +#include <stdio.h> +#include <stdint.h> +#include <string.h> +#define USE_BSD +#include <byteswap.h> +#include <endian.h> +#define _GNU_SOURCE + +#include "kexec-elf.h" +#include "unused.h" + +#ifndef BYTE_ORDER +#error BYTE_ORDER not defined +#endif + +#ifndef LITTLE_ENDIAN +#error LITTLE_ENDIAN not defined +#endif + +#ifndef BIG_ENDIAN +#error BIG_ENDIAN not defined +#endif + +#if BYTE_ORDER == LITTLE_ENDIAN +#define cpu_to_le16(val) (val) +#define cpu_to_le32(val) (val) +#define cpu_to_le64(val) (val) +#define cpu_to_be16(val) bswap_16(val) +#define cpu_to_be32(val) bswap_32(val) +#define cpu_to_be64(val) bswap_64(val) +#define le16_to_cpu(val) (val) +#define le32_to_cpu(val) (val) +#define le64_to_cpu(val) (val) +#define be16_to_cpu(val) bswap_16(val) +#define be32_to_cpu(val) bswap_32(val) +#define be64_to_cpu(val) bswap_64(val) +#elif BYTE_ORDER == BIG_ENDIAN +#define cpu_to_le16(val) bswap_16(val) +#define cpu_to_le32(val) bswap_32(val) +#define cpu_to_le64(val) bswap_64(val) +#define cpu_to_be16(val) (val) +#define cpu_to_be32(val) (val) +#define cpu_to_be64(val) (val) +#define le16_to_cpu(val) bswap_16(val) +#define le32_to_cpu(val) bswap_32(val) +#define le64_to_cpu(val) bswap_64(val) +#define be16_to_cpu(val) (val) +#define be32_to_cpu(val) (val) +#define be64_to_cpu(val) (val) +#else +#error unknwon BYTE_ORDER +#endif + +/* + * Document some of the reasons why crashdump may fail, so we can give + * better error messages + */ +#define EFAILED -1 /* default error code */ +#define ENOCRASHKERNEL -2 /* no memory reserved for crashkernel */ +#define EFALLBACK -3 /* fallback to kexec_load(2) may work */ + +/* + * This function doesn't actually exist. The idea is that when someone + * uses the macros below with an unsupported size (datatype), the linker + * will alert us to the problem via an unresolved reference error. + */ +extern unsigned long bad_unaligned_access_length (void); + +#define get_unaligned(loc) \ +({ \ + __typeof__(*(loc)) _v; \ + size_t size = sizeof(*(loc)); \ + switch(size) { \ + case 1: case 2: case 4: case 8: \ + memcpy(&_v, (loc), size); \ + break; \ + default: \ + _v = bad_unaligned_access_length(); \ + break; \ + } \ + _v; \ +}) + +#define put_unaligned(value, loc) \ +do { \ + size_t size = sizeof(*(loc)); \ + __typeof__(*(loc)) _v = value; \ + switch(size) { \ + case 1: case 2: case 4: case 8: \ + memcpy((loc), &_v, size); \ + break; \ + default: \ + bad_unaligned_access_length(); \ + break; \ + } \ +} while(0) + +#define _ALIGN_UP_MASK(addr, mask) (((addr) + (mask)) & ~(mask)) +#define _ALIGN_DOWN_MASK(addr, mask) ((addr) & ~(mask)) + +/* align addr on a size boundary - adjust address up/down if needed */ +#define _ALIGN_UP(addr, size) \ + _ALIGN_UP_MASK(addr, (typeof(addr))(size) - 1) +#define _ALIGN_DOWN(addr, size) \ + _ALIGN_DOWN_MASK(addr, (typeof(addr))(size) - 1) + +/* align addr on a size boundary - adjust address up if needed */ +#define _ALIGN(addr, size) _ALIGN_UP(addr, size) + +extern unsigned long long mem_min, mem_max; +extern int kexec_debug; + +#define dbgprintf(...) \ +do { \ + if (kexec_debug) \ + fprintf(stderr, __VA_ARGS__); \ +} while(0) + +struct kexec_segment { + const void *buf; + size_t bufsz; + const void *mem; + size_t memsz; +}; + +struct memory_range { + unsigned long long start, end; + unsigned type; +#define RANGE_RAM 0 +#define RANGE_RESERVED 1 +#define RANGE_ACPI 2 +#define RANGE_ACPI_NVS 3 +#define RANGE_UNCACHED 4 +#define RANGE_PMEM 6 +#define RANGE_PRAM 11 +}; + +struct memory_ranges { + unsigned int size; + unsigned int max_size; + struct memory_range *ranges; +}; + +struct kexec_info { + struct kexec_segment *segment; + int nr_segments; + struct memory_range *memory_range; + int memory_ranges; + struct memory_range *crash_range; + int nr_crash_ranges; + void *entry; + struct mem_ehdr rhdr; + unsigned long backup_start; + unsigned long kexec_flags; + unsigned long backup_src_start; + unsigned long backup_src_size; + /* Set to 1 if we are using kexec file syscall */ + unsigned long file_mode :1; + + /* Filled by kernel image processing code */ + int kernel_fd; + int initrd_fd; + char *command_line; + int command_line_len; + + int skip_checks; +}; + +struct arch_map_entry { + const char *machine; + unsigned long arch; +}; + +extern const struct arch_map_entry arches[]; +long physical_arch(void); + +void usage(void); +int get_memory_ranges(struct memory_range **range, int *ranges, + unsigned long kexec_flags); +int valid_memory_range(struct kexec_info *info, + unsigned long sstart, unsigned long send); +void print_segments(FILE *file, struct kexec_info *info); +int sort_segments(struct kexec_info *info); +unsigned long locate_hole(struct kexec_info *info, + unsigned long hole_size, unsigned long hole_align, + unsigned long hole_min, unsigned long hole_max, + int hole_end); + +typedef int (probe_t)(const char *kernel_buf, off_t kernel_size); +typedef int (load_t )(int argc, char **argv, + const char *kernel_buf, off_t kernel_size, + struct kexec_info *info); +typedef void (usage_t)(void); +struct file_type { + const char *name; + probe_t *probe; + load_t *load; + usage_t *usage; +}; + +extern struct file_type file_type[]; +extern int file_types; + +#define OPT_HELP 'h' +#define OPT_VERSION 'v' +#define OPT_DEBUG 'd' +#define OPT_FORCE 'f' +#define OPT_NOCHECKS 'i' +#define OPT_NOIFDOWN 'x' +#define OPT_NOSYNC 'y' +#define OPT_EXEC 'e' +#define OPT_LOAD 'l' +#define OPT_UNLOAD 'u' +#define OPT_TYPE 't' +#define OPT_PANIC 'p' +#define OPT_KEXEC_FILE_SYSCALL 's' +#define OPT_KEXEC_SYSCALL 'c' +#define OPT_KEXEC_SYSCALL_AUTO 'a' +#define OPT_STATUS 'S' +#define OPT_MEM_MIN 256 +#define OPT_MEM_MAX 257 +#define OPT_REUSE_INITRD 258 +#define OPT_LOAD_PRESERVE_CONTEXT 259 +#define OPT_LOAD_JUMP_BACK_HELPER 260 +#define OPT_ENTRY 261 +#define OPT_PRINT_CKR_SIZE 262 +#define OPT_LOAD_LIVE_UPDATE 263 +#define OPT_EXEC_LIVE_UPDATE 264 +#define OPT_MAX 265 +#define KEXEC_OPTIONS \ + { "help", 0, 0, OPT_HELP }, \ + { "version", 0, 0, OPT_VERSION }, \ + { "force", 0, 0, OPT_FORCE }, \ + { "no-checks", 0, 0, OPT_NOCHECKS }, \ + { "no-ifdown", 0, 0, OPT_NOIFDOWN }, \ + { "no-sync", 0, 0, OPT_NOSYNC }, \ + { "load", 0, 0, OPT_LOAD }, \ + { "unload", 0, 0, OPT_UNLOAD }, \ + { "exec", 0, 0, OPT_EXEC }, \ + { "exec-live-update", 0, 0, OPT_EXEC_LIVE_UPDATE}, \ + { "load-preserve-context", 0, 0, OPT_LOAD_PRESERVE_CONTEXT}, \ + { "load-jump-back-helper", 0, 0, OPT_LOAD_JUMP_BACK_HELPER }, \ + { "load-live-update", 0, 0, OPT_LOAD_LIVE_UPDATE }, \ + { "entry", 1, 0, OPT_ENTRY }, \ + { "type", 1, 0, OPT_TYPE }, \ + { "load-panic", 0, 0, OPT_PANIC }, \ + { "mem-min", 1, 0, OPT_MEM_MIN }, \ + { "mem-max", 1, 0, OPT_MEM_MAX }, \ + { "reuseinitrd", 0, 0, OPT_REUSE_INITRD }, \ + { "kexec-file-syscall", 0, 0, OPT_KEXEC_FILE_SYSCALL }, \ + { "kexec-syscall", 0, 0, OPT_KEXEC_SYSCALL }, \ + { "kexec-syscall-auto", 0, 0, OPT_KEXEC_SYSCALL_AUTO }, \ + { "debug", 0, 0, OPT_DEBUG }, \ + { "status", 0, 0, OPT_STATUS }, \ + { "print-ckr-size", 0, 0, OPT_PRINT_CKR_SIZE }, \ + +#define KEXEC_OPT_STR "h?vdfixyluet:pscaS" + +extern void dbgprint_mem_range(const char *prefix, struct memory_range *mr, int nr_mr); +extern void die(const char *fmt, ...) + __attribute__ ((format (printf, 1, 2))); +extern void *xmalloc(size_t size); +extern void *xrealloc(void *ptr, size_t size); +extern char *slurp_file(const char *filename, off_t *r_size); +extern char *slurp_file_mmap(const char *filename, off_t *r_size); +extern char *slurp_file_len(const char *filename, off_t size, off_t *nread); +extern char *slurp_decompress_file(const char *filename, off_t *r_size); +extern unsigned long virt_to_phys(unsigned long addr); +extern void add_segment(struct kexec_info *info, + const void *buf, size_t bufsz, unsigned long base, size_t memsz); +extern void add_segment_phys_virt(struct kexec_info *info, + const void *buf, size_t bufsz, unsigned long base, size_t memsz, + int phys); +extern unsigned long add_buffer(struct kexec_info *info, + const void *buf, unsigned long bufsz, unsigned long memsz, + unsigned long buf_align, unsigned long buf_min, unsigned long buf_max, + int buf_end); +extern unsigned long add_buffer_virt(struct kexec_info *info, + const void *buf, unsigned long bufsz, unsigned long memsz, + unsigned long buf_align, unsigned long buf_min, unsigned long buf_max, + int buf_end); +extern unsigned long add_buffer_phys_virt(struct kexec_info *info, + const void *buf, unsigned long bufsz, unsigned long memsz, + unsigned long buf_align, unsigned long buf_min, unsigned long buf_max, + int buf_end, int phys); +extern void arch_reuse_initrd(void); + +extern int ifdown(void); + +extern char purgatory[]; +extern size_t purgatory_size; + +#define BOOTLOADER "kexec" +#define BOOTLOADER_VERSION PACKAGE_VERSION + +void arch_usage(void); +int arch_process_options(int argc, char **argv); +int arch_compat_trampoline(struct kexec_info *info); +void arch_update_purgatory(struct kexec_info *info); +int is_crashkernel_mem_reserved(void); +int get_crash_kernel_load_range(uint64_t *start, uint64_t *end); +void remove_parameter(char *line, const char *param_name); +char *get_command_line(void); + +int kexec_iomem_for_each_line(char *match, + int (*callback)(void *data, + int nr, + char *str, + unsigned long long base, + unsigned long long length), + void *data); +int parse_iomem_single(char *str, uint64_t *start, uint64_t *end); +const char * proc_iomem(void); + +#define MAX_LINE 160 + +char *concat_cmdline(const char *base, const char *append); +void cmdline_add_liveupdate(char **base); + +int xen_present(void); +int xen_kexec_load(struct kexec_info *info); +int xen_kexec_unload(uint64_t kexec_flags); +int xen_kexec_exec(uint64_t kexec_flags); +int xen_kexec_status(uint64_t kexec_flags); + +extern unsigned long long get_kernel_sym(const char *text); + +/* Converts unsigned long to ascii string. */ +static inline void ultoa(unsigned long val, char *str) +{ + char buf[36]; + int len = 0, pos = 0; + + do { + buf[len++] = val % 10; + val /= 10; + } while (val); + + while (len) + str[pos++] = buf[--len] + '0'; + str[pos] = 0; +} + +#endif /* KEXEC_H */ diff --git a/kexec/libfdt/Makefile.libfdt b/kexec/libfdt/Makefile.libfdt new file mode 100644 index 0000000..9a2b4f7 --- /dev/null +++ b/kexec/libfdt/Makefile.libfdt @@ -0,0 +1,13 @@ +# Makefile.libfdt +# +# This is not a complete Makefile of itself. Instead, it is designed to +# be easily embeddable into other systems of Makefiles. +# +LIBFDT_INCLUDES = fdt.h libfdt.h +LIBFDT_SRCS = fdt.c fdt_ro.c fdt_wip.c fdt_sw.c fdt_rw.c fdt_strerror.c +LIBFDT_OBJS = $(LIBFDT_SRCS:%.c=%.o) + +dist += kexec/libfdt/Makefile.libfdt \ + kexec/libfdt/fdt.h kexec/libfdt/libfdt.h \ + kexec/libfdt/libfdt_env.h \ + kexec/libfdt/libfdt_internal.h diff --git a/kexec/libfdt/fdt.c b/kexec/libfdt/fdt.c new file mode 100644 index 0000000..2acaec5 --- /dev/null +++ b/kexec/libfdt/fdt.c @@ -0,0 +1,201 @@ +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * libfdt is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * + * a) This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * Alternatively, + * + * b) Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "libfdt_env.h" + +#include <fdt.h> +#include <libfdt.h> + +#include "libfdt_internal.h" + +int fdt_check_header(const void *fdt) +{ + if (fdt_magic(fdt) == FDT_MAGIC) { + /* Complete tree */ + if (fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION) + return -FDT_ERR_BADVERSION; + if (fdt_last_comp_version(fdt) > FDT_LAST_SUPPORTED_VERSION) + return -FDT_ERR_BADVERSION; + } else if (fdt_magic(fdt) == FDT_SW_MAGIC) { + /* Unfinished sequential-write blob */ + if (fdt_size_dt_struct(fdt) == 0) + return -FDT_ERR_BADSTATE; + } else { + return -FDT_ERR_BADMAGIC; + } + + return 0; +} + +const void *fdt_offset_ptr(const void *fdt, int offset, int len) +{ + const char *p; + + if (fdt_version(fdt) >= 0x11) + if (((offset + len) < offset) + || ((offset + len) > fdt_size_dt_struct(fdt))) + return NULL; + + p = _fdt_offset_ptr(fdt, offset); + + if (p + len < p) + return NULL; + return p; +} + +uint32_t fdt_next_tag(const void *fdt, int offset, int *nextoffset) +{ + const uint32_t *tagp, *lenp; + uint32_t tag; + const char *p; + + if (offset % FDT_TAGSIZE) + return -1; + + tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE); + if (! tagp) + return FDT_END; /* premature end */ + tag = fdt32_to_cpu(*tagp); + offset += FDT_TAGSIZE; + + switch (tag) { + case FDT_BEGIN_NODE: + /* skip name */ + do { + p = fdt_offset_ptr(fdt, offset++, 1); + } while (p && (*p != '\0')); + if (! p) + return FDT_END; + break; + case FDT_PROP: + lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp)); + if (! lenp) + return FDT_END; + /* skip name offset, length and value */ + offset += 2*FDT_TAGSIZE + fdt32_to_cpu(*lenp); + break; + } + + if (nextoffset) + *nextoffset = FDT_TAGALIGN(offset); + + return tag; +} + +int _fdt_check_node_offset(const void *fdt, int offset) +{ + if ((offset < 0) || (offset % FDT_TAGSIZE) + || (fdt_next_tag(fdt, offset, &offset) != FDT_BEGIN_NODE)) + return -FDT_ERR_BADOFFSET; + + return offset; +} + +int fdt_next_node(const void *fdt, int offset, int *depth) +{ + int nextoffset = 0; + uint32_t tag; + + if (offset >= 0) + if ((nextoffset = _fdt_check_node_offset(fdt, offset)) < 0) + return nextoffset; + + do { + offset = nextoffset; + tag = fdt_next_tag(fdt, offset, &nextoffset); + + switch (tag) { + case FDT_PROP: + case FDT_NOP: + break; + + case FDT_BEGIN_NODE: + if (depth) + (*depth)++; + break; + + case FDT_END_NODE: + if (depth) + (*depth)--; + break; + + case FDT_END: + return -FDT_ERR_NOTFOUND; + + default: + return -FDT_ERR_BADSTRUCTURE; + } + } while (tag != FDT_BEGIN_NODE); + + return offset; +} + +const char *_fdt_find_string(const char *strtab, int tabsize, const char *s) +{ + int len = strlen(s) + 1; + const char *last = strtab + tabsize - len; + const char *p; + + for (p = strtab; p <= last; p++) + if (memcmp(p, s, len) == 0) + return p; + return NULL; +} + +int fdt_move(const void *fdt, void *buf, int bufsize) +{ + FDT_CHECK_HEADER(fdt); + + if (fdt_totalsize(fdt) > bufsize) + return -FDT_ERR_NOSPACE; + + memmove(buf, fdt, fdt_totalsize(fdt)); + return 0; +} diff --git a/kexec/libfdt/fdt.h b/kexec/libfdt/fdt.h new file mode 100644 index 0000000..48ccfd9 --- /dev/null +++ b/kexec/libfdt/fdt.h @@ -0,0 +1,60 @@ +#ifndef _FDT_H +#define _FDT_H + +#ifndef __ASSEMBLY__ + +struct fdt_header { + uint32_t magic; /* magic word FDT_MAGIC */ + uint32_t totalsize; /* total size of DT block */ + uint32_t off_dt_struct; /* offset to structure */ + uint32_t off_dt_strings; /* offset to strings */ + uint32_t off_mem_rsvmap; /* offset to memory reserve map */ + uint32_t version; /* format version */ + uint32_t last_comp_version; /* last compatible version */ + + /* version 2 fields below */ + uint32_t boot_cpuid_phys; /* Which physical CPU id we're + booting on */ + /* version 3 fields below */ + uint32_t size_dt_strings; /* size of the strings block */ + + /* version 17 fields below */ + uint32_t size_dt_struct; /* size of the structure block */ +}; + +struct fdt_reserve_entry { + uint64_t address; + uint64_t size; +}; + +struct fdt_node_header { + uint32_t tag; + char name[0]; +}; + +struct fdt_property { + uint32_t tag; + uint32_t len; + uint32_t nameoff; + char data[0]; +}; + +#endif /* !__ASSEMBLY */ + +#define FDT_MAGIC 0xd00dfeed /* 4: version, 4: total size */ +#define FDT_TAGSIZE sizeof(uint32_t) + +#define FDT_BEGIN_NODE 0x1 /* Start node: full name */ +#define FDT_END_NODE 0x2 /* End node */ +#define FDT_PROP 0x3 /* Property: name off, + size, content */ +#define FDT_NOP 0x4 /* nop */ +#define FDT_END 0x9 + +#define FDT_V1_SIZE (7*sizeof(uint32_t)) +#define FDT_V2_SIZE (FDT_V1_SIZE + sizeof(uint32_t)) +#define FDT_V3_SIZE (FDT_V2_SIZE + sizeof(uint32_t)) +#define FDT_V16_SIZE FDT_V3_SIZE +#define FDT_V17_SIZE (FDT_V16_SIZE + sizeof(uint32_t)) + +#endif /* _FDT_H */ diff --git a/kexec/libfdt/fdt_ro.c b/kexec/libfdt/fdt_ro.c new file mode 100644 index 0000000..129b532 --- /dev/null +++ b/kexec/libfdt/fdt_ro.c @@ -0,0 +1,466 @@ +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * libfdt is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * + * a) This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * Alternatively, + * + * b) Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "libfdt_env.h" + +#include <fdt.h> +#include <libfdt.h> + +#include "libfdt_internal.h" + +static int _fdt_nodename_eq(const void *fdt, int offset, + const char *s, int len) +{ + const char *p = fdt_offset_ptr(fdt, offset + FDT_TAGSIZE, len+1); + + if (! p) + /* short match */ + return 0; + + if (memcmp(p, s, len) != 0) + return 0; + + if (p[len] == '\0') + return 1; + else if (!memchr(s, '@', len) && (p[len] == '@')) + return 1; + else + return 0; +} + +const char *fdt_string(const void *fdt, int stroffset) +{ + return (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset; +} + +int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size) +{ + FDT_CHECK_HEADER(fdt); + *address = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->address); + *size = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->size); + return 0; +} + +int fdt_num_mem_rsv(const void *fdt) +{ + int i = 0; + + while (fdt64_to_cpu(_fdt_mem_rsv(fdt, i)->size) != 0) + i++; + return i; +} + +int fdt_subnode_offset_namelen(const void *fdt, int offset, + const char *name, int namelen) +{ + int depth; + + FDT_CHECK_HEADER(fdt); + + for (depth = 0; + offset >= 0; + offset = fdt_next_node(fdt, offset, &depth)) { + if (depth < 0) + return -FDT_ERR_NOTFOUND; + else if ((depth == 1) + && _fdt_nodename_eq(fdt, offset, name, namelen)) + return offset; + } + + return offset; /* error */ +} + +int fdt_subnode_offset(const void *fdt, int parentoffset, + const char *name) +{ + return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name)); +} + +int fdt_path_offset(const void *fdt, const char *path) +{ + const char *end = path + strlen(path); + const char *p = path; + int offset = 0; + + FDT_CHECK_HEADER(fdt); + + if (*path != '/') + return -FDT_ERR_BADPATH; + + while (*p) { + const char *q; + + while (*p == '/') + p++; + if (! *p) + return offset; + q = strchr(p, '/'); + if (! q) + q = end; + + offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p); + if (offset < 0) + return offset; + + p = q; + } + + return offset; +} + +const char *fdt_get_name(const void *fdt, int nodeoffset, int *len) +{ + const struct fdt_node_header *nh = _fdt_offset_ptr(fdt, nodeoffset); + int err; + + if (((err = fdt_check_header(fdt)) != 0) + || ((err = _fdt_check_node_offset(fdt, nodeoffset)) < 0)) + goto fail; + + if (len) + *len = strlen(nh->name); + + return nh->name; + + fail: + if (len) + *len = err; + return NULL; +} + +const struct fdt_property *fdt_get_property(const void *fdt, + int nodeoffset, + const char *name, int *lenp) +{ + uint32_t tag; + const struct fdt_property *prop; + int namestroff; + int offset, nextoffset; + int err; + + if (((err = fdt_check_header(fdt)) != 0) + || ((err = _fdt_check_node_offset(fdt, nodeoffset)) < 0)) + goto fail; + + nextoffset = err; + do { + offset = nextoffset; + + tag = fdt_next_tag(fdt, offset, &nextoffset); + switch (tag) { + case FDT_END: + err = -FDT_ERR_TRUNCATED; + goto fail; + + case FDT_BEGIN_NODE: + case FDT_END_NODE: + case FDT_NOP: + break; + + case FDT_PROP: + err = -FDT_ERR_BADSTRUCTURE; + prop = fdt_offset_ptr(fdt, offset, sizeof(*prop)); + if (! prop) + goto fail; + namestroff = fdt32_to_cpu(prop->nameoff); + if (strcmp(fdt_string(fdt, namestroff), name) == 0) { + /* Found it! */ + int len = fdt32_to_cpu(prop->len); + prop = fdt_offset_ptr(fdt, offset, + sizeof(*prop)+len); + if (! prop) + goto fail; + + if (lenp) + *lenp = len; + + return prop; + } + break; + + default: + err = -FDT_ERR_BADSTRUCTURE; + goto fail; + } + } while ((tag != FDT_BEGIN_NODE) && (tag != FDT_END_NODE)); + + err = -FDT_ERR_NOTFOUND; + fail: + if (lenp) + *lenp = err; + return NULL; +} + +const void *fdt_getprop(const void *fdt, int nodeoffset, + const char *name, int *lenp) +{ + const struct fdt_property *prop; + + prop = fdt_get_property(fdt, nodeoffset, name, lenp); + if (! prop) + return NULL; + + return prop->data; +} + +uint32_t fdt_get_phandle(const void *fdt, int nodeoffset) +{ + const uint32_t *php; + int len; + + php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len); + if (!php || (len != sizeof(*php))) + return 0; + + return fdt32_to_cpu(*php); +} + +int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen) +{ + int pdepth = 0, p = 0; + int offset, depth, namelen; + const char *name; + + FDT_CHECK_HEADER(fdt); + + if (buflen < 2) + return -FDT_ERR_NOSPACE; + + for (offset = 0, depth = 0; + (offset >= 0) && (offset <= nodeoffset); + offset = fdt_next_node(fdt, offset, &depth)) { + if (pdepth < depth) + continue; /* overflowed buffer */ + + while (pdepth > depth) { + do { + p--; + } while (buf[p-1] != '/'); + pdepth--; + } + + name = fdt_get_name(fdt, offset, &namelen); + if (!name) + return namelen; + if ((p + namelen + 1) <= buflen) { + memcpy(buf + p, name, namelen); + p += namelen; + buf[p++] = '/'; + pdepth++; + } + + if (offset == nodeoffset) { + if (pdepth < (depth + 1)) + return -FDT_ERR_NOSPACE; + + if (p > 1) /* special case so that root path is "/", not "" */ + p--; + buf[p] = '\0'; + return p; + } + } + + if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) + return -FDT_ERR_BADOFFSET; + else if (offset == -FDT_ERR_BADOFFSET) + return -FDT_ERR_BADSTRUCTURE; + + return offset; /* error from fdt_next_node() */ +} + +int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset, + int supernodedepth, int *nodedepth) +{ + int offset, depth; + int supernodeoffset = -FDT_ERR_INTERNAL; + + FDT_CHECK_HEADER(fdt); + + if (supernodedepth < 0) + return -FDT_ERR_NOTFOUND; + + for (offset = 0, depth = 0; + (offset >= 0) && (offset <= nodeoffset); + offset = fdt_next_node(fdt, offset, &depth)) { + if (depth == supernodedepth) + supernodeoffset = offset; + + if (offset == nodeoffset) { + if (nodedepth) + *nodedepth = depth; + + if (supernodedepth > depth) + return -FDT_ERR_NOTFOUND; + else + return supernodeoffset; + } + } + + if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) + return -FDT_ERR_BADOFFSET; + else if (offset == -FDT_ERR_BADOFFSET) + return -FDT_ERR_BADSTRUCTURE; + + return offset; /* error from fdt_next_node() */ +} + +int fdt_node_depth(const void *fdt, int nodeoffset) +{ + int nodedepth; + int err; + + err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth); + if (err) + return (err < 0) ? err : -FDT_ERR_INTERNAL; + return nodedepth; +} + +int fdt_parent_offset(const void *fdt, int nodeoffset) +{ + int nodedepth = fdt_node_depth(fdt, nodeoffset); + + if (nodedepth < 0) + return nodedepth; + return fdt_supernode_atdepth_offset(fdt, nodeoffset, + nodedepth - 1, NULL); +} + +int fdt_node_offset_by_prop_value(const void *fdt, int startoffset, + const char *propname, + const void *propval, int proplen) +{ + int offset; + const void *val; + int len; + + FDT_CHECK_HEADER(fdt); + + /* FIXME: The algorithm here is pretty horrible: we scan each + * property of a node in fdt_getprop(), then if that didn't + * find what we want, we scan over them again making our way + * to the next node. Still it's the easiest to implement + * approach; performance can come later. */ + for (offset = fdt_next_node(fdt, startoffset, NULL); + offset >= 0; + offset = fdt_next_node(fdt, offset, NULL)) { + val = fdt_getprop(fdt, offset, propname, &len); + if (val && (len == proplen) + && (memcmp(val, propval, len) == 0)) + return offset; + } + + return offset; /* error from fdt_next_node() */ +} + +int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle) +{ + if ((phandle == 0) || (phandle == -1)) + return -FDT_ERR_BADPHANDLE; + phandle = cpu_to_fdt32(phandle); + return fdt_node_offset_by_prop_value(fdt, -1, "linux,phandle", + &phandle, sizeof(phandle)); +} + +int _stringlist_contains(const char *strlist, int listlen, const char *str) +{ + int len = strlen(str); + const char *p; + + while (listlen >= len) { + if (memcmp(str, strlist, len+1) == 0) + return 1; + p = memchr(strlist, '\0', listlen); + if (!p) + return 0; /* malformed strlist.. */ + listlen -= (p-strlist) + 1; + strlist = p + 1; + } + return 0; +} + +int fdt_node_check_compatible(const void *fdt, int nodeoffset, + const char *compatible) +{ + const void *prop; + int len; + + prop = fdt_getprop(fdt, nodeoffset, "compatible", &len); + if (!prop) + return len; + if (_stringlist_contains(prop, len, compatible)) + return 0; + else + return 1; +} + +int fdt_node_offset_by_compatible(const void *fdt, int startoffset, + const char *compatible) +{ + int offset, err; + + FDT_CHECK_HEADER(fdt); + + /* FIXME: The algorithm here is pretty horrible: we scan each + * property of a node in fdt_node_check_compatible(), then if + * that didn't find what we want, we scan over them again + * making our way to the next node. Still it's the easiest to + * implement approach; performance can come later. */ + for (offset = fdt_next_node(fdt, startoffset, NULL); + offset >= 0; + offset = fdt_next_node(fdt, offset, NULL)) { + err = fdt_node_check_compatible(fdt, offset, compatible); + if ((err < 0) && (err != -FDT_ERR_NOTFOUND)) + return err; + else if (err == 0) + return offset; + } + + return offset; /* error from fdt_next_node() */ +} diff --git a/kexec/libfdt/fdt_rw.c b/kexec/libfdt/fdt_rw.c new file mode 100644 index 0000000..8e7ec4c --- /dev/null +++ b/kexec/libfdt/fdt_rw.c @@ -0,0 +1,463 @@ +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * libfdt is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * + * a) This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * Alternatively, + * + * b) Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "libfdt_env.h" + +#include <fdt.h> +#include <libfdt.h> + +#include "libfdt_internal.h" + +static int _fdt_blocks_misordered(const void *fdt, + int mem_rsv_size, int struct_size) +{ + return (fdt_off_mem_rsvmap(fdt) < FDT_ALIGN(sizeof(struct fdt_header), 8)) + || (fdt_off_dt_struct(fdt) < + (fdt_off_mem_rsvmap(fdt) + mem_rsv_size)) + || (fdt_off_dt_strings(fdt) < + (fdt_off_dt_struct(fdt) + struct_size)) + || (fdt_totalsize(fdt) < + (fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt))); +} + +static int _fdt_rw_check_header(void *fdt) +{ + FDT_CHECK_HEADER(fdt); + + if (fdt_version(fdt) < 17) + return -FDT_ERR_BADVERSION; + if (_fdt_blocks_misordered(fdt, sizeof(struct fdt_reserve_entry), + fdt_size_dt_struct(fdt))) + return -FDT_ERR_BADLAYOUT; + if (fdt_version(fdt) > 17) + fdt_set_version(fdt, 17); + + return 0; +} + +#define FDT_RW_CHECK_HEADER(fdt) \ + { \ + int err; \ + if ((err = _fdt_rw_check_header(fdt)) != 0) \ + return err; \ + } + +static inline int _fdt_data_size(void *fdt) +{ + return fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt); +} + +static int _fdt_splice(void *fdt, void *splicepoint, int oldlen, int newlen) +{ + char *p = splicepoint; + char *end = (char *)fdt + _fdt_data_size(fdt); + + if (((p + oldlen) < p) || ((p + oldlen) > end)) + return -FDT_ERR_BADOFFSET; + if ((end - oldlen + newlen) > ((char *)fdt + fdt_totalsize(fdt))) + return -FDT_ERR_NOSPACE; + memmove(p + newlen, p + oldlen, end - p - oldlen); + return 0; +} + +static int _fdt_splice_mem_rsv(void *fdt, struct fdt_reserve_entry *p, + int oldn, int newn) +{ + int delta = (newn - oldn) * sizeof(*p); + int err; + err = _fdt_splice(fdt, p, oldn * sizeof(*p), newn * sizeof(*p)); + if (err) + return err; + fdt_set_off_dt_struct(fdt, fdt_off_dt_struct(fdt) + delta); + fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta); + return 0; +} + +static int _fdt_splice_struct(void *fdt, void *p, + int oldlen, int newlen) +{ + int delta = newlen - oldlen; + int err; + + if ((err = _fdt_splice(fdt, p, oldlen, newlen))) + return err; + + fdt_set_size_dt_struct(fdt, fdt_size_dt_struct(fdt) + delta); + fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta); + return 0; +} + +static int _fdt_splice_string(void *fdt, int newlen) +{ + void *p = (char *)fdt + + fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt); + int err; + + if ((err = _fdt_splice(fdt, p, 0, newlen))) + return err; + + fdt_set_size_dt_strings(fdt, fdt_size_dt_strings(fdt) + newlen); + return 0; +} + +static int _fdt_find_add_string(void *fdt, const char *s) +{ + char *strtab = (char *)fdt + fdt_off_dt_strings(fdt); + const char *p; + char *new; + int len = strlen(s) + 1; + int err; + + p = _fdt_find_string(strtab, fdt_size_dt_strings(fdt), s); + if (p) + /* found it */ + return (p - strtab); + + new = strtab + fdt_size_dt_strings(fdt); + err = _fdt_splice_string(fdt, len); + if (err) + return err; + + memcpy(new, s, len); + return (new - strtab); +} + +int fdt_add_mem_rsv(void *fdt, uint64_t address, uint64_t size) +{ + struct fdt_reserve_entry *re; + int err; + + FDT_RW_CHECK_HEADER(fdt); + + re = _fdt_mem_rsv_w(fdt, fdt_num_mem_rsv(fdt)); + err = _fdt_splice_mem_rsv(fdt, re, 0, 1); + if (err) + return err; + + re->address = cpu_to_fdt64(address); + re->size = cpu_to_fdt64(size); + return 0; +} + +int fdt_del_mem_rsv(void *fdt, int n) +{ + struct fdt_reserve_entry *re = _fdt_mem_rsv_w(fdt, n); + int err; + + FDT_RW_CHECK_HEADER(fdt); + + if (n >= fdt_num_mem_rsv(fdt)) + return -FDT_ERR_NOTFOUND; + + err = _fdt_splice_mem_rsv(fdt, re, 1, 0); + if (err) + return err; + return 0; +} + +static int _fdt_resize_property(void *fdt, int nodeoffset, const char *name, + int len, struct fdt_property **prop) +{ + int oldlen; + int err; + + *prop = fdt_get_property_w(fdt, nodeoffset, name, &oldlen); + if (! (*prop)) + return oldlen; + + if ((err = _fdt_splice_struct(fdt, (*prop)->data, FDT_TAGALIGN(oldlen), + FDT_TAGALIGN(len)))) + return err; + + (*prop)->len = cpu_to_fdt32(len); + return 0; +} + +static int _fdt_add_property(void *fdt, int nodeoffset, const char *name, + int len, struct fdt_property **prop) +{ + int proplen; + int nextoffset; + int namestroff; + int err; + + if ((nextoffset = _fdt_check_node_offset(fdt, nodeoffset)) < 0) + return nextoffset; + + namestroff = _fdt_find_add_string(fdt, name); + if (namestroff < 0) + return namestroff; + + *prop = _fdt_offset_ptr_w(fdt, nextoffset); + proplen = sizeof(**prop) + FDT_TAGALIGN(len); + + err = _fdt_splice_struct(fdt, *prop, 0, proplen); + if (err) + return err; + + (*prop)->tag = cpu_to_fdt32(FDT_PROP); + (*prop)->nameoff = cpu_to_fdt32(namestroff); + (*prop)->len = cpu_to_fdt32(len); + return 0; +} + +int fdt_set_name(void *fdt, int nodeoffset, const char *name) +{ + char *namep; + int oldlen, newlen; + int err; + + FDT_RW_CHECK_HEADER(fdt); + + namep = (char *)(uintptr_t)fdt_get_name(fdt, nodeoffset, &oldlen); + if (!namep) + return oldlen; + + newlen = strlen(name); + + err = _fdt_splice_struct(fdt, namep, FDT_TAGALIGN(oldlen+1), + FDT_TAGALIGN(newlen+1)); + if (err) + return err; + + memcpy(namep, name, newlen+1); + return 0; +} + +int fdt_setprop(void *fdt, int nodeoffset, const char *name, + const void *val, int len) +{ + struct fdt_property *prop; + int err; + + FDT_RW_CHECK_HEADER(fdt); + + err = _fdt_resize_property(fdt, nodeoffset, name, len, &prop); + if (err == -FDT_ERR_NOTFOUND) + err = _fdt_add_property(fdt, nodeoffset, name, len, &prop); + if (err) + return err; + + memcpy(prop->data, val, len); + return 0; +} + +int fdt_delprop(void *fdt, int nodeoffset, const char *name) +{ + struct fdt_property *prop; + int len, proplen; + + FDT_RW_CHECK_HEADER(fdt); + + prop = fdt_get_property_w(fdt, nodeoffset, name, &len); + if (! prop) + return len; + + proplen = sizeof(*prop) + FDT_TAGALIGN(len); + return _fdt_splice_struct(fdt, prop, proplen, 0); +} + +int fdt_add_subnode_namelen(void *fdt, int parentoffset, + const char *name, int namelen) +{ + struct fdt_node_header *nh; + int offset, nextoffset; + int nodelen; + int err; + uint32_t tag; + uint32_t *endtag; + + FDT_RW_CHECK_HEADER(fdt); + + offset = fdt_subnode_offset_namelen(fdt, parentoffset, name, namelen); + if (offset >= 0) + return -FDT_ERR_EXISTS; + else if (offset != -FDT_ERR_NOTFOUND) + return offset; + + /* Try to place the new node after the parent's properties */ + fdt_next_tag(fdt, parentoffset, &nextoffset); /* skip the BEGIN_NODE */ + do { + offset = nextoffset; + tag = fdt_next_tag(fdt, offset, &nextoffset); + } while ((tag == FDT_PROP) || (tag == FDT_NOP)); + + nh = _fdt_offset_ptr_w(fdt, offset); + nodelen = sizeof(*nh) + FDT_TAGALIGN(namelen+1) + FDT_TAGSIZE; + + err = _fdt_splice_struct(fdt, nh, 0, nodelen); + if (err) + return err; + + nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE); + memset(nh->name, 0, FDT_TAGALIGN(namelen+1)); + memcpy(nh->name, name, namelen); + endtag = (uint32_t *)((char *)nh + nodelen - FDT_TAGSIZE); + *endtag = cpu_to_fdt32(FDT_END_NODE); + + return offset; +} + +int fdt_add_subnode(void *fdt, int parentoffset, const char *name) +{ + return fdt_add_subnode_namelen(fdt, parentoffset, name, strlen(name)); +} + +int fdt_del_node(void *fdt, int nodeoffset) +{ + int endoffset; + + FDT_RW_CHECK_HEADER(fdt); + + endoffset = _fdt_node_end_offset(fdt, nodeoffset); + if (endoffset < 0) + return endoffset; + + return _fdt_splice_struct(fdt, _fdt_offset_ptr_w(fdt, nodeoffset), + endoffset - nodeoffset, 0); +} + +static void _fdt_packblocks(const char *old, char *new, + int mem_rsv_size, int struct_size) +{ + int mem_rsv_off, struct_off, strings_off; + + mem_rsv_off = FDT_ALIGN(sizeof(struct fdt_header), 8); + struct_off = mem_rsv_off + mem_rsv_size; + strings_off = struct_off + struct_size; + + memmove(new + mem_rsv_off, old + fdt_off_mem_rsvmap(old), mem_rsv_size); + fdt_set_off_mem_rsvmap(new, mem_rsv_off); + + memmove(new + struct_off, old + fdt_off_dt_struct(old), struct_size); + fdt_set_off_dt_struct(new, struct_off); + fdt_set_size_dt_struct(new, struct_size); + + memmove(new + strings_off, old + fdt_off_dt_strings(old), + fdt_size_dt_strings(old)); + fdt_set_off_dt_strings(new, strings_off); + fdt_set_size_dt_strings(new, fdt_size_dt_strings(old)); +} + +int fdt_open_into(const void *fdt, void *buf, int bufsize) +{ + int err; + int mem_rsv_size, struct_size; + int newsize; + const char *fdtstart = fdt; + const char *fdtend = fdtstart + fdt_totalsize(fdt); + char *tmp; + + FDT_CHECK_HEADER(fdt); + + mem_rsv_size = (fdt_num_mem_rsv(fdt)+1) + * sizeof(struct fdt_reserve_entry); + + if (fdt_version(fdt) >= 17) { + struct_size = fdt_size_dt_struct(fdt); + } else { + struct_size = 0; + while (fdt_next_tag(fdt, struct_size, &struct_size) != FDT_END) + ; + } + + if (!_fdt_blocks_misordered(fdt, mem_rsv_size, struct_size)) { + /* no further work necessary */ + err = fdt_move(fdt, buf, bufsize); + if (err) + return err; + fdt_set_version(buf, 17); + fdt_set_size_dt_struct(buf, struct_size); + fdt_set_totalsize(buf, bufsize); + return 0; + } + + /* Need to reorder */ + newsize = FDT_ALIGN(sizeof(struct fdt_header), 8) + mem_rsv_size + + struct_size + fdt_size_dt_strings(fdt); + + if (bufsize < newsize) + return -FDT_ERR_NOSPACE; + + /* First attempt to build converted tree at beginning of buffer */ + tmp = buf; + /* But if that overlaps with the old tree... */ + if (((tmp + newsize) > fdtstart) && (tmp < fdtend)) { + /* Try right after the old tree instead */ + tmp = (char *)(uintptr_t)fdtend; + if ((tmp + newsize) > ((char *)buf + bufsize)) + return -FDT_ERR_NOSPACE; + } + + _fdt_packblocks(fdt, tmp, mem_rsv_size, struct_size); + memmove(buf, tmp, newsize); + + fdt_set_magic(buf, FDT_MAGIC); + fdt_set_totalsize(buf, bufsize); + fdt_set_version(buf, 17); + fdt_set_last_comp_version(buf, 16); + fdt_set_boot_cpuid_phys(buf, fdt_boot_cpuid_phys(fdt)); + + return 0; +} + +int fdt_pack(void *fdt) +{ + int mem_rsv_size; + + FDT_RW_CHECK_HEADER(fdt); + + mem_rsv_size = (fdt_num_mem_rsv(fdt)+1) + * sizeof(struct fdt_reserve_entry); + _fdt_packblocks(fdt, fdt, mem_rsv_size, fdt_size_dt_struct(fdt)); + fdt_set_totalsize(fdt, _fdt_data_size(fdt)); + + return 0; +} diff --git a/kexec/libfdt/fdt_strerror.c b/kexec/libfdt/fdt_strerror.c new file mode 100644 index 0000000..e6c3cee --- /dev/null +++ b/kexec/libfdt/fdt_strerror.c @@ -0,0 +1,96 @@ +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * libfdt is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * + * a) This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * Alternatively, + * + * b) Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "libfdt_env.h" + +#include <fdt.h> +#include <libfdt.h> + +#include "libfdt_internal.h" + +struct fdt_errtabent { + const char *str; +}; + +#define FDT_ERRTABENT(val) \ + [(val)] = { .str = #val, } + +static struct fdt_errtabent fdt_errtable[] = { + FDT_ERRTABENT(FDT_ERR_NOTFOUND), + FDT_ERRTABENT(FDT_ERR_EXISTS), + FDT_ERRTABENT(FDT_ERR_NOSPACE), + + FDT_ERRTABENT(FDT_ERR_BADOFFSET), + FDT_ERRTABENT(FDT_ERR_BADPATH), + FDT_ERRTABENT(FDT_ERR_BADSTATE), + + FDT_ERRTABENT(FDT_ERR_TRUNCATED), + FDT_ERRTABENT(FDT_ERR_BADMAGIC), + FDT_ERRTABENT(FDT_ERR_BADVERSION), + FDT_ERRTABENT(FDT_ERR_BADSTRUCTURE), + FDT_ERRTABENT(FDT_ERR_BADLAYOUT), +}; +#define FDT_ERRTABSIZE (sizeof(fdt_errtable) / sizeof(fdt_errtable[0])) + +const char *fdt_strerror(int errval) +{ + if (errval > 0) + return "<valid offset/length>"; + else if (errval == 0) + return "<no error>"; + else if (errval > -FDT_ERRTABSIZE) { + const char *s = fdt_errtable[-errval].str; + + if (s) + return s; + } + + return "<unknown error>"; +} diff --git a/kexec/libfdt/fdt_sw.c b/kexec/libfdt/fdt_sw.c new file mode 100644 index 0000000..698329e --- /dev/null +++ b/kexec/libfdt/fdt_sw.c @@ -0,0 +1,257 @@ +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * libfdt is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * + * a) This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * Alternatively, + * + * b) Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "libfdt_env.h" + +#include <fdt.h> +#include <libfdt.h> + +#include "libfdt_internal.h" + +static int _fdt_sw_check_header(void *fdt) +{ + if (fdt_magic(fdt) != FDT_SW_MAGIC) + return -FDT_ERR_BADMAGIC; + /* FIXME: should check more details about the header state */ + return 0; +} + +#define FDT_SW_CHECK_HEADER(fdt) \ + { \ + int err; \ + if ((err = _fdt_sw_check_header(fdt)) != 0) \ + return err; \ + } + +static void *_fdt_grab_space(void *fdt, int len) +{ + int offset = fdt_size_dt_struct(fdt); + int spaceleft; + + spaceleft = fdt_totalsize(fdt) - fdt_off_dt_struct(fdt) + - fdt_size_dt_strings(fdt); + + if ((offset + len < offset) || (offset + len > spaceleft)) + return NULL; + + fdt_set_size_dt_struct(fdt, offset + len); + return fdt_offset_ptr_w(fdt, offset, len); +} + +int fdt_create(void *buf, int bufsize) +{ + void *fdt = buf; + + if (bufsize < sizeof(struct fdt_header)) + return -FDT_ERR_NOSPACE; + + memset(buf, 0, bufsize); + + fdt_set_magic(fdt, FDT_SW_MAGIC); + fdt_set_version(fdt, FDT_LAST_SUPPORTED_VERSION); + fdt_set_last_comp_version(fdt, FDT_FIRST_SUPPORTED_VERSION); + fdt_set_totalsize(fdt, bufsize); + + fdt_set_off_mem_rsvmap(fdt, FDT_ALIGN(sizeof(struct fdt_header), + sizeof(struct fdt_reserve_entry))); + fdt_set_off_dt_struct(fdt, fdt_off_mem_rsvmap(fdt)); + fdt_set_off_dt_strings(fdt, bufsize); + + return 0; +} + +int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size) +{ + struct fdt_reserve_entry *re; + int offset; + + FDT_SW_CHECK_HEADER(fdt); + + if (fdt_size_dt_struct(fdt)) + return -FDT_ERR_BADSTATE; + + offset = fdt_off_dt_struct(fdt); + if ((offset + sizeof(*re)) > fdt_totalsize(fdt)) + return -FDT_ERR_NOSPACE; + + re = (struct fdt_reserve_entry *)((char *)fdt + offset); + re->address = cpu_to_fdt64(addr); + re->size = cpu_to_fdt64(size); + + fdt_set_off_dt_struct(fdt, offset + sizeof(*re)); + + return 0; +} + +int fdt_finish_reservemap(void *fdt) +{ + return fdt_add_reservemap_entry(fdt, 0, 0); +} + +int fdt_begin_node(void *fdt, const char *name) +{ + struct fdt_node_header *nh; + int namelen = strlen(name) + 1; + + FDT_SW_CHECK_HEADER(fdt); + + nh = _fdt_grab_space(fdt, sizeof(*nh) + FDT_TAGALIGN(namelen)); + if (! nh) + return -FDT_ERR_NOSPACE; + + nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE); + memcpy(nh->name, name, namelen); + return 0; +} + +int fdt_end_node(void *fdt) +{ + uint32_t *en; + + FDT_SW_CHECK_HEADER(fdt); + + en = _fdt_grab_space(fdt, FDT_TAGSIZE); + if (! en) + return -FDT_ERR_NOSPACE; + + *en = cpu_to_fdt32(FDT_END_NODE); + return 0; +} + +static int _fdt_find_add_string(void *fdt, const char *s) +{ + char *strtab = (char *)fdt + fdt_totalsize(fdt); + const char *p; + int strtabsize = fdt_size_dt_strings(fdt); + int len = strlen(s) + 1; + int struct_top, offset; + + p = _fdt_find_string(strtab - strtabsize, strtabsize, s); + if (p) + return p - strtab; + + /* Add it */ + offset = -strtabsize - len; + struct_top = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt); + if (fdt_totalsize(fdt) + offset < struct_top) + return 0; /* no more room :( */ + + memcpy(strtab + offset, s, len); + fdt_set_size_dt_strings(fdt, strtabsize + len); + return offset; +} + +int fdt_property(void *fdt, const char *name, const void *val, int len) +{ + struct fdt_property *prop; + int nameoff; + + FDT_SW_CHECK_HEADER(fdt); + + nameoff = _fdt_find_add_string(fdt, name); + if (nameoff == 0) + return -FDT_ERR_NOSPACE; + + prop = _fdt_grab_space(fdt, sizeof(*prop) + FDT_TAGALIGN(len)); + if (! prop) + return -FDT_ERR_NOSPACE; + + prop->tag = cpu_to_fdt32(FDT_PROP); + prop->nameoff = cpu_to_fdt32(nameoff); + prop->len = cpu_to_fdt32(len); + memcpy(prop->data, val, len); + return 0; +} + +int fdt_finish(void *fdt) +{ + char *p = (char *)fdt; + uint32_t *end; + int oldstroffset, newstroffset; + uint32_t tag; + int offset, nextoffset; + + FDT_SW_CHECK_HEADER(fdt); + + /* Add terminator */ + end = _fdt_grab_space(fdt, sizeof(*end)); + if (! end) + return -FDT_ERR_NOSPACE; + *end = cpu_to_fdt32(FDT_END); + + /* Relocate the string table */ + oldstroffset = fdt_totalsize(fdt) - fdt_size_dt_strings(fdt); + newstroffset = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt); + memmove(p + newstroffset, p + oldstroffset, fdt_size_dt_strings(fdt)); + fdt_set_off_dt_strings(fdt, newstroffset); + + /* Walk the structure, correcting string offsets */ + offset = 0; + while ((tag = fdt_next_tag(fdt, offset, &nextoffset)) != FDT_END) { + if (tag == FDT_PROP) { + struct fdt_property *prop = + fdt_offset_ptr_w(fdt, offset, sizeof(*prop)); + int nameoff; + + if (! prop) + return -FDT_ERR_BADSTRUCTURE; + + nameoff = fdt32_to_cpu(prop->nameoff); + nameoff += fdt_size_dt_strings(fdt); + prop->nameoff = cpu_to_fdt32(nameoff); + } + offset = nextoffset; + } + + /* Finally, adjust the header */ + fdt_set_totalsize(fdt, newstroffset + fdt_size_dt_strings(fdt)); + fdt_set_magic(fdt, FDT_MAGIC); + return 0; +} diff --git a/kexec/libfdt/fdt_wip.c b/kexec/libfdt/fdt_wip.c new file mode 100644 index 0000000..a4652c6 --- /dev/null +++ b/kexec/libfdt/fdt_wip.c @@ -0,0 +1,145 @@ +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * libfdt is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * + * a) This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * Alternatively, + * + * b) Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "libfdt_env.h" + +#include <fdt.h> +#include <libfdt.h> + +#include "libfdt_internal.h" + +int fdt_setprop_inplace(void *fdt, int nodeoffset, const char *name, + const void *val, int len) +{ + void *propval; + int proplen; + + propval = fdt_getprop_w(fdt, nodeoffset, name, &proplen); + if (! propval) + return proplen; + + if (proplen != len) + return -FDT_ERR_NOSPACE; + + memcpy(propval, val, len); + return 0; +} + +static void _fdt_nop_region(void *start, int len) +{ + uint32_t *p; + + for (p = start; (char *)p < ((char *)start + len); p++) + *p = cpu_to_fdt32(FDT_NOP); +} + +int fdt_nop_property(void *fdt, int nodeoffset, const char *name) +{ + struct fdt_property *prop; + int len; + + prop = fdt_get_property_w(fdt, nodeoffset, name, &len); + if (! prop) + return len; + + _fdt_nop_region(prop, len + sizeof(*prop)); + + return 0; +} + +int _fdt_node_end_offset(void *fdt, int nodeoffset) +{ + int level = 0; + uint32_t tag; + int offset, nextoffset; + + tag = fdt_next_tag(fdt, nodeoffset, &nextoffset); + if (tag != FDT_BEGIN_NODE) + return -FDT_ERR_BADOFFSET; + do { + offset = nextoffset; + tag = fdt_next_tag(fdt, offset, &nextoffset); + + switch (tag) { + case FDT_END: + return offset; + + case FDT_BEGIN_NODE: + level++; + break; + + case FDT_END_NODE: + level--; + break; + + case FDT_PROP: + case FDT_NOP: + break; + + default: + return -FDT_ERR_BADSTRUCTURE; + } + } while (level >= 0); + + return nextoffset; +} + +int fdt_nop_node(void *fdt, int nodeoffset) +{ + int endoffset; + + endoffset = _fdt_node_end_offset(fdt, nodeoffset); + if (endoffset < 0) + return endoffset; + + _fdt_nop_region(fdt_offset_ptr_w(fdt, nodeoffset, 0), + endoffset - nodeoffset); + return 0; +} diff --git a/kexec/libfdt/libfdt.h b/kexec/libfdt/libfdt.h new file mode 100644 index 0000000..87a24ab --- /dev/null +++ b/kexec/libfdt/libfdt.h @@ -0,0 +1,1100 @@ +#ifndef _LIBFDT_H +#define _LIBFDT_H +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * libfdt is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * + * a) This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * Alternatively, + * + * b) Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <libfdt_env.h> +#include <fdt.h> + +#define FDT_FIRST_SUPPORTED_VERSION 0x10 +#define FDT_LAST_SUPPORTED_VERSION 0x11 + +/* Error codes: informative error codes */ +#define FDT_ERR_NOTFOUND 1 + /* FDT_ERR_NOTFOUND: The requested node or property does not exist */ +#define FDT_ERR_EXISTS 2 + /* FDT_ERR_EXISTS: Attemped to create a node or property which + * already exists */ +#define FDT_ERR_NOSPACE 3 + /* FDT_ERR_NOSPACE: Operation needed to expand the device + * tree, but its buffer did not have sufficient space to + * contain the expanded tree. Use fdt_open_into() to move the + * device tree to a buffer with more space. */ + +/* Error codes: codes for bad parameters */ +#define FDT_ERR_BADOFFSET 4 + /* FDT_ERR_BADOFFSET: Function was passed a structure block + * offset which is out-of-bounds, or which points to an + * unsuitable part of the structure for the operation. */ +#define FDT_ERR_BADPATH 5 + /* FDT_ERR_BADPATH: Function was passed a badly formatted path + * (e.g. missing a leading / for a function which requires an + * absolute path) */ +#define FDT_ERR_BADPHANDLE 6 + /* FDT_ERR_BADPHANDLE: Function was passed an invalid phandle + * value. phandle values of 0 and -1 are not permitted. */ +#define FDT_ERR_BADSTATE 7 + /* FDT_ERR_BADSTATE: Function was passed an incomplete device + * tree created by the sequential-write functions, which is + * not sufficiently complete for the requested operation. */ + +/* Error codes: codes for bad device tree blobs */ +#define FDT_ERR_TRUNCATED 8 + /* FDT_ERR_TRUNCATED: Structure block of the given device tree + * ends without an FDT_END tag. */ +#define FDT_ERR_BADMAGIC 9 + /* FDT_ERR_BADMAGIC: Given "device tree" appears not to be a + * device tree at all - it is missing the flattened device + * tree magic number. */ +#define FDT_ERR_BADVERSION 10 + /* FDT_ERR_BADVERSION: Given device tree has a version which + * can't be handled by the requested operation. For + * read-write functions, this may mean that fdt_open_into() is + * required to convert the tree to the expected version. */ +#define FDT_ERR_BADSTRUCTURE 11 + /* FDT_ERR_BADSTRUCTURE: Given device tree has a corrupt + * structure block or other serious error (e.g. misnested + * nodes, or subnodes preceding properties). */ +#define FDT_ERR_BADLAYOUT 12 + /* FDT_ERR_BADLAYOUT: For read-write functions, the given + * device tree has it's sub-blocks in an order that the + * function can't handle (memory reserve map, then structure, + * then strings). Use fdt_open_into() to reorganize the tree + * into a form suitable for the read-write operations. */ + +/* "Can't happen" error indicating a bug in libfdt */ +#define FDT_ERR_INTERNAL 13 + /* FDT_ERR_INTERNAL: libfdt has failed an internal assertion. + * Should never be returned, if it is, it indicates a bug in + * libfdt itself. */ + +#define FDT_ERR_MAX 13 + +/**********************************************************************/ +/* Low-level functions (you probably don't need these) */ +/**********************************************************************/ + +const void *fdt_offset_ptr(const void *fdt, int offset, int checklen); +static inline void *fdt_offset_ptr_w(void *fdt, int offset, int checklen) +{ + return (void *)(uintptr_t)fdt_offset_ptr(fdt, offset, checklen); +} + +uint32_t fdt_next_tag(const void *fdt, int offset, int *nextoffset); + +/**********************************************************************/ +/* Traversal functions */ +/**********************************************************************/ + +int fdt_next_node(const void *fdt, int offset, int *depth); + +/**********************************************************************/ +/* General functions */ +/**********************************************************************/ + +#define fdt_get_header(fdt, field) \ + (fdt32_to_cpu(((const struct fdt_header *)(fdt))->field)) +#define fdt_magic(fdt) (fdt_get_header(fdt, magic)) +#define fdt_totalsize(fdt) (fdt_get_header(fdt, totalsize)) +#define fdt_off_dt_struct(fdt) (fdt_get_header(fdt, off_dt_struct)) +#define fdt_off_dt_strings(fdt) (fdt_get_header(fdt, off_dt_strings)) +#define fdt_off_mem_rsvmap(fdt) (fdt_get_header(fdt, off_mem_rsvmap)) +#define fdt_version(fdt) (fdt_get_header(fdt, version)) +#define fdt_last_comp_version(fdt) (fdt_get_header(fdt, last_comp_version)) +#define fdt_boot_cpuid_phys(fdt) (fdt_get_header(fdt, boot_cpuid_phys)) +#define fdt_size_dt_strings(fdt) (fdt_get_header(fdt, size_dt_strings)) +#define fdt_size_dt_struct(fdt) (fdt_get_header(fdt, size_dt_struct)) + +#define __fdt_set_hdr(name) \ + static inline void fdt_set_##name(void *fdt, uint32_t val) \ + { \ + struct fdt_header *fdth = fdt; \ + fdth->name = cpu_to_fdt32(val); \ + } +__fdt_set_hdr(magic); +__fdt_set_hdr(totalsize); +__fdt_set_hdr(off_dt_struct); +__fdt_set_hdr(off_dt_strings); +__fdt_set_hdr(off_mem_rsvmap); +__fdt_set_hdr(version); +__fdt_set_hdr(last_comp_version); +__fdt_set_hdr(boot_cpuid_phys); +__fdt_set_hdr(size_dt_strings); +__fdt_set_hdr(size_dt_struct); +#undef __fdt_set_hdr + +/** + * fdt_check_header - sanity check a device tree or possible device tree + * @fdt: pointer to data which might be a flattened device tree + * + * fdt_check_header() checks that the given buffer contains what + * appears to be a flattened device tree with sane information in its + * header. + * + * returns: + * 0, if the buffer appears to contain a valid device tree + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, standard meanings, as above + */ +int fdt_check_header(const void *fdt); + +/** + * fdt_move - move a device tree around in memory + * @fdt: pointer to the device tree to move + * @buf: pointer to memory where the device is to be moved + * @bufsize: size of the memory space at buf + * + * fdt_move() relocates, if possible, the device tree blob located at + * fdt to the buffer at buf of size bufsize. The buffer may overlap + * with the existing device tree blob at fdt. Therefore, + * fdt_move(fdt, fdt, fdt_totalsize(fdt)) + * should always succeed. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, bufsize is insufficient to contain the device tree + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, standard meanings + */ +int fdt_move(const void *fdt, void *buf, int bufsize); + +/**********************************************************************/ +/* Read-only functions */ +/**********************************************************************/ + +/** + * fdt_string - retrieve a string from the strings block of a device tree + * @fdt: pointer to the device tree blob + * @stroffset: offset of the string within the strings block (native endian) + * + * fdt_string() retrieves a pointer to a single string from the + * strings block of the device tree blob at fdt. + * + * returns: + * a pointer to the string, on success + * NULL, if stroffset is out of bounds + */ +const char *fdt_string(const void *fdt, int stroffset); + +/** + * fdt_num_mem_rsv - retrieve the number of memory reserve map entries + * @fdt: pointer to the device tree blob + * + * Returns the number of entries in the device tree blob's memory + * reservation map. This does not include the terminating 0,0 entry + * or any other (0,0) entries reserved for expansion. + * + * returns: + * the number of entries + */ +int fdt_num_mem_rsv(const void *fdt); + +/** + * fdt_get_mem_rsv - retrieve one memory reserve map entry + * @fdt: pointer to the device tree blob + * @address, @size: pointers to 64-bit variables + * + * On success, *address and *size will contain the address and size of + * the n-th reserve map entry from the device tree blob, in + * native-endian format. + * + * returns: + * 0, on success + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, standard meanings + */ +int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size); + +/** + * fdt_subnode_offset_namelen - find a subnode based on substring + * @fdt: pointer to the device tree blob + * @parentoffset: structure block offset of a node + * @name: name of the subnode to locate + * @namelen: number of characters of name to consider + * + * Identical to fdt_subnode_offset(), but only examine the first + * namelen characters of name for matching the subnode name. This is + * useful for finding subnodes based on a portion of a larger string, + * such as a full path. + */ +int fdt_subnode_offset_namelen(const void *fdt, int parentoffset, + const char *name, int namelen); +/** + * fdt_subnode_offset - find a subnode of a given node + * @fdt: pointer to the device tree blob + * @parentoffset: structure block offset of a node + * @name: name of the subnode to locate + * + * fdt_subnode_offset() finds a subnode of the node at structure block + * offset parentoffset with the given name. name may include a unit + * address, in which case fdt_subnode_offset() will find the subnode + * with that unit address, or the unit address may be omitted, in + * which case fdt_subnode_offset() will find an arbitrary subnode + * whose name excluding unit address matches the given name. + * + * returns: + * structure block offset of the requested subnode (>=0), on success + * -FDT_ERR_NOTFOUND, if the requested subnode does not exist + * -FDT_ERR_BADOFFSET, if parentoffset did not point to an FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings. + */ +int fdt_subnode_offset(const void *fdt, int parentoffset, const char *name); + +/** + * fdt_path_offset - find a tree node by its full path + * @fdt: pointer to the device tree blob + * @path: full path of the node to locate + * + * fdt_path_offset() finds a node of a given path in the device tree. + * Each path component may omit the unit address portion, but the + * results of this are undefined if any such path component is + * ambiguous (that is if there are multiple nodes at the relevant + * level matching the given component, differentiated only by unit + * address). + * + * returns: + * structure block offset of the node with the requested path (>=0), on success + * -FDT_ERR_BADPATH, given path does not begin with '/' or is invalid + * -FDT_ERR_NOTFOUND, if the requested node does not exist + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings. + */ +int fdt_path_offset(const void *fdt, const char *path); + +/** + * fdt_get_name - retrieve the name of a given node + * @fdt: pointer to the device tree blob + * @nodeoffset: structure block offset of the starting node + * @lenp: pointer to an integer variable (will be overwritten) or NULL + * + * fdt_get_name() retrieves the name (including unit address) of the + * device tree node at structure block offset nodeoffset. If lenp is + * non-NULL, the length of this name is also returned, in the integer + * pointed to by lenp. + * + * returns: + * pointer to the node's name, on success + * If lenp is non-NULL, *lenp contains the length of that name (>=0) + * NULL, on error + * if lenp is non-NULL *lenp contains an error code (<0): + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, standard meanings + */ +const char *fdt_get_name(const void *fdt, int nodeoffset, int *lenp); + +/** + * fdt_get_property - find a given property in a given node + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to find + * @name: name of the property to find + * @lenp: pointer to an integer variable (will be overwritten) or NULL + * + * fdt_get_property() retrieves a pointer to the fdt_property + * structure within the device tree blob corresponding to the property + * named 'name' of the node at offset nodeoffset. If lenp is + * non-NULL, the length of the property value is also returned, in the + * integer pointed to by lenp. + * + * returns: + * pointer to the structure representing the property + * if lenp is non-NULL, *lenp contains the length of the property + * value (>=0) + * NULL, on error + * if lenp is non-NULL, *lenp contains an error code (<0): + * -FDT_ERR_NOTFOUND, node does not have named property + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +const struct fdt_property *fdt_get_property(const void *fdt, int nodeoffset, + const char *name, int *lenp); +static inline struct fdt_property *fdt_get_property_w(void *fdt, int nodeoffset, + const char *name, + int *lenp) +{ + return (struct fdt_property *)(uintptr_t) + fdt_get_property(fdt, nodeoffset, name, lenp); +} + +/** + * fdt_getprop - retrieve the value of a given property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to find + * @name: name of the property to find + * @lenp: pointer to an integer variable (will be overwritten) or NULL + * + * fdt_getprop() retrieves a pointer to the value of the property + * named 'name' of the node at offset nodeoffset (this will be a + * pointer to within the device blob itself, not a copy of the value). + * If lenp is non-NULL, the length of the property value is also + * returned, in the integer pointed to by lenp. + * + * returns: + * pointer to the property's value + * if lenp is non-NULL, *lenp contains the length of the property + * value (>=0) + * NULL, on error + * if lenp is non-NULL, *lenp contains an error code (<0): + * -FDT_ERR_NOTFOUND, node does not have named property + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +const void *fdt_getprop(const void *fdt, int nodeoffset, + const char *name, int *lenp); +static inline void *fdt_getprop_w(void *fdt, int nodeoffset, + const char *name, int *lenp) +{ + return (void *)(uintptr_t)fdt_getprop(fdt, nodeoffset, name, lenp); +} + +/** + * fdt_get_phandle - retrieve the phandle of a given node + * @fdt: pointer to the device tree blob + * @nodeoffset: structure block offset of the node + * + * fdt_get_phandle() retrieves the phandle of the device tree node at + * structure block offset nodeoffset. + * + * returns: + * the phandle of the node at nodeoffset, on success (!= 0, != -1) + * 0, if the node has no phandle, or another error occurs + */ +uint32_t fdt_get_phandle(const void *fdt, int nodeoffset); + +/** + * fdt_get_path - determine the full path of a node + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose path to find + * @buf: character buffer to contain the returned path (will be overwritten) + * @buflen: size of the character buffer at buf + * + * fdt_get_path() computes the full path of the node at offset + * nodeoffset, and records that path in the buffer at buf. + * + * NOTE: This function is expensive, as it must scan the device tree + * structure from the start to nodeoffset. + * + * returns: + * 0, on success + * buf contains the absolute path of the node at + * nodeoffset, as a NUL-terminated string. + * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag + * -FDT_ERR_NOSPACE, the path of the given node is longer than (bufsize-1) + * characters and will not fit in the given buffer. + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen); + +/** + * fdt_supernode_atdepth_offset - find a specific ancestor of a node + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose parent to find + * @supernodedepth: depth of the ancestor to find + * @nodedepth: pointer to an integer variable (will be overwritten) or NULL + * + * fdt_supernode_atdepth_offset() finds an ancestor of the given node + * at a specific depth from the root (where the root itself has depth + * 0, its immediate subnodes depth 1 and so forth). So + * fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, NULL); + * will always return 0, the offset of the root node. If the node at + * nodeoffset has depth D, then: + * fdt_supernode_atdepth_offset(fdt, nodeoffset, D, NULL); + * will return nodeoffset itself. + * + * NOTE: This function is expensive, as it must scan the device tree + * structure from the start to nodeoffset. + * + * returns: + + * structure block offset of the node at node offset's ancestor + * of depth supernodedepth (>=0), on success + * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag +* -FDT_ERR_NOTFOUND, supernodedepth was greater than the depth of nodeoffset + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset, + int supernodedepth, int *nodedepth); + +/** + * fdt_node_depth - find the depth of a given node + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose parent to find + * + * fdt_node_depth() finds the depth of a given node. The root node + * has depth 0, its immediate subnodes depth 1 and so forth. + * + * NOTE: This function is expensive, as it must scan the device tree + * structure from the start to nodeoffset. + * + * returns: + * depth of the node at nodeoffset (>=0), on success + * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_node_depth(const void *fdt, int nodeoffset); + +/** + * fdt_parent_offset - find the parent of a given node + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose parent to find + * + * fdt_parent_offset() locates the parent node of a given node (that + * is, it finds the offset of the node which contains the node at + * nodeoffset as a subnode). + * + * NOTE: This function is expensive, as it must scan the device tree + * structure from the start to nodeoffset, *twice*. + * + * returns: + * structure block offset of the parent of the node at nodeoffset + * (>=0), on success + * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_parent_offset(const void *fdt, int nodeoffset); + +/** + * fdt_node_offset_by_prop_value - find nodes with a given property value + * @fdt: pointer to the device tree blob + * @startoffset: only find nodes after this offset + * @propname: property name to check + * @propval: property value to search for + * @proplen: length of the value in propval + * + * fdt_node_offset_by_prop_value() returns the offset of the first + * node after startoffset, which has a property named propname whose + * value is of length proplen and has value equal to propval; or if + * startoffset is -1, the very first such node in the tree. + * + * To iterate through all nodes matching the criterion, the following + * idiom can be used: + * offset = fdt_node_offset_by_prop_value(fdt, -1, propname, + * propval, proplen); + * while (offset != -FDT_ERR_NOTFOUND) { + * // other code here + * offset = fdt_node_offset_by_prop_value(fdt, offset, propname, + * propval, proplen); + * } + * + * Note the -1 in the first call to the function, if 0 is used here + * instead, the function will never locate the root node, even if it + * matches the criterion. + * + * returns: + * structure block offset of the located node (>= 0, >startoffset), + * on success + * -FDT_ERR_NOTFOUND, no node matching the criterion exists in the + * tree after startoffset + * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_node_offset_by_prop_value(const void *fdt, int startoffset, + const char *propname, + const void *propval, int proplen); + +/** + * fdt_node_offset_by_phandle - find the node with a given phandle + * @fdt: pointer to the device tree blob + * @phandle: phandle value + * + * fdt_node_offset_by_phandle() returns the offset of the node + * which has the given phandle value. If there is more than one node + * in the tree with the given phandle (an invalid tree), results are + * undefined. + * + * returns: + * structure block offset of the located node (>= 0), on success + * -FDT_ERR_NOTFOUND, no node with that phandle exists + * -FDT_ERR_BADPHANDLE, given phandle value was invalid (0 or -1) + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle); + +/** + * fdt_node_check_compatible: check a node's compatible property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of a tree node + * @compatible: string to match against + * + * + * fdt_node_check_compatible() returns 0 if the given node contains a + * 'compatible' property with the given string as one of its elements, + * it returns non-zero otherwise, or on error. + * + * returns: + * 0, if the node has a 'compatible' property listing the given string + * 1, if the node has a 'compatible' property, but it does not list + * the given string + * -FDT_ERR_NOTFOUND, if the given node has no 'compatible' property + * -FDT_ERR_BADOFFSET, if nodeoffset does not refer to a BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_node_check_compatible(const void *fdt, int nodeoffset, + const char *compatible); + +/** + * fdt_node_offset_by_compatible - find nodes with a given 'compatible' value + * @fdt: pointer to the device tree blob + * @startoffset: only find nodes after this offset + * @compatible: 'compatible' string to match against + * + * fdt_node_offset_by_compatible() returns the offset of the first + * node after startoffset, which has a 'compatible' property which + * lists the given compatible string; or if startoffset is -1, the + * very first such node in the tree. + * + * To iterate through all nodes matching the criterion, the following + * idiom can be used: + * offset = fdt_node_offset_by_compatible(fdt, -1, compatible); + * while (offset != -FDT_ERR_NOTFOUND) { + * // other code here + * offset = fdt_node_offset_by_compatible(fdt, offset, compatible); + * } + * + * Note the -1 in the first call to the function, if 0 is used here + * instead, the function will never locate the root node, even if it + * matches the criterion. + * + * returns: + * structure block offset of the located node (>= 0, >startoffset), + * on success + * -FDT_ERR_NOTFOUND, no node matching the criterion exists in the + * tree after startoffset + * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_node_offset_by_compatible(const void *fdt, int startoffset, + const char *compatible); + +/**********************************************************************/ +/* Write-in-place functions */ +/**********************************************************************/ + +/** + * fdt_setprop_inplace - change a property's value, but not its size + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @val: pointer to data to replace the property value with + * @len: length of the property value + * + * fdt_setprop_inplace() replaces the value of a given property with + * the data in val, of length len. This function cannot change the + * size of a property, and so will only work if len is equal to the + * current length of the property. + * + * This function will alter only the bytes in the blob which contain + * the given property value, and will not alter or move any other part + * of the tree. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, if len is not equal to the property's current length + * -FDT_ERR_NOTFOUND, node does not have the named property + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_setprop_inplace(void *fdt, int nodeoffset, const char *name, + const void *val, int len); + +/** + * fdt_setprop_inplace_cell - change the value of a single-cell property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @val: cell (32-bit integer) value to replace the property with + * + * fdt_setprop_inplace_cell() replaces the value of a given property + * with the 32-bit integer cell value in val, converting val to + * big-endian if necessary. This function cannot change the size of a + * property, and so will only work if the property already exists and + * has length 4. + * + * This function will alter only the bytes in the blob which contain + * the given property value, and will not alter or move any other part + * of the tree. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, if the property's length is not equal to 4 + * -FDT_ERR_NOTFOUND, node does not have the named property + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +static inline int fdt_setprop_inplace_cell(void *fdt, int nodeoffset, + const char *name, uint32_t val) +{ + val = cpu_to_fdt32(val); + return fdt_setprop_inplace(fdt, nodeoffset, name, &val, sizeof(val)); +} + +/** + * fdt_nop_property - replace a property with nop tags + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to nop + * @name: name of the property to nop + * + * fdt_nop_property() will replace a given property's representation + * in the blob with FDT_NOP tags, effectively removing it from the + * tree. + * + * This function will alter only the bytes in the blob which contain + * the property, and will not alter or move any other part of the + * tree. + * + * returns: + * 0, on success + * -FDT_ERR_NOTFOUND, node does not have the named property + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_nop_property(void *fdt, int nodeoffset, const char *name); + +/** + * fdt_nop_node - replace a node (subtree) with nop tags + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node to nop + * + * fdt_nop_node() will replace a given node's representation in the + * blob, including all its subnodes, if any, with FDT_NOP tags, + * effectively removing it from the tree. + * + * This function will alter only the bytes in the blob which contain + * the node and its properties and subnodes, and will not alter or + * move any other part of the tree. + * + * returns: + * 0, on success + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_nop_node(void *fdt, int nodeoffset); + +/**********************************************************************/ +/* Sequential write functions */ +/**********************************************************************/ + +int fdt_create(void *buf, int bufsize); +int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size); +int fdt_finish_reservemap(void *fdt); +int fdt_begin_node(void *fdt, const char *name); +int fdt_property(void *fdt, const char *name, const void *val, int len); +static inline int fdt_property_cell(void *fdt, const char *name, uint32_t val) +{ + val = cpu_to_fdt32(val); + return fdt_property(fdt, name, &val, sizeof(val)); +} +#define fdt_property_string(fdt, name, str) \ + fdt_property(fdt, name, str, strlen(str)+1) +int fdt_end_node(void *fdt); +int fdt_finish(void *fdt); + +/**********************************************************************/ +/* Read-write functions */ +/**********************************************************************/ + +int fdt_open_into(const void *fdt, void *buf, int bufsize); +int fdt_pack(void *fdt); + +/** + * fdt_add_mem_rsv - add one memory reserve map entry + * @fdt: pointer to the device tree blob + * @address, @size: 64-bit values (native endian) + * + * Adds a reserve map entry to the given blob reserving a region at + * address address of length size. + * + * This function will insert data into the reserve map and will + * therefore change the indexes of some entries in the table. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new reservation entry + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_add_mem_rsv(void *fdt, uint64_t address, uint64_t size); + +/** + * fdt_del_mem_rsv - remove a memory reserve map entry + * @fdt: pointer to the device tree blob + * @n: entry to remove + * + * fdt_del_mem_rsv() removes the n-th memory reserve map entry from + * the blob. + * + * This function will delete data from the reservation table and will + * therefore change the indexes of some entries in the table. + * + * returns: + * 0, on success + * -FDT_ERR_NOTFOUND, there is no entry of the given index (i.e. there + * are less than n+1 reserve map entries) + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_del_mem_rsv(void *fdt, int n); + +/** + * fdt_set_name - change the name of a given node + * @fdt: pointer to the device tree blob + * @nodeoffset: structure block offset of a node + * @name: name to give the node + * + * fdt_set_name() replaces the name (including unit address, if any) + * of the given node with the given string. NOTE: this function can't + * efficiently check if the new name is unique amongst the given + * node's siblings; results are undefined if this function is invoked + * with a name equal to one of the given node's siblings. + * + * This function may insert or delete data from the blob, and will + * therefore change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob + * to contain the new name + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, standard meanings + */ +int fdt_set_name(void *fdt, int nodeoffset, const char *name); + +/** + * fdt_setprop - create or change a property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @val: pointer to data to set the property value to + * @len: length of the property value + * + * fdt_setprop() sets the value of the named property in the given + * node to the given value and length, creating the property if it + * does not already exist. + * + * This function may insert or delete data from the blob, and will + * therefore change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new property value + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_setprop(void *fdt, int nodeoffset, const char *name, + const void *val, int len); + +/** + * fdt_setprop_cell - set a property to a single cell value + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @val: 32-bit integer value for the property (native endian) + * + * fdt_setprop_cell() sets the value of the named property in the + * given node to the given cell value (converting to big-endian if + * necessary), or creates a new property with that value if it does + * not already exist. + * + * This function may insert or delete data from the blob, and will + * therefore change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new property value + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +static inline int fdt_setprop_cell(void *fdt, int nodeoffset, const char *name, + uint32_t val) +{ + val = cpu_to_fdt32(val); + return fdt_setprop(fdt, nodeoffset, name, &val, sizeof(val)); +} + +/** + * fdt_setprop_string - set a property to a string value + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @str: string value for the property + * + * fdt_setprop_string() sets the value of the named property in the + * given node to the given string value (using the length of the + * string to determine the new length of the property), or creates a + * new property with that value if it does not already exist. + * + * This function may insert or delete data from the blob, and will + * therefore change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new property value + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +#define fdt_setprop_string(fdt, nodeoffset, name, str) \ + fdt_setprop((fdt), (nodeoffset), (name), (str), strlen(str)+1) + +/** + * fdt_delprop - delete a property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to nop + * @name: name of the property to nop + * + * fdt_del_property() will delete the given property. + * + * This function will delete data from the blob, and will therefore + * change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOTFOUND, node does not have the named property + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_delprop(void *fdt, int nodeoffset, const char *name); + +/** + * fdt_add_subnode_namelen - creates a new node based on substring + * @fdt: pointer to the device tree blob + * @parentoffset: structure block offset of a node + * @name: name of the subnode to locate + * @namelen: number of characters of name to consider + * + * Identical to fdt_add_subnode(), but use only the first namelen + * characters of name as the name of the new node. This is useful for + * creating subnodes based on a portion of a larger string, such as a + * full path. + */ +int fdt_add_subnode_namelen(void *fdt, int parentoffset, + const char *name, int namelen); + +/** + * fdt_add_subnode - creates a new node + * @fdt: pointer to the device tree blob + * @parentoffset: structure block offset of a node + * @name: name of the subnode to locate + * + * fdt_add_subnode() creates a new node as a subnode of the node at + * structure block offset parentoffset, with the given name (which + * should include the unit address, if any). + * + * This function will insert data into the blob, and will therefore + * change the offsets of some existing nodes. + + * returns: + * structure block offset of the created nodeequested subnode (>=0), on success + * -FDT_ERR_NOTFOUND, if the requested subnode does not exist + * -FDT_ERR_BADOFFSET, if parentoffset did not point to an FDT_BEGIN_NODE tag + * -FDT_ERR_EXISTS, if the node at parentoffset already has a subnode of + * the given name + * -FDT_ERR_NOSPACE, if there is insufficient free space in the + * blob to contain the new node + * -FDT_ERR_NOSPACE + * -FDT_ERR_BADLAYOUT + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings. + */ +int fdt_add_subnode(void *fdt, int parentoffset, const char *name); + +/** + * fdt_del_node - delete a node (subtree) + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node to nop + * + * fdt_del_node() will remove the given node, including all its + * subnodes if any, from the blob. + * + * This function will delete data from the blob, and will therefore + * change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_del_node(void *fdt, int nodeoffset); + +/**********************************************************************/ +/* Debugging / informational functions */ +/**********************************************************************/ + +const char *fdt_strerror(int errval); + +#define FDT_ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1)) +#define FDT_TAGALIGN(x) (FDT_ALIGN((x), FDT_TAGSIZE)) + +/* + * if add a new subnode: + * see: fdt_add_subnode -> fdt_add_subnode_namelen + */ +static inline int fdt_node_len(const char* node_name) +{ + return sizeof(struct fdt_node_header) + + FDT_TAGALIGN(strlen(node_name) + 1) + FDT_TAGSIZE; +} + +/* + * if add a new prop: (assume prop_name not exist in strtab) + * see: fdt_setprop -> _fdt_add_property + */ +static inline int fdt_prop_len(const char* prop_name, int len) +{ + return (strlen(prop_name) + 1) + + sizeof(struct fdt_property) + + FDT_TAGALIGN(len); +} + +#endif /* _LIBFDT_H */ diff --git a/kexec/libfdt/libfdt_env.h b/kexec/libfdt/libfdt_env.h new file mode 100644 index 0000000..449bf60 --- /dev/null +++ b/kexec/libfdt/libfdt_env.h @@ -0,0 +1,23 @@ +#ifndef _LIBFDT_ENV_H +#define _LIBFDT_ENV_H + +#include <stddef.h> +#include <stdint.h> +#include <string.h> + +#define _B(n) ((unsigned long long)((uint8_t *)&x)[n]) +static inline uint32_t fdt32_to_cpu(uint32_t x) +{ + return (_B(0) << 24) | (_B(1) << 16) | (_B(2) << 8) | _B(3); +} +#define cpu_to_fdt32(x) fdt32_to_cpu(x) + +static inline uint64_t fdt64_to_cpu(uint64_t x) +{ + return (_B(0) << 56) | (_B(1) << 48) | (_B(2) << 40) | (_B(3) << 32) + | (_B(4) << 24) | (_B(5) << 16) | (_B(6) << 8) | _B(7); +} +#define cpu_to_fdt64(x) fdt64_to_cpu(x) +#undef _B + +#endif /* _LIBFDT_ENV_H */ diff --git a/kexec/libfdt/libfdt_internal.h b/kexec/libfdt/libfdt_internal.h new file mode 100644 index 0000000..7e6c4c8 --- /dev/null +++ b/kexec/libfdt/libfdt_internal.h @@ -0,0 +1,92 @@ +#ifndef _LIBFDT_INTERNAL_H +#define _LIBFDT_INTERNAL_H +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * libfdt is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * + * a) This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * Alternatively, + * + * b) Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include <fdt.h> + +#define FDT_CHECK_HEADER(fdt) \ + { \ + int err; \ + if ((err = fdt_check_header(fdt)) != 0) \ + return err; \ + } + +uint32_t _fdt_next_tag(const void *fdt, int startoffset, int *nextoffset); +int _fdt_check_node_offset(const void *fdt, int offset); +const char *_fdt_find_string(const char *strtab, int tabsize, const char *s); +int _fdt_node_end_offset(void *fdt, int nodeoffset); + +static inline const void *_fdt_offset_ptr(const void *fdt, int offset) +{ + return (const char *)fdt + fdt_off_dt_struct(fdt) + offset; +} + +static inline void *_fdt_offset_ptr_w(void *fdt, int offset) +{ + return (void *)(uintptr_t)_fdt_offset_ptr(fdt, offset); +} + +static inline const struct fdt_reserve_entry *_fdt_mem_rsv(const void *fdt, int n) +{ + const struct fdt_reserve_entry *rsv_table = + (const struct fdt_reserve_entry *) + ((const char *)fdt + fdt_off_mem_rsvmap(fdt)); + + return rsv_table + n; +} +static inline struct fdt_reserve_entry *_fdt_mem_rsv_w(void *fdt, int n) +{ + return (void *)(uintptr_t)_fdt_mem_rsv(fdt, n); +} + +#define FDT_SW_MAGIC (~FDT_MAGIC) + +#endif /* _LIBFDT_INTERNAL_H */ diff --git a/kexec/lzma.c b/kexec/lzma.c new file mode 100644 index 0000000..2fc07e6 --- /dev/null +++ b/kexec/lzma.c @@ -0,0 +1,253 @@ +#include <unistd.h> +#include <sys/types.h> + +#include "kexec-lzma.h" +#include "config.h" +#include "kexec.h" + +#ifdef HAVE_LIBLZMA +#define _GNU_SOURCE +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <limits.h> +#include <sys/stat.h> +#include <ctype.h> +#include <lzma.h> + +#define kBufferSize (1 << 15) + +typedef struct lzfile { + uint8_t buf[kBufferSize]; + lzma_stream strm; + FILE *file; + int encoding; + int eof; +} LZFILE; + +LZFILE *lzopen(const char *path, const char *mode); +int lzclose(LZFILE *lzfile); +ssize_t lzread(LZFILE *lzfile, void *buf, size_t len); + +static LZFILE *lzopen_internal(const char *path, const char *mode, int fd) +{ + int level = 5; + int encoding = 0; + FILE *fp; + LZFILE *lzfile; + lzma_ret ret; + lzma_stream lzma_strm_tmp = LZMA_STREAM_INIT; + + for (; *mode; mode++) { + if (*mode == 'w') + encoding = 1; + else if (*mode == 'r') + encoding = 0; + else if (*mode >= '1' && *mode <= '9') + level = *mode - '0'; + } + if (fd != -1) + fp = fdopen(fd, encoding ? "w" : "r"); + else + fp = fopen(path, encoding ? "w" : "r"); + if (!fp) + return NULL; + + lzfile = calloc(1, sizeof(*lzfile)); + + if (!lzfile) { + fclose(fp); + return NULL; + } + + lzfile->file = fp; + lzfile->encoding = encoding; + lzfile->eof = 0; + lzfile->strm = lzma_strm_tmp; + if (encoding) { + lzma_options_lzma opt_lzma; + if (lzma_lzma_preset(&opt_lzma, level - 1)) + return NULL; + ret = lzma_alone_encoder(&lzfile->strm, &opt_lzma); + } else { + ret = lzma_auto_decoder(&lzfile->strm, + UINT64_C(64) * 1024 * 1024, 0); + } + if (ret != LZMA_OK) { + fclose(fp); + free(lzfile); + return NULL; + } + return lzfile; +} + +LZFILE *lzopen(const char *path, const char *mode) +{ + return lzopen_internal(path, mode, -1); +} + +int lzclose(LZFILE *lzfile) +{ + lzma_ret ret; + size_t n; + + if (!lzfile) + return -1; + + if (lzfile->encoding) { + for (;;) { + lzfile->strm.avail_out = kBufferSize; + lzfile->strm.next_out = lzfile->buf; + ret = lzma_code(&lzfile->strm, LZMA_FINISH); + if (ret != LZMA_OK && ret != LZMA_STREAM_END) + return -1; + n = kBufferSize - lzfile->strm.avail_out; + if (n && fwrite(lzfile->buf, 1, n, lzfile->file) != n) + return -1; + if (ret == LZMA_STREAM_END) + break; + } + } + lzma_end(&lzfile->strm); + + return fclose(lzfile->file); + free(lzfile); +} + +ssize_t lzread(LZFILE *lzfile, void *buf, size_t len) +{ + lzma_ret ret; + int eof = 0; + + if (!lzfile || lzfile->encoding) + return -1; + + if (lzfile->eof) + return 0; + + lzfile->strm.next_out = buf; + lzfile->strm.avail_out = len; + + for (;;) { + if (!lzfile->strm.avail_in) { + lzfile->strm.next_in = lzfile->buf; + lzfile->strm.avail_in = fread(lzfile->buf, 1, kBufferSize, lzfile->file); + if (!lzfile->strm.avail_in) + eof = 1; + } + + ret = lzma_code(&lzfile->strm, LZMA_RUN); + if (ret == LZMA_STREAM_END) { + lzfile->eof = 1; + return len - lzfile->strm.avail_out; + } + + if (ret != LZMA_OK) + return -1; + + if (!lzfile->strm.avail_out) + return len; + + if (eof) + return -1; + } +} + +int is_lzma_file(const char *filename) +{ + FILE *fp; + int ret = 0; + uint8_t buf[13]; + + if (!filename) + return 0; + + fp = fopen(filename, "r"); + if (fp == NULL) + return 0; + + const size_t size = fread(buf, 1, sizeof(buf), fp); + + if (size != 13) { + /* file is too small to be a lzma file. */ + fclose(fp); + return 0; + } + + lzma_filter filter = { .id = LZMA_FILTER_LZMA1 }; + + switch (lzma_properties_decode(&filter, NULL, buf, 5)) { + case LZMA_OK: + ret = 1; + break; + default: + /* It's not a lzma file */ + ret = 0; + } + + fclose(fp); + return ret; +} + +char *lzma_decompress_file(const char *filename, off_t *r_size) +{ + LZFILE *fp; + char *buf; + off_t size, allocated; + ssize_t result; + + dbgprintf("Try LZMA decompression.\n"); + + *r_size = 0; + if (!filename) + return NULL; + + if (!is_lzma_file(filename)) + return NULL; + + fp = lzopen(filename, "rb"); + if (fp == 0) { + dbgprintf("Cannot open `%s'\n", filename); + return NULL; + } + size = 0; + allocated = 65536; + buf = xmalloc(allocated); + do { + if (size == allocated) { + allocated <<= 1; + buf = xrealloc(buf, allocated); + } + result = lzread(fp, buf + size, allocated - size); + if (result < 0) { + if ((errno == EINTR) || (errno == EAGAIN)) + continue; + + dbgprintf("%s: read on %s of %ld bytes failed\n", + __func__, filename, (allocated - size) + 0UL); + break; + } + size += result; + } while (result > 0); + + if (lzclose(fp) != LZMA_OK) { + dbgprintf("%s: Close of %s failed\n", __func__, filename); + goto fail; + } + if (result < 0) + goto fail; + + *r_size = size; + return buf; +fail: + free(buf); + return NULL; +} +#else +char *lzma_decompress_file(const char *UNUSED(filename), off_t *UNUSED(r_size)) +{ + return NULL; +} +#endif /* HAVE_LIBLZMA */ diff --git a/kexec/mem_regions.c b/kexec/mem_regions.c new file mode 100644 index 0000000..ad7d3f1 --- /dev/null +++ b/kexec/mem_regions.c @@ -0,0 +1,169 @@ +#include <stdlib.h> + +#include "kexec.h" +#include "mem_regions.h" + +static int mem_range_cmp(const void *a1, const void *a2) +{ + const struct memory_range *r1 = a1; + const struct memory_range *r2 = a2; + + if (r1->start > r2->start) + return 1; + if (r1->start < r2->start) + return -1; + + return 0; +} + +/** + * mem_regions_sort() - sort ranges into ascending address order + * @ranges: ranges to sort + * + * Sort the memory regions into ascending address order. + */ +void mem_regions_sort(struct memory_ranges *ranges) +{ + qsort(ranges->ranges, ranges->size, sizeof(*ranges->ranges), + mem_range_cmp); +} + +/** + * mem_regions_add() - add a memory region to a set of ranges + * @ranges: ranges to add the memory region to + * @base: base address of memory region + * @length: length of memory region in bytes + * @type: type of memory region + * + * Add the memory region to the set of ranges, and return %0 if successful, + * or %-1 if we ran out of space. + */ +int mem_regions_add(struct memory_ranges *ranges, unsigned long long base, + unsigned long long length, int type) +{ + struct memory_range *range; + + if (ranges->size >= ranges->max_size) + return -1; + + range = ranges->ranges + ranges->size++; + range->start = base; + range->end = base + length - 1; + range->type = type; + + return 0; +} + +static void mem_regions_remove(struct memory_ranges *ranges, int index) +{ + int tail_entries; + + /* we are assured to have at least one entry */ + ranges->size -= 1; + + /* if we have following entries, shuffle them down one place */ + tail_entries = ranges->size - index; + if (tail_entries) + memmove(ranges->ranges + index, ranges->ranges + index + 1, + tail_entries * sizeof(*ranges->ranges)); + + /* zero the new tail entry */ + memset(ranges->ranges + ranges->size, 0, sizeof(*ranges->ranges)); +} + +/** + * mem_regions_exclude() - excludes a memory region from a set of memory ranges + * @ranges: memory ranges to exclude the region from + * @range: memory range to exclude + * + * Exclude a memory region from a set of memory ranges. We assume that + * the region to be excluded is either wholely located within one of the + * memory ranges, or not at all. + */ +int mem_regions_exclude(struct memory_ranges *ranges, + const struct memory_range *range) +{ + int i, ret; + + for (i = 0; i < ranges->size; i++) { + struct memory_range *r = ranges->ranges + i; + + /* + * We assume that crash area is fully contained in + * some larger memory area. + */ + if (r->start <= range->start && r->end >= range->end) { + if (r->start == range->start) { + if (r->end == range->end) + /* Remove this entry */ + mem_regions_remove(ranges, i); + else + /* Shrink the start of this memory range */ + r->start = range->end + 1; + } else if (r->end == range->end) { + /* Shrink the end of this memory range */ + r->end = range->start - 1; + } else { + /* + * Split this area into 2 smaller ones and + * remove excluded range from between. First + * create new entry for the remaining area. + */ + ret = mem_regions_add(ranges, range->end + 1, + r->end - range->end, 0); + if (ret < 0) + return ret; + + /* + * Update this area to end before excluded + * range. + */ + r->end = range->start - 1; + break; + } + } + } + return 0; +} + +#define KEXEC_MEMORY_RANGES 16 + +int mem_regions_alloc_and_add(struct memory_ranges *ranges, + unsigned long long base, + unsigned long long length, int type) +{ + void *new_ranges; + + if (ranges->size >= ranges->max_size) { + new_ranges = realloc(ranges->ranges, + sizeof(struct memory_range) * + (ranges->max_size + KEXEC_MEMORY_RANGES)); + if (!new_ranges) + return -1; + + ranges->ranges = new_ranges; + ranges->max_size += KEXEC_MEMORY_RANGES; + } + + return mem_regions_add(ranges, base, length, type); +} + +int mem_regions_alloc_and_exclude(struct memory_ranges *ranges, + const struct memory_range *range) +{ + void *new_ranges; + + /* for safety, we should have at least one free entry in ranges */ + if (ranges->size >= ranges->max_size) { + new_ranges = realloc(ranges->ranges, + sizeof(struct memory_range) * + (ranges->max_size + KEXEC_MEMORY_RANGES)); + if (!new_ranges) + return -1; + + ranges->ranges = new_ranges; + ranges->max_size += KEXEC_MEMORY_RANGES; + } + + return mem_regions_exclude(ranges, range); +} diff --git a/kexec/mem_regions.h b/kexec/mem_regions.h new file mode 100644 index 0000000..e306d67 --- /dev/null +++ b/kexec/mem_regions.h @@ -0,0 +1,22 @@ +#ifndef MEM_REGIONS_H +#define MEM_REGIONS_H + +struct memory_ranges; +struct memory_range; + +void mem_regions_sort(struct memory_ranges *ranges); + +int mem_regions_exclude(struct memory_ranges *ranges, + const struct memory_range *range); + +int mem_regions_add(struct memory_ranges *ranges, unsigned long long base, + unsigned long long length, int type); + +int mem_regions_alloc_and_exclude(struct memory_ranges *ranges, + const struct memory_range *range); + +int mem_regions_alloc_and_add(struct memory_ranges *ranges, + unsigned long long base, + unsigned long long length, int type); + +#endif diff --git a/kexec/phys_arch.c b/kexec/phys_arch.c new file mode 100644 index 0000000..1571a0f --- /dev/null +++ b/kexec/phys_arch.c @@ -0,0 +1,28 @@ +#include "kexec.h" +#include <errno.h> +#include <string.h> +#include <sys/utsname.h> + +long physical_arch(void) +{ + struct utsname utsname; + int i, result = uname(&utsname); + if (result < 0) { + fprintf(stderr, "uname failed: %s\n", + strerror(errno)); + return -1; + } + + for (i = 0; arches[i].machine; ++i) { + if (strcmp(utsname.machine, arches[i].machine) == 0) + return arches[i].arch; + if ((strcmp(arches[i].machine, "arm") == 0) && + (strncmp(utsname.machine, arches[i].machine, + strlen(arches[i].machine)) == 0)) + return arches[i].arch; + } + + fprintf(stderr, "Unsupported machine type: %s\n", + utsname.machine); + return -1; +} diff --git a/kexec/phys_to_virt.c b/kexec/phys_to_virt.c new file mode 100644 index 0000000..5e8c4e3 --- /dev/null +++ b/kexec/phys_to_virt.c @@ -0,0 +1,16 @@ +#include "kexec.h" +#include "crashdump.h" + +/** + * phys_to_virt() - translate physical address to virtual address + * @paddr: physical address to translate + * + * For most architectures physical address is simply virtual address minus + * PAGE_OFFSET. Architectures that don't follow this convention should provide + * their own implementation. + */ +unsigned long +phys_to_virt(struct crash_elf_info *elf_info, unsigned long long paddr) +{ + return paddr + elf_info->page_offset; +} diff --git a/kexec/proc_iomem.c b/kexec/proc_iomem.c new file mode 100644 index 0000000..85daa85 --- /dev/null +++ b/kexec/proc_iomem.c @@ -0,0 +1,13 @@ +#include "kexec.h" + +static const char proc_iomem_str[] = "/proc/iomem"; + +/* + * Allow an architecture specific implementation of this + * function to override the location of a file looking a lot + * like /proc/iomem + */ +const char *proc_iomem(void) +{ + return proc_iomem_str; +} diff --git a/kexec/symbols.c b/kexec/symbols.c new file mode 100644 index 0000000..04377ca --- /dev/null +++ b/kexec/symbols.c @@ -0,0 +1,36 @@ +#include <stdio.h> +#include <string.h> +#include "kexec.h" + +/* Retrieve kernel symbol virtual address from /proc/kallsyms */ +unsigned long long get_kernel_sym(const char *symbol) +{ + const char *kallsyms = "/proc/kallsyms"; + char sym[128]; + char line[128]; + FILE *fp; + unsigned long long vaddr; + char type; + + fp = fopen(kallsyms, "r"); + if (!fp) { + fprintf(stderr, "Cannot open %s\n", kallsyms); + return 0; + } + + while (fgets(line, sizeof(line), fp) != NULL) { + if (sscanf(line, "%llx %c %s", &vaddr, &type, sym) != 3) + continue; + if (strcmp(sym, symbol) == 0) { + dbgprintf("kernel symbol %s vaddr = %16llx\n", + symbol, vaddr); + fclose(fp); + return vaddr; + } + } + + dbgprintf("Cannot get kernel %s symbol address\n", symbol); + + fclose(fp); + return 0; +} diff --git a/kexec/virt_to_phys.c b/kexec/virt_to_phys.c new file mode 100644 index 0000000..a746917 --- /dev/null +++ b/kexec/virt_to_phys.c @@ -0,0 +1,7 @@ +#include "kexec.h" +#include <stdlib.h> + +unsigned long virt_to_phys(unsigned long UNUSED(addr)) +{ + abort(); +} diff --git a/kexec/zlib.c b/kexec/zlib.c new file mode 100644 index 0000000..3ed6bd6 --- /dev/null +++ b/kexec/zlib.c @@ -0,0 +1,129 @@ +#include "kexec-zlib.h" +#include "kexec.h" + +#ifdef HAVE_LIBZ +#define _GNU_SOURCE +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <limits.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <ctype.h> +#include <zlib.h> + +static void _gzerror(gzFile fp, int *errnum, const char **errmsg) +{ + *errmsg = gzerror(fp, errnum); + if (*errnum == Z_ERRNO) { + *errmsg = strerror(*errnum); + } +} + +int is_zlib_file(const char *filename, off_t *r_size) +{ + gzFile fp; + int errnum; + int is_zlib_file = 0; /* default: It's not in gzip format */ + const char *msg; + ssize_t result; + + if (!filename) + goto out; + + fp = gzopen(filename, "rb"); + if (fp == 0) { + _gzerror(fp, &errnum, &msg); + dbgprintf("Cannot open `%s': %s\n", filename, msg); + goto out; + } + + if (!gzdirect(fp)) + /* It's in gzip format */ + is_zlib_file = 1; + + result = gzclose(fp); + if (result != Z_OK) { + _gzerror(fp, &errnum, &msg); + dbgprintf(" Close of %s failed: %s\n", filename, msg); + } + +out: + return is_zlib_file; +} + +char *zlib_decompress_file(const char *filename, off_t *r_size) +{ + gzFile fp; + int errnum; + const char *msg; + char *buf = NULL; + off_t size = 0, allocated; + ssize_t result; + + dbgprintf("Try gzip decompression.\n"); + + *r_size = 0; + if (!filename) { + return NULL; + } + fp = gzopen(filename, "rb"); + if (fp == 0) { + _gzerror(fp, &errnum, &msg); + dbgprintf("Cannot open `%s': %s\n", filename, msg); + return NULL; + } + if (gzdirect(fp)) { + /* It's not in gzip format */ + goto fail; + } + allocated = 65536; + buf = xmalloc(allocated); + do { + if (size == allocated) { + allocated <<= 1; + buf = xrealloc(buf, allocated); + } + result = gzread(fp, buf + size, allocated - size); + if (result < 0) { + if ((errno == EINTR) || (errno == EAGAIN)) + continue; + _gzerror(fp, &errnum, &msg); + dbgprintf("Read on %s of %d bytes failed: %s\n", + filename, (int)(allocated - size), msg); + size = 0; + goto fail; + } + size += result; + } while(result > 0); + +fail: + result = gzclose(fp); + if (result != Z_OK) { + _gzerror(fp, &errnum, &msg); + dbgprintf(" Close of %s failed: %s\n", filename, msg); + } + + if (size > 0) { + *r_size = size; + } else { + free(buf); + buf = NULL; + } + return buf; +} +#else + +int is_zlib_file(const char *filename, off_t *r_size) +{ + return 0; +} + +char *zlib_decompress_file(const char *UNUSED(filename), off_t *UNUSED(r_size)) +{ + return NULL; +} +#endif /* HAVE_ZLIB */ diff --git a/kexec_test/Makefile b/kexec_test/Makefile new file mode 100644 index 0000000..fec6210 --- /dev/null +++ b/kexec_test/Makefile @@ -0,0 +1,40 @@ +# +# kexec_test Debugging payload to be certain the infrastructure works +# +RELOC:=0x10000 +KEXEC_TEST_SRCS:= kexec_test/kexec_test16.S kexec_test/kexec_test.S + +dist += kexec_test/Makefile $(KEXEC_TEST_SRCS) \ + kexec_test/x86-setup-legacy-pic.S + +BUILD_KEXEC_TEST = no +ifeq ($(ARCH),i386) +BUILD_KEXEC_TEST = yes +endif +ifeq ($(ARCH),x86_64) +BUILD_KEXEC_TEST = yes +endif + +ifeq ($(BUILD_KEXEC_TEST),yes) + +KEXEC_TEST_OBJS = $(call objify, $(KEXEC_TEST_SRCS)) +KEXEC_TEST_DEPS = $(call depify, $(KEXEC_TEST_OBJS)) + +KEXEC_TEST = $(PKGLIBDIR)/kexec_test + +clean += $(KEXEC_TEST_OBJS) $(KEXEC_TEST_DEPS) $(KEXEC_TEST) + +-include $(KEXEC_TEST_DEPS) + +$(KEXEC_TEST): CC=$(TARGET_CC) +$(KEXEC_TEST): CPPFLAGS+=-DRELOC=$(RELOC) +$(KEXEC_TEST): ASFLAGS+=-m32 +#$(KEXEC_TEST): LDFLAGS=-m32 -Wl,-e -Wl,_start -Wl,-Ttext -Wl,$(RELOC) \ +# -nostartfiles +$(KEXEC_TEST): LDFLAGS=-melf_i386 -e _start -Ttext $(RELOC) + +$(KEXEC_TEST): $(KEXEC_TEST_OBJS) + mkdir -p $(@D) + $(TARGET_LD) $(LDFLAGS) -o $@ $^ + +endif diff --git a/kexec_test/kexec_test.S b/kexec_test/kexec_test.S new file mode 100644 index 0000000..ad081bc --- /dev/null +++ b/kexec_test/kexec_test.S @@ -0,0 +1,477 @@ +/* + * kexec: Linux boots Linux + * + * Copyright (C) 2003,2004 Eric Biederman (ebiederm@xmission.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation (version 2 of the License). + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "config.h" + + .equ PROT_CODE_SEG, pmcs - gdt + .equ REAL_CODE_SEG, rmcs - gdt + .equ PROT_DATA_SEG, pmds - gdt + .equ REAL_DATA_SEG, rmds - gdt + .equ CR0_PE, 1 + /* Gas thinks the .equs for these are non-absolute so use a define */ +#define PROT_CODE_SEG 0x08 +#define REAL_CODE_SEG 0x18 +#undef i386 + + .text + .arch i386 + .globl _start +_start: + .code32 + # Disable interrupts + cli + + # Save the initial registers + movl %eax, orig_eax + movl %ebx, orig_ebx + movl %ecx, orig_ecx + movl %edx, orig_edx + movl %esi, orig_esi + movl %edi, orig_edi + movl %esp, orig_esp + movl %ebp, orig_ebp + + # Setup a stack + movl $stack_end, %esp + + # Display a message to say everything is working so far + pushl $s_hello + call print_string + addl $4, %esp + + # Save the idt and gdt + sidt orig_idtp + sgdt orig_gdtp + + # Display the initial register contents + call print_orig_regs + + pushl $s_switching_descriptors + call print_string + addl $4, %esp + + # Load descriptor pointers + lgdt gdtp + lidt idtp + # Reload the data segments + movl $PROT_DATA_SEG, %eax + movl %eax, %ds + movl %eax, %es + movl %eax, %ss + movl %eax, %fs + movl %eax, %gs + + # Reload %cs + ljmp $PROT_CODE_SEG, $_start.1 +_start.1: + + pushl $s_descriptors_changed + call print_string + addl $4, %esp + + call setup_legacy_pic + pushl $s_legacy_pic_setup + call print_string + addl $4, %esp + + call prot_to_real + .code16 + + callw test16 + + /* Return to 32bit mode */ + data32 call real_to_prot + .code32 + pushl $s_in_protected_mode + call print_string + addl $4, %esp + + pushl $s_halting + call print_string + addl $4, %esp + jmp halt + + + /* Go from protected to real mode */ +prot_to_real: + .code32 + /* Load the 16bit idt */ + lidt idtp_real + + popl %eax + subl $RELOC, %eax /* Adjust return address */ + pushl %eax + subl $RELOC, %esp /* Adjust stack pointer */ + ljmp $REAL_CODE_SEG, $1f - RELOC +1: + .code16 + /* Reload the segment registers to force a 16bit limit */ + movw $REAL_DATA_SEG, %ax + movw %ax, %ds + movw %ax, %es + movw %ax, %ss + movw %ax, %fs + movw %ax, %gs + + /* Clear the PE bit of CR0 */ + movl %cr0, %eax + andl $0!CR0_PE, %eax + movl %eax, %cr0 + + /* make intersegment jmp to flush the processor pipeline + * and reload %cs:%eip (to clear upper 16 bits of %eip). + */ + data32 ljmp $(RELOC)>>4,$2f- RELOC +2: + /* we are in real mode now + * set up the real mode segment registers + */ + movw %cs,%ax + movw %ax,%ds + movw %ax,%es + movw %ax,%ss + movw %ax,%fs + movw %ax,%gs + data32 ret + +real_to_prot: + .code16 + pushl %ebx + + /* Compute the address of gdtp */ + movw %cs, %ax + shlw $4, %ax + movl $gdtp, %ebx + subw %ax, %bx + + data32 lgdt %cs:(%bx) + movl %cr0, %eax + orl $CR0_PE, %eax + movl %eax, %cr0 + + /* flush prefetch queue and reload %cs:%eip */ + data32 ljmp $PROT_CODE_SEG, $1f +1: + .code32 + /* reload other segment registers */ + movl $PROT_DATA_SEG, %eax + movl %eax, %ds + movl %eax, %es + movl %eax, %ss + movl %eax, %fs + movl %eax, %gs + + popl %ebx /* Restore %ebx */ + + addl $RELOC, %esp /* Fix up stack pointer */ + + popl %eax /* Fix up return address */ + addl $RELOC, %eax + pushl %eax + + lidt idtp /* Load a dummy idt */ + ret + + +halt: + .code32 + hlt + jmp halt + +print_orig_regs: + .code32 + # Display the initial register contents + pushl $s_eax + call print_string + pushl orig_eax + call print_hex + pushl $space + call print_string + addl $12, %esp + + pushl $s_ebx + call print_string + pushl orig_ebx + call print_hex + pushl $space + call print_string + addl $12, %esp + + + pushl $s_ecx + call print_string + pushl orig_ecx + call print_hex + pushl $space + call print_string + addl $12, %esp + + + pushl $s_edx + call print_string + pushl orig_edx + call print_hex + pushl $crlf + call print_string + addl $12, %esp + + + pushl $s_esi + call print_string + pushl orig_esi + call print_hex + pushl $space + call print_string + addl $12, %esp + + pushl $s_edi + call print_string + pushl orig_edi + call print_hex + pushl $space + call print_string + addl $12, %esp + + + pushl $s_esp + call print_string + pushl orig_esp + call print_hex + pushl $space + call print_string + addl $12, %esp + + + pushl $s_ebp + call print_string + pushl orig_ebp + call print_hex + pushl $crlf + call print_string + addl $12, %esp + + # display the interrupt descritor table pointer + pushl $s_idtp + call print_string + movzwl orig_idtp, %eax + pushl %eax + call print_hex + pushl $space + call print_string + pushl orig_idt_base + call print_hex + pushl $crlf + call print_string + addl $20, %esp + + # display the global descritor table pointer + pushl $s_gdtp + call print_string + movzwl orig_gdtp, %eax + pushl %eax + call print_hex + pushl $space + call print_string + pushl orig_gdt_base + call print_hex + pushl $crlf + call print_string + addl $20, %esp + + ret + + +print_string: + .code32 + pushl %ebp + movl %esp, %ebp + pushl %esi + movl 8(%ebp), %esi + xorl %eax, %eax +print_string.1: + lodsb %ds:(%esi), %al + testb $0xff, %al + jz print_string.2 + call print_char + jmp print_string.1 +print_string.2: + popl %esi + popl %ebp + ret + + +print_hex: + .code32 + pushl %ebp + movl %esp, %ebp + movb $32, %cl +print_hex.1: + movl 8(%ebp), %eax + subb $4, %cl + shrl %cl, %eax + andb $0x0f, %al + cmpb $9, %al + ja print_hex.2 + addb $'0', %al + jmp print_hex.3 +print_hex.2: + addb $'A' - 10, %al +print_hex.3: + pushl %ecx + call print_char + popl %ecx + testb %cl, %cl + jnz print_hex.1 + + popl %ebp + ret + +print_char: + .code32 + # The character to print is in al + call serial_print_char + retl + + +#define TTYS0_BASE 0x3f8 +#define TTYS0_RBR (TTYS0_BASE + 0x00) +#define TTYS0_TBR (TTYS0_BASE + 0x00) +#define TTYS0_LSR (TTYS0_BASE + 0x05) +serial_print_char: + .code32 + # The character to print is in al + pushl %eax + + # Wait until the serial port is ready to receive characters +serial_print_char.1: + movl $TTYS0_LSR, %edx + inb %dx, %al + testb $0x20, %al + jz serial_print_char.1 + + # Output the character + movl $TTYS0_TBR, %edx + movb 0(%esp), %al + outb %al, %dx + + # Wait until the serial port has transmitted the character +serial_print_char.2: + movl $TTYS0_LSR, %edx + inb %dx, %al + testb $0x40, %al + jz serial_print_char.2 + + # Restore %eax + popl %eax + # Return to caller + ret + + .code32 + +idtp_real: + .word 0x400 # idt limit = 256 + .word 0, 0 +idtp: + .word 0 # idt limit = 0 + .word 0, 0 # idt base = 0L + +gdt: +gdtp: + .word gdt_end - gdt - 1 # gdt limit + .long gdt # gdt base + .word 0 # dummy + +pmcs: + # the 32 bit protected mode code segment + .word 0xffff,0 + .byte 0,0x9f,0xcf,0 + +pmds: + # the 32 bit protected mode data segment + .word 0xffff,0 + .byte 0,0x93,0xcf,0 + +rmcs: + # the 16 bit real mode code segment + .word 0xffff,(RELOC&0xffff) + .byte (RELOC>>16),0x9b,0x00,(RELOC>>24) + +rmds: + # the 16 bit real mode data segment + .word 0xffff,(RELOC&0xffff) + .byte (RELOC>>16),0x93,0x00,(RELOC>>24) +gdt_end: + + +s_hello: + .ascii "kexec_test " + .ascii PACKAGE_VERSION + .asciz " starting...\r\n" +s_switching_descriptors: + .asciz "Switching descriptors.\r\n" +s_descriptors_changed: + .asciz "Descriptors changed.\r\n" +s_legacy_pic_setup: + .asciz "Legacy pic setup.\r\n" +s_in_protected_mode: + .asciz "In protected mode.\r\n" +s_halting: + .asciz "Halting.\r\n" + + +space: .asciz " " +crlf: .asciz "\r\n" +s_eax: .asciz "eax: " +s_ebx: .asciz "ebx: " +s_ecx: .asciz "ecx: " +s_edx: .asciz "edx: " +s_esi: .asciz "esi: " +s_edi: .asciz "edi: " +s_esp: .asciz "esp: " +s_ebp: .asciz "ebp: " + + +s_idtp: .asciz "idt: " +s_gdtp: .asciz "gdt: " + +#include "x86-setup-legacy-pic.S" + + .bss + .balign 4096 +stack: + .skip 4096 +stack_end: + + .bss + .balign 4 +orig_eax: .long 0 +orig_ebx: .long 0 +orig_ecx: .long 0 +orig_edx: .long 0 +orig_esi: .long 0 +orig_edi: .long 0 +orig_esp: .long 0 +orig_ebp: .long 0 + + .balign 4 +orig_idtp: .short 0 +orig_idt_base: .long 0 +orig_gdtp: .short 0 +orig_gdt_base: .long 0 + diff --git a/kexec_test/kexec_test16.S b/kexec_test/kexec_test16.S new file mode 100644 index 0000000..4d37915 --- /dev/null +++ b/kexec_test/kexec_test16.S @@ -0,0 +1,1004 @@ +/* + * kexec: Linux boots Linux + * + * Copyright (C) 2003,2004 Eric Biederman (ebiederm@xmission.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation (version 2 of the License). + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + .text + .code16 + + .globl test16 + .balign 16 + .globl _start16 +_start16: +test16: + pushw $s_in_real_mode - _start16 + call print_string16 + addw $2, %sp + +#if 0 + /* Disable interrupts */ + movb $0xff, %al + outb %al, $0x21 + outb %al, $0xa1 +#endif + /* Enable interrupts, BIOS calls may fail if we don't */ + sti + pushw $s_interrupts_enabled - _start16 + call print_string16 + addw $2, %sp + + /* Get the base memory size, via a bios call */ + /* This is to test BIOS calls more than to achieve anything practical */ + xorw %ax, %ax + int $0x12 + pushw %ax + pushw $s_base_memory_size - _start16 + call print_string16 + addw $2, %sp + call print_hex16 + addw $2, %sp + pushw $s_crlf - _start16 + call print_string16 + addw $2, %sp + + + /* Some things do not like a20 being enabled so disable it */ + call disable_a20 + + /* Here we test various BIOS calls to determine how much of the system is working */ + call get_meme820 + call print_meme820 + call print_meme801 + call print_mem88 + call disable_apm + call print_equipment_list + call print_sysdesc + call print_video + call print_cursor + call print_video_mode + call set_auto_repeat_rate + call print_dasd_type + call print_edd + + /* Enable a20 */ + call enable_a20 + pushw $s_a20_enabled - _start16 + call print_string16 + addw $2, %sp + + /* Disable interrupts */ + cli + pushw $s_interrupts_disabled - _start16 + call print_string16 + addw $2, %sp + + retw + +# +# Enable A20. This is at the very best an annoying procedure. +# A20 code ported from SYSLINUX 1.52-1.63 by H. Peter Anvin. +# + +A20_TEST_LOOPS = 32 # Iterations per wait +A20_ENABLE_LOOPS = 255 # Total loops to try +A20_DISABLE_LOOPS = 255 # Total loops to try + +enable_a20: + .code16 + movb $A20_ENABLE_LOOPS, a20_tries - _start16 +a20_try_loop: + + # First, see if we are on a system with no A20 gate. +a20_none: + call a20_test + jnz a20_done + + # Next, try the BIOS (INT 0x15, AX=0x2401) +a20_bios: + movw $0x2401, %ax + pushfl # Be paranoid about flags + int $0x15 + popfl + + call a20_test + jnz a20_done + + # Try enabling A20 through the keyboard controller +a20_kbc: + call empty_8042 + + call a20_test # Just in case the BIOS worked + jnz a20_done # but had a delayed reaction. + + movb $0xD1, %al # command write + outb %al, $0x64 + call empty_8042 + + movb $0xDF, %al # A20 on + outb %al, $0x60 + call empty_8042 + + # Wait until a20 really *is* enabled; it can take a fair amount of + # time on certain systems; Toshiba Tecras are known to have this + # problem. +a20_kbc_wait: + xorw %cx, %cx +a20_kbc_wait_loop: + call a20_test + jnz a20_done + loop a20_kbc_wait_loop + + # Final attempt: use "configuration port A" +a20_fast: + inb $0x92, %al # Configuration Port A + orb $0x02, %al # "fast A20" version + andb $0xFE, %al # dont accidentally reset + outb %al, $0x92 + + # Wait for configuration port A to take effect +a20_fast_wait: + xorw %cx, %cx +a20_fast_wait_loop: + call a20_test + jnz a20_done + loop a20_fast_wait_loop + + # A20 is still not responding. Try frobbing it again. + # + decb (a20_tries - _start16) + jnz a20_try_loop + jmp a20_die + +a20_die: + pushw $s_a20_err_msg - _start16 + call print_string16 + jmp halt16 + + # If we get here, all is good +a20_done: + ret + + + +# This routine tests whether or not A20 is enabled. If so, it +# exits with zf = 0. +# +# The memory address used, 0x200, is the int $0x80 vector, which +# should be safe. + +A20_TEST_ADDR = 4*0x80 + +a20_test: + .code16 + pushw %cx + pushw %ax + xorw %cx, %cx + movw %cx, %fs # Low memory + decw %cx + movw %cx, %gs # High memory area + movw $A20_TEST_LOOPS, %cx + movw %fs:(A20_TEST_ADDR), %ax + pushw %ax +a20_test_wait: + incw %ax + movw %ax, %fs:(A20_TEST_ADDR) + call delay # Serialize and make delay constant + cmpw %gs:(A20_TEST_ADDR+0x10), %ax + loope a20_test_wait + + popw %fs:(A20_TEST_ADDR) + popw %ax + popw %cx + + ret + +# +# Disable A20 +# + +disable_a20: + .code16 + movb $A20_DISABLE_LOOPS, a20_disable_tries - _start16 +a20_disable_loop: + + # First see if gate A20 is already disabled + call a20_test + jz a20_disabled + + + # Next, try the BIOS (INT 0x15, AX= 0x2400) + movw $0x2400, %ax + pushfl # Be paranoid about flags + int $0x15 + popfl + + call a20_test + jz a20_disabled + + # Try disabling A20 through the keyboard controller + call empty_8042 + + call a20_test # Just in case the BIOS worked + jz a20_disabled # but had a delayed reaction. + + movb $0xD1, %al # command write + outb %al, $0x64 + call empty_8042 + + movb $0xDD, %al # A20 off + outb %al, $0x60 + call empty_8042 + + # Wait until a20 really *is* disabled + xorw %cx, %cx +a20_kbc_disable_loop: + call a20_test + jz a20_disabled + loop a20_kbc_disable_loop + + # Final attempt: use "configuration port A" + inb $0x92, %al # Configuratin Port A + andb $0xFD, %al # "fast A20" version + andb $0xFE, %al # dont accidentally reset + outb %al, $0x92 + + # Wait for configuration port A to take affect + xorw %cx, %cx +a20_fast_disable_loop: + call a20_test + jz a20_disabled + loop a20_fast_disable_loop + + # A20 is still not responding. Try it again + decb (a20_disable_tries - _start16) + jnz a20_disable_loop + + pushw $s_a20_cant_disable - _start16 + call print_string16 + addw $2, %sp + retw + + # If we get here, all is good +a20_disabled: + pushw $s_a20_disabled - _start16 + call print_string16 + addw $2, %sp + retw + + +# This routine checks that the keyboard command queue is empty +# (after emptying the output buffers) +# +# Some machines have delusions that the keyboard buffer is always full +# with no keyboard attached... +# +# If there is no keyboard controller, we will usually get 0xff +# to all the reads. With each IO taking a microsecond and +# a timeout of 100,000 iterations, this can take about half a +# second ("delay" == outb to port 0x80). That should be ok, +# and should also be plenty of time for a real keyboard controller +# to empty. +# + +empty_8042: + .code16 + pushl %ecx + movl $100000, %ecx + +empty_8042_loop: + decl %ecx + jz empty_8042_end_loop + + call delay + + inb $0x64, %al # 8042 status port + testb $1, %al # output buffer? + jz no_output + + call delay + inb $0x60, %al # read it + jmp empty_8042_loop + +no_output: + testb $2, %al # is input buffer full? + jnz empty_8042_loop # yes - loop +empty_8042_end_loop: + popl %ecx + ret + + + + +# method E820H: +# the memory map from hell. e820h returns memory classified into +# a whole bunch of different types, and allows memory holes and +# everything. We scan through this memory map and build a list +# of the first 32 memory areas, which we return at [E820MAP]. +# This is documented at http://www.teleport.com/~acpi/acpihtml/topic245.htm + +#define SMAP 0x534d4150 +#define E820_MAX 32 +#define E820_SIZE 20 + +get_meme820: + .code16 + pushw %bp + movw %sp, %bp + pushw %ds + pushw %es + pushl %esi + pushl %edi + pushl %ebx + + xorl %eax, %eax + movb %al, e820nr - _start16 + xorl %ebx, %ebx # continuation counter + movw $e820_map - _start16, %di # point into the whitelist + # so we can have the bios + # directly write into it. + +jmpe820: + movl $0x0000e820, %eax # e820, upper word zeroed + movl $SMAP, %edx # ascii SMAP + movl $E820_SIZE, %ecx # size of the e820rec + pushw %ds # data record. + popw %es + int $0x15 # make the call + jc bail820 # fall to e801 if it fails + + cmpl $SMAP, %eax # check the return is SMAP + jne bail820 # fall to e801 if it fails + +# cmpl $1, 16(%di) # is this usable memory? +# jne again820 + + # If this is usable memory, we save it by simply advancing %di by + # sizeof(e820rec). + # +good820: + movb e820nr - _start16, %al # up to 32 entries + cmpb $E820_MAX, %al + jnl bail820 + + incb e820nr - _start16 + movw %di, %ax + addw $20, %ax + movw %ax, %di +again820: + cmpl $0, %ebx # check to see if + jne jmpe820 # %ebx is set to EOF +bail820: + popl %ebx + popl %edi + popl %esi + popw %es + popw %ds + popw %bp + retw + + +print_meme820: + .code16 + pushw %si + xorw %cx, %cx + movb (e820nr - _start16), %cl + movw $e820_map - _start16, %si + + pushw $s_meme820 - _start16 + call print_string16 + addw $2, %sp + +print_meme820.1: + pushw %cx + + pushw 8(%si) + pushw 10(%si) + pushw 12(%si) + pushw 14(%si) + call print_hex16 + addw $2, %sp + call print_hex16 + addw $2, %sp + call print_hex16 + addw $2, %sp + call print_hex16 + addw $2, %sp + + pushw $s_at - _start16 + call print_string16 + addw $2, %sp + + pushw 0(%si) + pushw 2(%si) + pushw 4(%si) + pushw 6(%si) + call print_hex16 + addw $2, %sp + call print_hex16 + addw $2, %sp + call print_hex16 + addw $2, %sp + call print_hex16 + addw $2, %sp + + pushw $s_type - _start16 + call print_string16 + addw $2, %sp + + pushw 16(%si) + pushw 18(%si) + call print_hex16 + addw $2, %sp + call print_hex16 + addw $2, %sp + + pushw $s_crlf - _start16 + call print_string16 + addw $2, %sp + + popw %cx + addw $E820_SIZE, %si + subw $1, %cx + jnz print_meme820.1 + + popw %si + retw + + + +print_meme801: + .code16 + pushw %bp + movw %sp, %bp + pushw %bx + pushl $0 + +# method E801H: +# memory size is in 1k chunksizes + + stc # fix to work around buggy + xorw %cx,%cx # BIOSes which dont clear/set + xorw %dx,%dx # carry on pass/error of + # e801h memory size call + # or merely pass cx,dx though + # without changing them. + movw $0xe801, %ax + int $0x15 + jc print_meme801.2 + + cmpw $0x0, %cx # Kludge to handle BIOSes + jne e801usecxdx # which report their extended + cmpw $0x0, %dx # memory in AX/BX rather than + jne e801usecxdx # CX/DX. The spec I have read + movw %ax, %cx # seems to indicate AX/BX + movw %bx, %dx # are more reasonable anyway... + +e801usecxdx: + andl $0xffff, %edx # clear sign extend + shll $6, %edx # and go from 64k to 1k chunks + movl %edx, -6(%bp) # store extended memory size + andl $0xffff, %ecx # clear sign extend + addl %ecx, -6(%bp) # and add lower memory into + + pushw $s_meme801 - _start16 + call print_string16 + addw $2, %sp + + pushw -6(%bp) + pushw -4(%bp) + call print_hex16 + addw $2, %sp + call print_hex16 + addw $2, %sp + + pushw $s_crlf - _start16 + call print_string16 + addw $2, %sp + +print_meme801.2: + addw $4, %sp + popw %bx + popw %bp + retw + +print_mem88: + .code16 +# Ye Olde Traditional Methode. Returns the memory size (up to 16mb or +# 64mb, depending on the bios) in ax. + movb $0x88, %ah + int $0x15 + + pushw %ax + pushw $s_mem88 - _start16 + call print_string16 + addw $2, %sp + call print_hex16 + addw $2, %sp + pushw $s_crlf - _start16 + call print_string16 + addw $2, %sp + + retw + +print_dasd_type: + .code16 + pushw $s_dasd_type - _start16 + call print_string16 + addw $2, %sp + + movw $0x1500, %ax + movb $0x81, %dl + int $0x13 + jc print_dasd_type.1 + + pushw %dx + pushw %cx + pushw $s_space - _start16 + pushw %ax + + call print_hex16 + addw $2, %sp + call print_string16 + addw $2, %sp + call print_hex16 + addw $2, %sp + call print_hex16 + addw $2, %sp + jmp print_dasd_type.2 +print_dasd_type.1: + pushw $s_none - _start16 + call print_string16 + addw $2, %sp + +print_dasd_type.2: + pushw $s_crlf - _start16 + call print_string16 + addw $2, %sp + + retw + +print_equipment_list: + .code16 + pushw $s_equipment_list - _start16 + call print_string16 + addw $2, %sp + + int $0x11 + pushw %ax + call print_hex16 + addw $2, %sp + + pushw $s_crlf - _start16 + call print_string16 + addw $2, %sp + + retw + +print_sysdesc: + .code16 + pushw $s_sysdesc - _start16 + call print_string16 + addw $2, %sp + + pushw %es + movb $0xc0, %ah + stc + int $0x15 + movw %es, %ax + popw %es + jc print_sysdesc.1 + + pushw %bx + pushw $s_colon - _start16 + pushw %ax + call print_hex16 + addw $2, %sp + call print_string16 + addw $2, %sp + call print_hex16 + addw $2, %sp + jmp print_sysdesc.2 + +print_sysdesc.1: + pushw $s_none - _start16 + call print_string16 + addw $2, %sp + +print_sysdesc.2: + pushw $s_crlf - _start16 + call print_string16 + addw $2, %sp + + retw + +print_edd: + .code16 + pushw $s_edd - _start16 + call print_string16 + add $2, %sp + + movb $0x80, %dl + movb $0x41, %ah # Function 41 + movw $0x55aa, %bx # magic + int $0x13 # make the call + jc print_edd.1 # no more BIOS devices + + cmpw $0xAA55, %bx # is magic right? + jne print_edd.1 # nope + + pushw $s_ok - _start16 + call print_string16 + add $2, %sp + jmp print_edd.2 + +print_edd.1: + pushw $s_none - _start16 + call print_string16 + add $2, %sp + +print_edd.2: + pushw $s_crlf - _start16 + call print_string16 + addw $2, %sp + + retw + +set_auto_repeat_rate: + .code16 + pushw $s_auto_repeat_rate - _start16 + call print_string16 + add $2, %sp + +# Set the keyboard repeat rate to the max + movw $0x0305, %ax + xorw %bx, %bx + int $0x16 + + pushw $s_done - _start16 + call print_string16 + add $2, %sp + + retw + +print_video: + .code16 + pushw $s_video_type - _start16 + call print_string16 + add $2, %sp + + movb $0x12, %ah # Check EGA/VGA + movb $0x10, %bl + int $0x10 + movw $s_video_pre_ega - _start16, %cx + cmpb $0x10, %bl + je print_video.1 + + movw $0x1a00, %ax # Check EGA or VGA? + int $0x10 + movw $s_video_vga - _start16, %cx + cmpb $0x1a, %al # 1a means VGA... + je print_video.1 # anything else is EGA. + + movw $s_video_ega - _start16, %cx + +print_video.1: + pushw %cx + call print_string16 + addw $2, %sp + + pushw $s_crlf - _start16 + call print_string16 + addw $2, %sp + + retw + +print_cursor: + .code16 + pushw $s_cursor - _start16 + call print_string16 + add $2, %sp + + movb $0x03, %ah # Read cursor position + xorb %bh, %bh + int $0x10 + + xorw %ax, %ax + movb %dl, %al + pushw %ax + pushw $s_space - _start16 + movb %dh, %al + pushw %ax + + call print_hex16 + add $2, %sp + call print_string16 + add $2, %sp + call print_hex16 + add $2, %sp + + pushw $s_crlf - _start16 + call print_string16 + add $2, %sp + + retw + +print_video_mode: + .code16 + pushw $s_video_mode - _start16 + call print_string16 + add $2, %sp + + movb $0x0f, %ah # Read cursor position + int $0x10 + + xorb %ah, %ah + pushw %ax + call print_hex16 + add $2, %sp + + pushw $s_crlf - _start16 + call print_string16 + add $2, %sp + + retw + + +disable_apm: + push %bp + movw %sp, %bp + pushw %bx + + pushw $s_testing_for_apm - _start16 + call print_string16 + add $2, %sp + + # check for APM BIOS + movw $0x5300, %ax # APM BIOS installation check + xorw %bx, %bx + int $0x15 + jc done_apm_bios # error -> no APM BIOS + + cmpw $0x504d, %bx # check for "PM" signature + jne done_apm_bios # no signature -> no APM BIOS + + pushw $s_apm_found_disconnecting - _start16 + call print_string16 + add $2, %sp + + movw $0x5304, %ax # Disconnect first just in case + xorw %bx, %bx + int $0x15 # ignore return code + + pushw $s_apm_connecting - _start16 + call print_string16 + add $2, %sp + + movw $0x5301, %ax # Real Mode connect + xorw %bx, %bx + int $0x15 + jc done_apm_bios # error + + pushw $s_apm_disabling - _start16 + call print_string16 + add $2, %sp + + movw $0x5308, %ax # Disable APM + mov $0xffff, %bx + xorw %cx, %cx + int $0x15 + + pushw $s_apm_disconnecting - _start16 + call print_string16 + add $2, %sp + + movw $0x5304, %ax # Do a final disconnect + xorw %bx, %bx + int $0x15 + +done_apm_bios: + pushw $s_apm_test_done - _start16 + call print_string16 + add $2, %sp + + popw %bx + popw %bp + retw + + +# Delay is needed after doing I/O +delay: + .code16 + outb %al,$0x80 + retw + +halt16: + .code16 + hlt + jmp halt16 + + +print_string16: + .code16 + pushw %bp + movw %sp, %bp + pushw %si + movw 4(%bp), %si + xorw %ax, %ax +print_string16.1: + lodsb %ds:(%si), %al + testb $0xff, %al + jz print_string16.2 + call print_char16 + jmp print_string16.1 +print_string16.2: + popw %si + popw %bp + ret + +print_hex16: + .code16 + pushw %bp + movw %sp, %bp + movw $16, %cx +print_hex16.1: + movw 4(%bp), %ax + subb $4, %cl + shrw %cl, %ax + andb $0x0f, %al + cmpb $9, %al + ja print_hex16.2 + addb $'0', %al + jmp print_hex16.3 +print_hex16.2: + addb $'A' - 10, %al +print_hex16.3: + pushw %cx + call print_char16 + popw %cx + testb %cl, %cl + jnz print_hex16.1 + + popw %bp + ret + +print_char16: + .code16 + # The character to print is in al + call serial_print_char16 + retw + + +#define TTYS0_BASE 0x3f8 +#define TTYS0_RBR (TTYS0_BASE + 0x00) +#define TTYS0_TBR (TTYS0_BASE + 0x00) +#define TTYS0_LSR (TTYS0_BASE + 0x05) + +serial_print_char16: + .code16 + pushw %bp + movw %sp, %bp + # The character to print is in al + pushw %ax + + # Wait until the serial port is ready to receive characters +serial_print_char16.1: + movw $TTYS0_LSR, %dx + inb %dx, %al + testb $0x20, %al + jz serial_print_char16.1 + + # Output the character + movw $TTYS0_TBR, %dx + movb -2(%bp), %al + outb %al, %dx + + # Wait until the serial port has transmitted the character +serial_print_char16.2: + movw $TTYS0_LSR, %dx + inb %dx, %al + testb $0x40, %al + jz serial_print_char16.2 + + # Restore %eax + popw %ax + # Return to caller + popw %bp + retw + + +s_a20_err_msg: + .asciz "A20 gate not responding!\r\n" + +s_in_real_mode: + .asciz "In real mode.\r\n" +s_base_memory_size: + .asciz "Base memory size: " +s_interrupts_enabled: + .asciz "Interrupts enabled.\r\n" +s_a20_disabled: + .asciz "A20 disabled.\r\n" +s_a20_cant_disable: + .asciz "Can not A20 line.\r\n" +s_a20_enabled: + .asciz "A20 enabled\r\n" +s_interrupts_disabled: + .asciz "Interrupts disabled.\r\n" + +s_meme820: .asciz "E820 Memory Map.\r\n" +s_at: .asciz " @ " +s_type: .asciz " type: " +s_space: .asciz " " +s_colon: .asciz ":" +s_none: .asciz " none " +s_ok: .asciz " ok " +s_done: .asciz " done\r\n" + +s_meme801: + .asciz "E801 Memory size: " +s_mem88: + .asciz "Mem88 Memory size: " + +s_dasd_type: + .asciz "DASD type: " +s_equipment_list: + .asciz "Equiptment list: " +s_sysdesc: + .asciz "Sysdesc: " +s_edd: + .asciz "EDD: " +s_auto_repeat_rate: + .asciz "Setting auto repeat rate " + + +s_video_type: + .asciz "Video type: " +s_video_pre_ega: + .asciz "CGA/MDA/HGA" +s_video_ega: + .asciz "EGA" +s_video_vga: + .asciz "VGA" + +s_cursor: + .asciz "Cursor Position(Row,Column): " + +s_video_mode: + .asciz "Video Mode: " + +s_testing_for_apm: + .asciz "Testing for APM.\r\n" +s_apm_found_disconnecting: + .asciz "APM Found disconnecting.\r\n" +s_apm_connecting: + .asciz "APM connecting.\r\n" +s_apm_disabling: + .asciz "APM disabling.\r\n" +s_apm_disconnecting: + .asciz "APM disconnecting.\r\n" +s_apm_test_done: + .asciz "APM test done.\r\n" + +s_crlf: .asciz "\r\n" + + + +a20_tries: .byte A20_ENABLE_LOOPS +a20_disable_tries: .byte A20_DISABLE_LOOPS + + +e820nr: .byte 0 +e820_map: .fill E820_MAX * E820_SIZE, 1, 0 diff --git a/kexec_test/x86-setup-legacy-pic.S b/kexec_test/x86-setup-legacy-pic.S new file mode 100644 index 0000000..32ef42f --- /dev/null +++ b/kexec_test/x86-setup-legacy-pic.S @@ -0,0 +1,53 @@ +/* + * kexec: Linux boots Linux + * + * Copyright (C) 2003,2004 Eric Biederman (ebiederm@xmission.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation (version 2 of the License). + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + .text + .code32 +setup_legacy_pic: + /* Load the legacy dos settings into the 8259A pic */ + movb $0xff, %al + outb %al, $0x21 /* mask all of 8259A-1 */ + outb %al, $0xa1 /* mask all of 8259A-1 */ + + movb $0x11, %al + outb %al, $0x20 /* ICW1: select 8259A-1 init */ + outb %al, $0x80 /* A short delay */ + movb $0x08, %al + outb %al, $0x21 /* ICW2: 8259A-1 IR0-7 mappend to 0x8-0xf */ + outb %al, $0x80 /* A short delay */ + movb $01, %al + outb %al, $0x21 /* Normal 8086 auto EOI mode */ + outb %al, $0x80 /* A short delay */ + + + movb $0x11, %al + outb %al, $0xA0 /* ICW1: select 8259A-2 init */ + outb %al, $0x80 /* A short delay */ + movb $0x70, %al + outb %al, $0xA1 /* ICW2: 8259A-2 IR0-7 mappend to 0x70-0x77 */ + outb %al, $0x80 /* A short delay */ + movb $01, %al + outb %al, $0xA1 /* Normal 8086 auto EOI mode */ + outb %al, $0x80 /* A short delay */ + + movb $0, %al + outb %al, $0x21 /* Unmask all of 8259A-1 */ + outb %al, $0xa1 /* Unmask all of 8259A-2 */ + + ret diff --git a/purgatory/Makefile b/purgatory/Makefile new file mode 100644 index 0000000..4d2d071 --- /dev/null +++ b/purgatory/Makefile @@ -0,0 +1,76 @@ +# +# Purgatory (an uncomfortable intermediate state) +# In this case the code that runs between kernels +# + +# There is probably a cleaner way to do this but for now this +# should keep us from accidentially include unsafe library functions +# or headers. + +PURGATORY = purgatory/purgatory.ro +PURGATORY_SRCS = +PURGATORY_SRCS += purgatory/purgatory.c +PURGATORY_SRCS += purgatory/printf.c +PURGATORY_SRCS += purgatory/string.c +PURGATORY_MAP = purgatory/purgatory.map + +dist += purgatory/Makefile $(PURGATORY_SRCS) \ + purgatory/include/purgatory.h purgatory/include/string.h + +include $(srcdir)/purgatory/arch/alpha/Makefile +include $(srcdir)/purgatory/arch/arm/Makefile +include $(srcdir)/purgatory/arch/arm64/Makefile +include $(srcdir)/purgatory/arch/i386/Makefile +include $(srcdir)/purgatory/arch/ia64/Makefile +include $(srcdir)/purgatory/arch/mips/Makefile +include $(srcdir)/purgatory/arch/ppc/Makefile +include $(srcdir)/purgatory/arch/ppc64/Makefile +include $(srcdir)/purgatory/arch/s390/Makefile +include $(srcdir)/purgatory/arch/sh/Makefile +include $(srcdir)/purgatory/arch/x86_64/Makefile +include $(srcdir)/purgatory/arch/loongarch/Makefile + +PURGATORY_SRCS+=$($(ARCH)_PURGATORY_SRCS) + +PURGATORY_OBJS = $(call objify, $(PURGATORY_SRCS)) purgatory/sha256.o +PURGATORY_DEPS = $(call depify, $(PURGATORY_OBJS)) + +clean += $(PURGATORY_OBJS) $(PURGATORY_DEPS) $(PURGATORY) $(PURGATORY_MAP) $(PURGATORY).sym + +-include $(PURGATORY_DEPS) + +purgatory/sha256.o: CFLAGS += -O2 $($(ARCH)_PURGATORY_SHA256_CFLAGS) + +purgatory/sha256.o: $(srcdir)/util_lib/sha256.c + mkdir -p $(@D) + $(COMPILE.c) -o $@ $^ + +$(PURGATORY): CC=$(TARGET_CC) +$(PURGATORY): CFLAGS=$(PURGATORY_EXTRA_CFLAGS) \ + $($(ARCH)_PURGATORY_EXTRA_CFLAGS) \ + -Os -fno-builtin -ffreestanding \ + -fno-zero-initialized-in-bss \ + -fno-PIC -fno-PIE -fno-stack-protector -fno-tree-vectorize + +$(PURGATORY): CPPFLAGS=$($(ARCH)_PURGATORY_EXTRA_CFLAGS) \ + -I$(srcdir)/purgatory/include \ + -I$(srcdir)/purgatory/arch/$(ARCH)/include \ + -I$(srcdir)/util_lib/include \ + -I$(srcdir)/include \ + -Iinclude \ + -I$(shell $(CC) -print-file-name=include) +$(PURGATORY): LDFLAGS=$($(ARCH)_PURGATORY_EXTRA_CFLAGS)\ + -Wl,--no-undefined -nostartfiles -nostdlib \ + -nodefaultlibs -e purgatory_start -r \ + -Wl,-Map=$(PURGATORY_MAP) + +$(PURGATORY): $(PURGATORY_OBJS) + $(MKDIR) -p $(@D) + $(CC) $(CFLAGS) $(LDFLAGS) -o $@.sym $^ +# $(LD) $(LDFLAGS) $(EXTRA_LDFLAGS) --no-undefined -e purgatory_start -r -o $@ $(PURGATORY_OBJS) $(UTIL_LIB) + $(STRIP) --strip-debug -o $@ $@.sym + +echo:: + @echo "PURGATORY_SRCS $(PURGATORY_SRCS)" + @echo "PURGATORY_DEPS $(PURGATORY_DEPS)" + @echo "PURGATORY_OBJS $(PURGATORY_OBJS)" diff --git a/purgatory/arch/alpha/Makefile b/purgatory/arch/alpha/Makefile new file mode 100644 index 0000000..c58693f --- /dev/null +++ b/purgatory/arch/alpha/Makefile @@ -0,0 +1,8 @@ +# +# Purgatory alpha +# + +alpha_PURGATORY_SRCS = + +dist += purgatory/arch/alpha/Makefile $(alpha_PURGATORY_SRCS) + diff --git a/purgatory/arch/arm/Makefile b/purgatory/arch/arm/Makefile new file mode 100644 index 0000000..690377e --- /dev/null +++ b/purgatory/arch/arm/Makefile @@ -0,0 +1,9 @@ +# +# Purgatory arm +# + +arm_PURGATORY_SRCS = + +dist += purgatory/arch/arm/Makefile $(arm_PURGATORY_SRCS) + + diff --git a/purgatory/arch/arm64/Makefile b/purgatory/arch/arm64/Makefile new file mode 100644 index 0000000..80068ca --- /dev/null +++ b/purgatory/arch/arm64/Makefile @@ -0,0 +1,19 @@ + +arm64_PURGATORY_EXTRA_CFLAGS = \ + -mcmodel=large \ + -fno-PIC \ + -fno-stack-protector \ + -fno-asynchronous-unwind-tables \ + -Wundef \ + -Werror-implicit-function-declaration \ + -Wdeclaration-after-statement \ + -Werror=implicit-int \ + -Werror=strict-prototypes + +arm64_PURGATORY_SRCS += \ + purgatory/arch/arm64/entry.S \ + purgatory/arch/arm64/purgatory-arm64.c + +dist += \ + $(arm64_PURGATORY_SRCS) \ + purgatory/arch/arm64/Makefile diff --git a/purgatory/arch/arm64/entry.S b/purgatory/arch/arm64/entry.S new file mode 100644 index 0000000..adf16f4 --- /dev/null +++ b/purgatory/arch/arm64/entry.S @@ -0,0 +1,51 @@ +/* + * ARM64 purgatory. + */ + +.macro size, sym:req + .size \sym, . - \sym +.endm + +.text + +.globl purgatory_start +purgatory_start: + + adr x19, .Lstack + mov sp, x19 + + bl purgatory + + /* Start new image. */ + ldr x17, arm64_kernel_entry + ldr x0, arm64_dtb_addr + mov x1, xzr + mov x2, xzr + mov x3, xzr + br x17 + +size purgatory_start + +.ltorg + +.align 4 + .rept 256 + .quad 0 + .endr +.Lstack: + +.data + +.align 3 + +.globl arm64_kernel_entry +arm64_kernel_entry: + .quad 0 +size arm64_kernel_entry + +.globl arm64_dtb_addr +arm64_dtb_addr: + .quad 0 +size arm64_dtb_addr + +.end
\ No newline at end of file diff --git a/purgatory/arch/arm64/purgatory-arm64.c b/purgatory/arch/arm64/purgatory-arm64.c new file mode 100644 index 0000000..b4d8578 --- /dev/null +++ b/purgatory/arch/arm64/purgatory-arm64.c @@ -0,0 +1,34 @@ +/* + * ARM64 purgatory. + */ + +#include <stdint.h> +#include <purgatory.h> + +/* Symbols set by kexec. */ + +uint8_t *arm64_sink __attribute__ ((section ("data"))); +extern void (*arm64_kernel_entry)(uint64_t, uint64_t, uint64_t, uint64_t); +extern uint64_t arm64_dtb_addr; + +void putchar(int ch) +{ + if (!arm64_sink) + return; + + *arm64_sink = ch; + + if (ch == '\n') + *arm64_sink = '\r'; +} + +void post_verification_setup_arch(void) +{ + printf("purgatory: booting kernel now\n"); +} + +void setup_arch(void) +{ + printf("purgatory: entry=%lx\n", (unsigned long)arm64_kernel_entry); + printf("purgatory: dtb=%lx\n", arm64_dtb_addr); +} diff --git a/purgatory/arch/i386/Makefile b/purgatory/arch/i386/Makefile new file mode 100644 index 0000000..1532219 --- /dev/null +++ b/purgatory/arch/i386/Makefile @@ -0,0 +1,21 @@ +# +# Purgatory i386 +# + +i386_PURGATORY_SRCS = purgatory/arch/i386/entry32-16.S +i386_PURGATORY_SRCS += purgatory/arch/i386/entry32-16-debug.S +i386_PURGATORY_SRCS += purgatory/arch/i386/entry32.S +i386_PURGATORY_SRCS += purgatory/arch/i386/setup-x86.S +i386_PURGATORY_SRCS += purgatory/arch/i386/stack.S +i386_PURGATORY_SRCS += purgatory/arch/i386/compat_x86_64.S +i386_PURGATORY_SRCS += purgatory/arch/i386/purgatory-x86.c +i386_PURGATORY_SRCS += purgatory/arch/i386/console-x86.c +i386_PURGATORY_SRCS += purgatory/arch/i386/vga.c +i386_PURGATORY_SRCS += purgatory/arch/i386/pic.c +i386_PURGATORY_SRCS += purgatory/arch/i386/crashdump_backup.c + +dist += purgatory/arch/i386/Makefile $(i386_PURGATORY_SRCS) \ + purgatory/arch/i386/purgatory-x86.h \ + purgatory/arch/i386/include/arch/io.h \ + purgatory/arch/i386/include/arch/debug.h + diff --git a/purgatory/arch/i386/compat_x86_64.S b/purgatory/arch/i386/compat_x86_64.S new file mode 100644 index 0000000..1649085 --- /dev/null +++ b/purgatory/arch/i386/compat_x86_64.S @@ -0,0 +1,100 @@ +/* + * kexec: Linux boots Linux + * + * Copyright (C) 2003,2004,2005 Eric Biederman (ebiederm@xmission.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation (version 2 of the License). + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + .equ MSR_K6_EFER, 0xC0000080 + .equ EFER_LME, 0x00000100 + .equ X86_CR4_PAE, 0x00000020 + .equ CR0_PG, 0x80000000 + + .globl compat_x86_64, compat_x86_64_entry32 + .text + .code64 + .balign 16 +compat_x86_64: + /* Setup a temporary gdt */ + /* This also acts as a serializing instruction ensuring + * my self modifying code works. + */ + lgdt gdt(%rip) + + /* Switch to 32bit compatiblity mode */ + ljmp *lm_exit_addr(%rip) +lm_exit: + .code32 + + /* Disable paging */ + movl %cr0, %eax + andl $~CR0_PG, %eax + movl %eax, %cr0 + + /* Disable long mode */ + movl $MSR_K6_EFER, %ecx + rdmsr + andl $~EFER_LME, %eax + wrmsr + + /* Disable PAE */ + xorl %eax, %eax + movl %eax, %cr4 + + /* load the data segments */ + movl $0x18, %eax /* data segment */ + movl %eax, %ds + movl %eax, %es + movl %eax, %ss + movl %eax, %fs + movl %eax, %gs + + /* set all of the registers to known values */ + /* leave %esp alone */ + + xorl %eax, %eax + xorl %ebx, %ebx + xorl %ecx, %ecx + xorl %edx, %edx + xorl %esi, %esi + xorl %edi, %edi + xorl %ebp, %ebp + + jmp *compat_x86_64_entry32 + + .section ".rodata" + .balign 16 +gdt: /* 0x00 unusable segment + * 0x08 unused + * so use them as the gdt ptr + */ + .word gdt_end - gdt - 1 + # A quad word pointer to the gdt with the high 32bits 0 + .long gdt, 0 + .word 0, 0, 0 + + /* 0x10 4GB flat code segment */ + .word 0xFFFF, 0x0000, 0x9A00, 0x00CF + /* 0x18 4GB flat data segment */ + .word 0xFFFF, 0x0000, 0x9200, 0x00CF +gdt_end: + +lm_exit_addr: + .long lm_exit + .long 0x10 + +compat_x86_64_entry32: + .long 0 +.size compat_x86_64_entry32, . - compat_x86_64_entry32 diff --git a/purgatory/arch/i386/console-x86.c b/purgatory/arch/i386/console-x86.c new file mode 100644 index 0000000..9773573 --- /dev/null +++ b/purgatory/arch/i386/console-x86.c @@ -0,0 +1,135 @@ +#include <stdint.h> +#include <arch/io.h> +#include <purgatory.h> + +/* + * VGA + * ============================================================================= + */ + +/* Simple VGA output */ + +#define VGABASE ((void *)0xb8000) + +#define MAX_YPOS 25 +#define MAX_XPOS 80 + +static int current_ypos = 1, current_xpos = 0; +uint8_t console_vga = 0; + +static void putchar_vga(int ch) +{ + int i, k, j; + if (!console_vga) + return; + + if (current_ypos >= MAX_YPOS) { + /* scroll 1 line up */ + for (k = 1, j = 0; k < MAX_YPOS; k++, j++) { + for (i = 0; i < MAX_XPOS; i++) { + writew(readw(VGABASE + 2*(MAX_XPOS*k + i)), + VGABASE + 2*(MAX_XPOS*j + i)); + } + } + for (i = 0; i < MAX_XPOS; i++) + writew(0x720, VGABASE + 2*(MAX_XPOS*j + i)); + current_ypos = MAX_YPOS-1; + } + if (ch == '\n') { + current_xpos = 0; + current_ypos++; + } else if (ch != '\r') { + writew(((0x7 << 8) | (unsigned short) ch), + VGABASE + 2*(MAX_XPOS*current_ypos + + current_xpos++)); + if (current_xpos >= MAX_XPOS) { + current_xpos = 0; + current_ypos++; + } + } +} + +/* + * Serial + * ============================================================================= + */ + +/* Base Address */ +uint8_t console_serial = 0; +uint16_t serial_base = 0x3f8; /* TTYS0 */ +uint32_t serial_baud = 0; + +#define XMTRDY 0x20 + +#define DLAB 0x80 + +#define TXR 0 /* Transmit register (WRITE) */ +#define TBR 0 /* Transmit register (WRITE) */ +#define RXR 0 /* Receive register (READ) */ +#define IER 1 /* Interrupt Enable */ +#define IIR 2 /* Interrupt ID */ +#define FCR 2 /* FIFO control */ +#define LCR 3 /* Line control */ +#define MCR 4 /* Modem control */ +#define LSR 5 /* Line Status */ +#define MSR 6 /* Modem Status */ +#define DLL 0 /* Divisor Latch Low */ +#define DLH 1 /* Divisor latch High */ + +static void serial_init(void) +{ + static int initialized = 0; + if (!initialized) { + unsigned lcr; + outb(0x3, serial_base + LCR); /* 8n1 */ + outb(0, serial_base + IER); /* no interrupt */ + outb(0, serial_base + FCR); /* no fifo */ + outb(0x3, serial_base + MCR); /* DTR + RTS */ + + lcr = inb(serial_base + LCR); + outb(lcr | DLAB, serial_base + LCR); + /* By default don't change the serial port baud rate */ + if (serial_baud) { + unsigned divisor = 115200 / serial_baud; + outb(divisor & 0xff, serial_base + DLL); + outb((divisor >> 8) & 0xff, serial_base + DLH); + } + outb(lcr & ~DLAB, serial_base + LCR); + initialized = 1; + } +} + +static void serial_tx_byte(unsigned byte) +{ + /* Ensure the serial port is initialized */ + serial_init(); + + /* Wait until I can send a byte */ + while((inb(serial_base + LSR) & 0x20) == 0) + ; + outb(byte, serial_base + TBR); + /* Wait until the byte is transmitted */ + while(!(inb(serial_base + LSR) & 0x40)) + ; +} + +static void putchar_serial(int ch) +{ + if (!console_serial) { + return; + } + if (ch == '\n') { + serial_tx_byte('\r'); + } + serial_tx_byte(ch); +} + +/* Generic wrapper function */ + +void putchar(int ch) +{ + putchar_vga(ch); + putchar_serial(ch); +} + + diff --git a/purgatory/arch/i386/crashdump_backup.c b/purgatory/arch/i386/crashdump_backup.c new file mode 100644 index 0000000..365eb5d --- /dev/null +++ b/purgatory/arch/i386/crashdump_backup.c @@ -0,0 +1,51 @@ +/* + * kexec: Linux boots Linux + * + * Created by: Vivek goyal (vgoyal@in.ibm.com) + * Copyright (C) IBM Corporation, 2005. All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation (version 2 of the License). + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <stdint.h> +#include <string.h> + +/* Backup region start gets set after /proc/iomem has been parsed. */ +/* We reuse the same code for x86_64 also so changing backup_start to + unsigned long */ +unsigned long backup_start = 0; + +unsigned long backup_src_start = 0; +unsigned long backup_src_size = 0; + +/* Backup first 640K of memory to backup region as reserved by kexec. + * Assuming first 640K has to be present on i386 machines and no address + * validity checks have to be performed. */ + +void crashdump_backup_memory(void) +{ + void *dest, *src; + size_t size; + + src = (void *) backup_src_start; + size = (size_t) backup_src_size; + + if (!size) + return; + + if (backup_start) { + dest = (void *)(backup_start); + memcpy(dest, src, size); + } +} diff --git a/purgatory/arch/i386/entry32-16-debug.S b/purgatory/arch/i386/entry32-16-debug.S new file mode 100644 index 0000000..5167944 --- /dev/null +++ b/purgatory/arch/i386/entry32-16-debug.S @@ -0,0 +1,200 @@ +/* + * kexec: Linux boots Linux + * + * Copyright (C) 2003,2004 Eric Biederman (ebiederm@xmission.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation (version 2 of the License). + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "arch/debug.h" + +#undef i386 + .text + .globl entry16_debug, entry16_debug_regs + .globl entry16_debug_pre32 + .globl entry16_debug_first32 + .globl entry16_debug_old_first32 + .arch i386 + .balign 16 +entry16_debug: + .code32 + /* Compute where I am running at (assumes esp valid) */ + call 1f +1: popl %ebx + subl $(1b - entry16_debug), %ebx + + /* Fixup my real mode segment */ + movl %ebx, %eax + shrl $4, %eax + movw %ax, (2 + realptr - entry16_debug)(%ebx) + + /* Fixup the gdt */ + leal (gdt - entry16_debug)(%ebx), %eax + movl %eax, (0x02 + gdt - entry16_debug)(%ebx) + + movl %ebx, %eax + shll $16, %eax + + movl %ebx, %ecx + shrl $16, %ecx + andl $0xff, %ecx + + movl %ebx, %edx + andl $0xff000000, %edx + orl %edx, %ecx + + orl %eax, (0x08 + gdt - entry16_debug)(%ebx) + orl %ecx, (0x0c + gdt - entry16_debug)(%ebx) + orl %eax, (0x10 + gdt - entry16_debug)(%ebx) + orl %ecx, (0x14 + gdt - entry16_debug)(%ebx) + + +DEBUG_CHAR('a') + /* Setup the classic BIOS interrupt table at 0x0 */ + lidt (idtptr - entry16_debug)(%ebx) + +DEBUG_CHAR('b') + /* Provide us with 16bit segments that we can use */ + lgdt (gdt - entry16_debug)(%ebx) + +DEBUG_CHAR('c') + /* Note we don't disable the a20 line, (this shouldn't be required) + * The code to do it is in kexec_test and it is a real pain. + * I will worry about that when I need it. + */ + + /* Load 16bit data segments, to ensure the segment limits are set */ + movl $0x10, %eax + movl %eax, %ds + movl %eax, %es + movl %eax, %ss + movl %eax, %fs + movl %eax, %gs + +DEBUG_CHAR('d') + + /* switch to 16bit mode */ + ljmp $0x08, $1f - entry16_debug +1: + .code16 +DEBUG_CHAR('e') + /* Disable Paging and protected mode */ + /* clear the PG & PE bits of CR0 */ + movl %cr0,%eax + andl $~((1 << 31)|(1<<0)),%eax + movl %eax,%cr0 + +DEBUG_CHAR('f') + /* make intersegment jmp to flush the processor pipeline + * and reload %cs:%eip (to clear upper 16 bits of %eip). + */ + ljmp *(realptr - entry16_debug) +3: +DEBUG_CHAR('g') + /* we are in real mode now + * set up the real mode segment registers : %ds, $ss, %es + */ + /* Setup the data segment */ + movw %cs, %ax + movw %ax, %ds + +DEBUG_CHAR('h') + /* Load the registers */ + movl eax - entry16_debug, %eax + movl ebx - entry16_debug, %ebx + movl ecx - entry16_debug, %ecx + movl edx - entry16_debug, %edx + movl esi - entry16_debug, %esi + movl edi - entry16_debug, %esi + movl esp - entry16_debug, %esp + movl ebp - entry16_debug, %ebp + movw es - entry16_debug, %es + movw ss - entry16_debug, %ss + movw fs - entry16_debug, %fs + movw gs - entry16_debug, %gs + movw ds - entry16_debug, %ds + + /* Jump to the kernel entrypoint */ + ljmp %cs:*(realdest - entry16_debug) + + .balign 4 +entry16_debug_regs: +eax: .long 0x00000000 +ebx: .long 0x00000000 +ecx: .long 0x00000000 +edx: .long 0x00000000 +esi: .long 0x00000000 +edi: .long 0x00000000 +esp: .long 0x00000000 +ebp: .long 0x00000000 +ds: .word 0x0000 +es: .word 0x0000 +ss: .word 0x0000 +fs: .word 0x0000 +gs: .word 0x0000 +realdest: +ip: .word 0x0000 +cs: .word 0x0000 +pad: .word 0x0000 + .size entry16_debug_regs, . - entry16_debug_regs + + .balign 16 +realptr: + .word 3b - entry16_debug + .word 0x0000 + + .data + .balign 16 + +idtptr: + /* 256 entry idt at 0 */ + .word 0x400 - 1 + .word 0, 0 + +gdt: + /* 0x00 unusable segment so used as the gdt ptr */ + .word gdt_end - gdt - 1 + .long 0 /* gdt */ + .word 0 + + /* 0x08 16 bit real mode code segment */ + .word 0xffff, 0x0000 + .byte 0x00, 0x9b, 0x00, 0x00 + + /* 0x10 16 bit real mode data segment */ + .word 0xffff, 0x0000 + .byte 0x00, 0x93, 0x00, 0x00 +gdt_end: + .size gdt, . - gdt + + .text +entry16_debug_pre32: + .code16 +DEBUG_CHAR('i') + cli # no interrupts allowed ! + movb $0x80, %al # disable NMI for bootup + # sequence + outb %al, $0x70 + lret +.size entry16_debug_pre32, . - entry16_debug_pre32 + +entry16_debug_first32: + .code32 +DEBUG_CHAR('j') + .byte 0xb8 /* movl $0x10000, %eax */ +entry16_debug_old_first32: + .long 0x100000 + .size entry16_debug_old_first32, . - entry16_debug_old_first32 + jmp *%eax +.size entry16_debug_first32, . - entry16_debug_first32 diff --git a/purgatory/arch/i386/entry32-16.S b/purgatory/arch/i386/entry32-16.S new file mode 100644 index 0000000..c051aab --- /dev/null +++ b/purgatory/arch/i386/entry32-16.S @@ -0,0 +1,165 @@ +/* + * kexec: Linux boots Linux + * + * Copyright (C) 2003,2004 Eric Biederman (ebiederm@xmission.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation (version 2 of the License). + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#undef i386 + .text + .globl entry16, entry16_regs + .arch i386 + .balign 16 +entry16: + .code32 + /* Compute where I am running at (assumes esp valid) */ + call 1f +1: popl %ebx + subl $(1b - entry16), %ebx + + /* Fixup my real mode segment */ + movl %ebx, %eax + shrl $4, %eax + movw %ax, (2 + realptr - entry16)(%ebx) + + /* Fixup the gdt */ + leal (gdt - entry16)(%ebx), %eax + movl %eax, (0x02 + gdt - entry16)(%ebx) + + movl %ebx, %eax + shll $16, %eax + + movl %ebx, %ecx + shrl $16, %ecx + andl $0xff, %ecx + + movl %ebx, %edx + andl $0xff000000, %edx + orl %edx, %ecx + + orl %eax, (0x08 + gdt - entry16)(%ebx) + orl %ecx, (0x0c + gdt - entry16)(%ebx) + orl %eax, (0x10 + gdt - entry16)(%ebx) + orl %ecx, (0x14 + gdt - entry16)(%ebx) + + + /* Setup the classic BIOS interrupt table at 0x0 */ + lidt (idtptr - entry16)(%ebx) + + /* Provide us with 16bit segments that we can use */ + lgdt (gdt - entry16)(%ebx) + + /* Note we don't disable the a20 line, (this shouldn't be required) + * The code to do it is in kexec_test and it is a real pain. + * I will worry about that when I need it. + */ + + /* Load 16bit data segments, to ensure the segment limits are set */ + movl $0x10, %eax + movl %eax, %ds + movl %eax, %es + movl %eax, %ss + movl %eax, %fs + movl %eax, %gs + + /* switch to 16bit mode */ + ljmp $0x08, $1f - entry16 +1: + .code16 + /* Disable Paging and protected mode */ + /* clear the PG & PE bits of CR0 */ + movl %cr0,%eax + andl $~((1 << 31)|(1<<0)),%eax + movl %eax,%cr0 + + /* make intersegment jmp to flush the processor pipeline + * and reload %cs:%eip (to clear upper 16 bits of %eip). + */ + ljmp *(realptr - entry16) +3: + /* we are in real mode now + * set up the real mode segment registers : %ds, $ss, %es + */ + /* Setup the data segment */ + movw %cs, %ax + movw %ax, %ds + + /* Load the registers */ + movl eax - entry16, %eax + movl ebx - entry16, %ebx + movl ecx - entry16, %ecx + movl edx - entry16, %edx + movl esi - entry16, %esi + movl edi - entry16, %esi + movl esp - entry16, %esp + movl ebp - entry16, %ebp + movw es - entry16, %es + movw ss - entry16, %ss + movw fs - entry16, %fs + movw gs - entry16, %gs + movw ds - entry16, %ds + + /* Jump to the kernel entrypoint */ + ljmp %cs:*(realdest - entry16) + + .balign 4 +entry16_regs: +eax: .long 0x00000000 +ebx: .long 0x00000000 +ecx: .long 0x00000000 +edx: .long 0x00000000 +esi: .long 0x00000000 +edi: .long 0x00000000 +esp: .long 0x00000000 +ebp: .long 0x00000000 +ds: .word 0x0000 +es: .word 0x0000 +ss: .word 0x0000 +fs: .word 0x0000 +gs: .word 0x0000 +realdest: +ip: .word 0x0000 +cs: .word 0x0000 +pad: .word 0x0000 + .size entry16_regs, . - entry16_regs + + .balign 16 +realptr: + .word 3b - entry16 + .word 0x0000 + + .data + .balign 16 + +idtptr: + /* 256 entry idt at 0 */ + .word 0x400 - 1 + .word 0, 0 + + .balign 16 +gdt: + /* 0x00 unusable segment so used as the gdt ptr */ + .word gdt_end - gdt - 1 + .long 0 /* gdt */ + .word 0 + + /* 0x08 16 bit real mode code segment */ + .word 0xffff, 0x0000 + .byte 0x00, 0x9b, 0x00, 0x00 + + /* 0x10 16 bit real mode data segment */ + .word 0xffff, 0x0000 + .byte 0x00, 0x93, 0x00, 0x00 +gdt_end: diff --git a/purgatory/arch/i386/entry32.S b/purgatory/arch/i386/entry32.S new file mode 100644 index 0000000..f7a494f --- /dev/null +++ b/purgatory/arch/i386/entry32.S @@ -0,0 +1,110 @@ +/* + * purgatory: setup code + * + * Copyright (C) 2003,2004 Eric Biederman (ebiederm@xmission.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation (version 2 of the License). + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#undef i386 + + .text + .arch i386 + .globl entry32, entry32_regs +entry32: + .code32 + + /* Setup a gdt that should that is generally usefully */ + lgdt %cs:gdt + + /* load the data segments */ + movl $0x18, %eax /* data segment */ + movl %eax, %ds + movl %eax, %es + movl %eax, %ss + movl %eax, %fs + movl %eax, %gs + + /* load the code segment */ + ljmp $0x10,$1f +1: + + /* Load the registers */ + movl eax, %eax + movl ecx, %ecx + movl edx, %edx + movl esi, %esi + movl edi, %edi + movl esp, %esp + movl ebp, %ebp + movl ebx, %ebx + + /* Jump to the loaded image */ + jmpl *(eip) + + .section ".rodata" + .balign 16 +gdt: + /* 0x00 unusable segment so used as the gdt ptr */ + .word gdt_end - gdt - 1 + .long gdt + .word 0 + + /* 0x08 dummy */ + .word 0x0000, 0x0000, 0x0000, 0x000 + + /* Documented linux kernel segments */ + /* 0x10 4GB flat code segment */ + .word 0xFFFF, 0x0000, 0x9A00, 0x00CF + /* 0x18 4GB flat data segment */ + .word 0xFFFF, 0x0000, 0x9200, 0x00CF + + /* 0x20 dummy */ + .word 0x0000, 0x0000, 0x0000, 0x000 + /* 0x28 dummy */ + .word 0x0000, 0x0000, 0x0000, 0x000 + /* 0x30 dummy */ + .word 0x0000, 0x0000, 0x0000, 0x000 + /* 0x38 dummy */ + .word 0x0000, 0x0000, 0x0000, 0x000 + /* 0x40 dummy */ + .word 0x0000, 0x0000, 0x0000, 0x000 + /* 0x48 dummy */ + .word 0x0000, 0x0000, 0x0000, 0x000 + /* 0x50 dummy */ + .word 0x0000, 0x0000, 0x0000, 0x000 + /* 0x58 dummy */ + .word 0x0000, 0x0000, 0x0000, 0x000 + + /* Segments used by the 2.5.x kernel */ + /* 0x60 4GB flat code segment */ + .word 0xFFFF, 0x0000, 0x9A00, 0x00CF + /* 0x68 4GB flat data segment */ + .word 0xFFFF, 0x0000, 0x9200, 0x00CF +gdt_end: + + .data + .balign 4 +entry32_regs: +eax: .long 0x00000000 +ebx: .long 0x00000000 +ecx: .long 0x00000000 +edx: .long 0x00000000 +esi: .long 0x00000000 +edi: .long 0x00000000 +esp: .long 0x00000000 +ebp: .long 0x00000000 +eip: .long entry16 + .size entry32_regs, . - entry32_regs + diff --git a/purgatory/arch/i386/include/arch/debug.h b/purgatory/arch/i386/include/arch/debug.h new file mode 100644 index 0000000..25853cd --- /dev/null +++ b/purgatory/arch/i386/include/arch/debug.h @@ -0,0 +1,316 @@ +/* Base Address */ +#define TTYS0_BASE 0x3f8 +/* Data */ +#define TTYS0_RBR (TTYS0_BASE+0x00) +#define TTYS0_TBR (TTYS0_BASE+0x00) +/* Control */ +#define TTYS0_IER (TTYS0_BASE+0x01) +#define TTYS0_IIR (TTYS0_BASE+0x02) +#define TTYS0_FCR (TTYS0_BASE+0x02) +#define TTYS0_LCR (TTYS0_BASE+0x03) +#define TTYS0_MCR (TTYS0_BASE+0x04) + +#define TTYS0_DLL (TTYS0_BASE+0x00) +#define TTYS0_DLM (TTYS0_BASE+0x01) +/* Status */ +#define TTYS0_LSR (TTYS0_BASE+0x05) +#define TTYS0_MSR (TTYS0_BASE+0x06) +#define TTYS0_SCR (TTYS0_BASE+0x07) + +#define TTYS0_BAUD 9600 +#define TTYS0_DIV (115200/TTYS0_BAUD) +#define TTYS0_DIV_LO (TTYS0_DIV&0xFF) +#define TTYS0_DIV_HI ((TTYS0_DIV >> 8)&0xFF) + +#if ((115200%TTYS0_BAUD) != 0) +#error Bad ttyS0 baud rate +#endif + +#define TTYS0_INIT \ + /* disable interrupts */ \ + movb $0x00, %al ; \ + movw $TTYS0_IER, %dx ; \ + outb %al, %dx ; \ + ; \ + /* enable fifos */ \ + movb $0x01, %al ; \ + movw $TTYS0_FCR, %dx ; \ + outb %al, %dx ; \ + ; \ + /* Set Baud Rate Divisor to TTYS0_BAUD */ \ + movw $TTYS0_LCR, %dx ; \ + movb $0x83, %al ; \ + outb %al, %dx ; \ + ; \ + movw $TTYS0_DLL, %dx ; \ + movb $TTYS0_DIV_LO, %al ; \ + outb %al, %dx ; \ + ; \ + movw $TTYS0_DLM, %dx ; \ + movb $TTYS0_DIV_HI, %al ; \ + outb %al, %dx ; \ + ; \ + movw $TTYS0_LCR, %dx ; \ + movb $0x03, %al ; \ + outb %al, %dx + + + /* uses: ax, dx */ +#define TTYS0_TX_AL \ + mov %al, %ah ; \ +9: mov $TTYS0_LSR, %dx ; \ + inb %dx, %al ; \ + test $0x20, %al ; \ + je 9b ; \ + mov $TTYS0_TBR, %dx ; \ + mov %ah, %al ; \ + outb %al, %dx + + /* uses: ax, dx */ +#define TTYS0_TX_CHAR(byte) \ + mov byte, %al ; \ + TTYS0_TX_AL + + /* uses: eax, dx */ +#define TTYS0_TX_HEX32(lword) \ + mov lword, %eax ; \ + shr $28, %eax ; \ + add $'0', %al ; \ + cmp $'9', %al ; \ + jle 9f ; \ + add $39, %al ; \ +9: ; \ + TTYS0_TX_AL ; \ + ; \ + mov lword, %eax ; \ + shr $24, %eax ; \ + and $0x0f, %al ; \ + add $'0', %al ; \ + cmp $'9', %al ; \ + jle 9f ; \ + add $39, %al ; \ +9: ; \ + TTYS0_TX_AL ; \ + ; \ + mov lword, %eax ; \ + shr $20, %eax ; \ + and $0x0f, %al ; \ + add $'0', %al ; \ + cmp $'9', %al ; \ + jle 9f ; \ + add $39, %al ; \ +9: ; \ + TTYS0_TX_AL ; \ + ; \ + mov lword, %eax ; \ + shr $16, %eax ; \ + and $0x0f, %al ; \ + add $'0', %al ; \ + cmp $'9', %al ; \ + jle 9f ; \ + add $39, %al ; \ +9: ; \ + TTYS0_TX_AL ; \ + ; \ + mov lword, %eax ; \ + shr $12, %eax ; \ + and $0x0f, %al ; \ + add $'0', %al ; \ + cmp $'9', %al ; \ + jle 9f ; \ + add $39, %al ; \ +9: ; \ + TTYS0_TX_AL ; \ + ; \ + mov lword, %eax ; \ + shr $8, %eax ; \ + and $0x0f, %al ; \ + add $'0', %al ; \ + cmp $'9', %al ; \ + jle 9f ; \ + add $39, %al ; \ +9: ; \ + TTYS0_TX_AL ; \ + ; \ + mov lword, %eax ; \ + shr $4, %eax ; \ + and $0x0f, %al ; \ + add $'0', %al ; \ + cmp $'9', %al ; \ + jle 9f ; \ + add $39, %al ; \ +9: ; \ + TTYS0_TX_AL ; \ + ; \ + mov lword, %eax ; \ + and $0x0f, %al ; \ + add $'0', %al ; \ + cmp $'9', %al ; \ + jle 9f ; \ + add $39, %al ; \ +9: ; \ + TTYS0_TX_AL + + /* uses: rax, dx */ +#define TTYS0_TX_HEX64(lword) \ + mov lword, %rax ; \ + shr $60, %rax ; \ + add $'0', %al ; \ + cmp $'9', %al ; \ + jle 9f ; \ + add $39, %al ; \ +9: ; \ + TTYS0_TX_AL ; \ + ; \ + mov lword, %rax ; \ + shr $56, %rax ; \ + and $0x0f, %al ; \ + add $'0', %al ; \ + cmp $'9', %al ; \ + jle 9f ; \ + add $39, %al ; \ +9: ; \ + TTYS0_TX_AL ; \ + ; \ + mov lword, %rax ; \ + shr $52, %rax ; \ + and $0x0f, %al ; \ + add $'0', %al ; \ + cmp $'9', %al ; \ + jle 9f ; \ + add $39, %al ; \ +9: ; \ + TTYS0_TX_AL ; \ + ; \ + mov lword, %rax ; \ + shr $48, %rax ; \ + and $0x0f, %al ; \ + add $'0', %al ; \ + cmp $'9', %al ; \ + jle 9f ; \ + add $39, %al ; \ +9: ; \ + TTYS0_TX_AL ; \ + ; \ + mov lword, %rax ; \ + shr $44, %rax ; \ + and $0x0f, %al ; \ + add $'0', %al ; \ + cmp $'9', %al ; \ + jle 9f ; \ + add $39, %al ; \ +9: ; \ + TTYS0_TX_AL ; \ + ; \ + mov lword, %rax ; \ + shr $40, %rax ; \ + and $0x0f, %al ; \ + add $'0', %al ; \ + cmp $'9', %al ; \ + jle 9f ; \ + add $39, %al ; \ +9: ; \ + TTYS0_TX_AL ; \ + ; \ + mov lword, %rax ; \ + shr $36, %rax ; \ + and $0x0f, %al ; \ + add $'0', %al ; \ + cmp $'9', %al ; \ + jle 9f ; \ + add $39, %al ; \ +9: ; \ + TTYS0_TX_AL ; \ + ; \ + mov lword, %rax ; \ + shr $32, %rax ; \ + and $0x0f, %al ; \ + add $'0', %al ; \ + cmp $'9', %al ; \ + jle 9f ; \ + add $39, %al ; \ +9: ; \ + TTYS0_TX_AL ; \ + ; \ + mov lword, %rax ; \ + shr $28, %rax ; \ + add $'0', %al ; \ + cmp $'9', %al ; \ + jle 9f ; \ + add $39, %al ; \ +9: ; \ + TTYS0_TX_AL ; \ + ; \ + mov lword, %rax ; \ + shr $24, %rax ; \ + and $0x0f, %al ; \ + add $'0', %al ; \ + cmp $'9', %al ; \ + jle 9f ; \ + add $39, %al ; \ +9: ; \ + TTYS0_TX_AL ; \ + ; \ + mov lword, %rax ; \ + shr $20, %rax ; \ + and $0x0f, %al ; \ + add $'0', %al ; \ + cmp $'9', %al ; \ + jle 9f ; \ + add $39, %al ; \ +9: ; \ + TTYS0_TX_AL ; \ + ; \ + mov lword, %rax ; \ + shr $16, %rax ; \ + and $0x0f, %al ; \ + add $'0', %al ; \ + cmp $'9', %al ; \ + jle 9f ; \ + add $39, %al ; \ +9: ; \ + TTYS0_TX_AL ; \ + ; \ + mov lword, %rax ; \ + shr $12, %rax ; \ + and $0x0f, %al ; \ + add $'0', %al ; \ + cmp $'9', %al ; \ + jle 9f ; \ + add $39, %al ; \ +9: ; \ + TTYS0_TX_AL ; \ + ; \ + mov lword, %rax ; \ + shr $8, %rax ; \ + and $0x0f, %al ; \ + add $'0', %al ; \ + cmp $'9', %al ; \ + jle 9f ; \ + add $39, %al ; \ +9: ; \ + TTYS0_TX_AL ; \ + ; \ + mov lword, %rax ; \ + shr $4, %rax ; \ + and $0x0f, %al ; \ + add $'0', %al ; \ + cmp $'9', %al ; \ + jle 9f ; \ + add $39, %al ; \ +9: ; \ + TTYS0_TX_AL ; \ + ; \ + mov lword, %rax ; \ + and $0x0f, %al ; \ + add $'0', %al ; \ + cmp $'9', %al ; \ + jle 9f ; \ + add $39, %al ; \ +9: ; \ + TTYS0_TX_AL + + +#define DEBUG_CHAR(x) TTYS0_TX_CHAR($x) ; TTYS0_TX_CHAR($'\r') ; TTYS0_TX_CHAR($'\n') +#define DEBUG_TX_HEX32(x) TTYS0_TX_HEX32(x); TTYS0_TX_CHAR($'\r') ; TTYS0_TX_CHAR($'\n') +#define DEBUG_TX_HEX64(x) TTYS0_TX_HEX64(x); TTYS0_TX_CHAR($'\r') ; TTYS0_TX_CHAR($'\n') diff --git a/purgatory/arch/i386/include/arch/io.h b/purgatory/arch/i386/include/arch/io.h new file mode 100644 index 0000000..0a9430a --- /dev/null +++ b/purgatory/arch/i386/include/arch/io.h @@ -0,0 +1,110 @@ +#ifndef ARCH_I386_IO_H +#define ARCH_I386_IO_H + +#include <stdint.h> +/* Helper functions for directly doing I/O */ + +static inline __attribute__((__always_inline__)) +uint8_t inb(uint16_t port) +{ + uint8_t result; + + __asm__ __volatile__ ( + "inb %w1,%0" + :"=a" (result) + :"Nd" (port)); + return result; +} + +static inline __attribute__((__always_inline__)) +uint16_t inw(uint16_t port) +{ + uint16_t result; + + __asm__ __volatile__ ( + "inw %w1,%0" + :"=a" (result) + :"Nd" (port)); + return result; +} + +static inline __attribute__((__always_inline__)) +uint32_t inl(uint32_t port) +{ + uint32_t result; + + __asm__ __volatile__ ( + "inl %w1,%0" + :"=a" (result) + :"Nd" (port)); + return result; +} + +static inline __attribute__((__always_inline__)) +void outb (uint8_t value, uint16_t port) +{ + __asm__ __volatile__ ( + "outb %b0,%w1" + : + :"a" (value), "Nd" (port)); +} + +static inline __attribute__((__always_inline__)) +void outw (uint16_t value, uint16_t port) +{ + __asm__ __volatile__ ( + "outw %w0,%w1" + : + :"a" (value), "Nd" (port)); +} + +static inline __attribute__((__always_inline__)) +void outl (uint32_t value, uint16_t port) +{ + __asm__ __volatile__ ( + "outl %0,%w1" + : + :"a" (value), "Nd" (port)); +} + + +/* + * readX/writeX() are used to access memory mapped devices. On some + * architectures the memory mapped IO stuff needs to be accessed + * differently. On the x86 architecture, we just read/write the + * memory location directly. + */ + +static inline __attribute__((__always_inline__)) +unsigned char readb(const volatile void *addr) +{ + return *(volatile unsigned char *) addr; +} +static inline __attribute__((__always_inline__)) +unsigned short readw(const volatile void *addr) +{ + return *(volatile unsigned short *) addr; +} +static inline __attribute__((__always_inline__)) +unsigned int readl(const volatile void *addr) +{ + return *(volatile unsigned int *) addr; +} + +static inline __attribute__((__always_inline__)) +void writeb(unsigned char b, volatile void *addr) +{ + *(volatile unsigned char *) addr = b; +} +static inline __attribute__((__always_inline__)) +void writew(unsigned short b, volatile void *addr) +{ + *(volatile unsigned short *) addr = b; +} +static inline __attribute__((__always_inline__)) +void writel(unsigned int b, volatile void *addr) +{ + *(volatile unsigned int *) addr = b; +} + +#endif /* ARCH_I386_IO_H */ diff --git a/purgatory/arch/i386/pic.c b/purgatory/arch/i386/pic.c new file mode 100644 index 0000000..c23c459 --- /dev/null +++ b/purgatory/arch/i386/pic.c @@ -0,0 +1,51 @@ +/* + * kexec: Linux boots Linux + * + * Copyright (C) 2003,2004 Eric Biederman (ebiederm@xmission.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation (version 2 of the License). + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include <sys/io.h> +#include <purgatory.h> +#include "purgatory-x86.h" + + +void x86_setup_legacy_pic(void) +{ + /* Load the legacy dos settings into the 8259A pic */ + outb(0xff, 0x21); /* mask all of 8259A-1 */ + outb(0xff, 0xa1); /* mask all of 8259A-2 */ + + outb(0x11, 0x20); /* ICW1: select 8259A-1 init */ + outb(0x11, 0x80); /* A short delay */ + + outb(0x08, 0x21); /* ICW2: 8259A-1 IR0-7 mappend to 0x8-0xf */ + outb(0x08, 0x80); /* A short delay */ + + outb(0x01, 0x21); /* Normal 8086 auto EOI mode */ + outb(0x01, 0x80); /* A short delay */ + + outb(0x11, 0xa0); /* ICW1: select 8259A-2 init */ + outb(0x11, 0x80); /* A short delay */ + + outb(0x70, 0xa1); /* ICW2: 8259A-2 IR0-7 mappend to 0x70-0x77 */ + outb(0x70, 0x80); /* A short delay */ + + outb(0x01, 0xa1); /* Normal 8086 auto EOI mode */ + outb(0x01, 0x80); /* A short delay */ + + outb(0x00, 0x21); /* Unmask all of 8259A-1 */ + outb(0x00, 0xa1); /* Unmask all of 8259A-2 */ +} + diff --git a/purgatory/arch/i386/purgatory-x86.c b/purgatory/arch/i386/purgatory-x86.c new file mode 100644 index 0000000..075e518 --- /dev/null +++ b/purgatory/arch/i386/purgatory-x86.c @@ -0,0 +1,57 @@ +#include <stdint.h> +#include <purgatory.h> +#include "purgatory-x86.h" + +/* + * CPU + * ============================================================================= + */ + +void x86_setup_cpu(void) +{ +#if 0 + /* This code is only needed for old versions of the kexec kernel patch. + * While it is still a good idea doing this unconditionally breaks + * on older cpus that did not implemented cr4. + * So this code is disabled for now. If this is revisited + * I first need to detect cpuid support and then use cpuid + * to conditionally change newer cpu registers. + */ + /* clear special bits in %cr4 */ + asm volatile( + "movl %0, %%eax\n\t" + "movl %%eax, %%cr4\n\t" + : /* outputs */ + : "r" (0) + ); +#endif +} + +uint8_t reset_vga = 0; +uint8_t legacy_timer = 0; +uint8_t legacy_pic = 0; +uint8_t panic_kernel = 0; +unsigned long jump_back_entry = 0; +char *cmdline_end = 0; + +void setup_arch(void) +{ + x86_setup_cpu(); + if (reset_vga) x86_reset_vga(); + if (legacy_pic) x86_setup_legacy_pic(); + /* if (legacy_timer) x86_setup_legacy_timer(); */ +} + +static void x86_setup_jump_back_entry(void) +{ + if (cmdline_end) + sprintf(cmdline_end, " kexec_jump_back_entry=0x%x", + jump_back_entry); +} + +/* This function can be used to execute after the SHA256 verification. */ +void post_verification_setup_arch(void) +{ + if (panic_kernel) crashdump_backup_memory(); + if (jump_back_entry) x86_setup_jump_back_entry(); +} diff --git a/purgatory/arch/i386/purgatory-x86.h b/purgatory/arch/i386/purgatory-x86.h new file mode 100644 index 0000000..02039c9 --- /dev/null +++ b/purgatory/arch/i386/purgatory-x86.h @@ -0,0 +1,9 @@ +#ifndef PURGATORY_X86_H +#define PURGATORY_X86_H + +void x86_reset_vga(void); +void x86_setup_legacy_pic(void); +void x86_setup_legacy_timer(void); +void crashdump_backup_memory(void); + +#endif /* PURGATORY_X86_H */ diff --git a/purgatory/arch/i386/setup-x86.S b/purgatory/arch/i386/setup-x86.S new file mode 100644 index 0000000..201bb2c --- /dev/null +++ b/purgatory/arch/i386/setup-x86.S @@ -0,0 +1,75 @@ +/* + * purgatory: setup code + * + * Copyright (C) 2003,2004 Eric Biederman (ebiederm@xmission.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation (version 2 of the License). + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + +#undef i386 + + .text + .arch i386 + .globl purgatory_start +purgatory_start: + .code32 + + /* Load a gdt so I know what the segment registers are */ + lgdt %cs:gdt + + /* load the data segments */ + movl $0x08, %eax /* data segment */ + movl %eax, %ds + movl %eax, %es + movl %eax, %ss + movl %eax, %fs + movl %eax, %gs + + /* load the code segment */ + ljmp $0x10,$1f +1: + + movl 0(%esp), %eax + movl %eax, jump_back_entry + + /* Setup a stack */ + movl $lstack_end, %esp + + /* Call the C code */ + call purgatory + jmp entry32 + + .section ".rodata" + .balign 16 +gdt: + /* 0x00 unusable segment so used as the gdt ptr */ + .word gdt_end - gdt - 1 + .long gdt + .word 0 + + /* 0x8 4GB flat data segment */ + .word 0xFFFF, 0x0000, 0x9200, 0x00CF + + /* 0x10 4GB flat code segment */ + .word 0xFFFF, 0x0000, 0x9A00, 0x00CF +gdt_end: + + /* A stack for the purgatory code */ + .bss + .balign 4096 +lstack: + .skip 4096 +lstack_end: + diff --git a/purgatory/arch/i386/stack.S b/purgatory/arch/i386/stack.S new file mode 100644 index 0000000..a597b0f --- /dev/null +++ b/purgatory/arch/i386/stack.S @@ -0,0 +1,39 @@ +/* + * purgatory: stack + * + * Copyright (C) 2003,2004 Eric Biederman (ebiederm@xmission.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation (version 2 of the License). + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + /* A stack for the loaded kernel. + * Seperate and in the data section so it can be prepopulated. + */ + .data + .globl stack, stack_end + .globl stack_arg32_1, stack_arg32_2, stack_arg32_3 ,stack_arg32_4 + .globl stack_arg32_5, stack_arg32_6, stack_arg32_7 ,stack_arg32_8 + .balign 4096 +stack: + .skip 4096 - (8*4) +stack_arg32_8: .long 0 ; .size stack_arg32_8, 4 +stack_arg32_7: .long 0 ; .size stack_arg32_7, 4 +stack_arg32_6: .long 0 ; .size stack_arg32_6, 4 +stack_arg32_5: .long 0 ; .size stack_arg32_5, 4 +stack_arg32_4: .long 0 ; .size stack_arg32_4, 4 +stack_arg32_3: .long 0 ; .size stack_arg32_3, 4 +stack_arg32_2: .long 0 ; .size stack_arg32_2, 4 +stack_arg32_1: .long 0 ; .size stack_arg32_1, 4 +stack_end: + diff --git a/purgatory/arch/i386/vga.c b/purgatory/arch/i386/vga.c new file mode 100644 index 0000000..e65976c --- /dev/null +++ b/purgatory/arch/i386/vga.c @@ -0,0 +1,152 @@ +#include <sys/io.h> +#include <purgatory.h> +#include "purgatory-x86.h" + +/* Crudely reset a VGA card to text mode 3, by writing plausible default */ +/* values into its registers. */ +/* Tim Deegan (tjd21 at cl.cam.ac.uk), March 2003 */ +/* Based on Keir Fraser's start-of-day reset code from the Xen hypervisor */ +/* Converted from Assembly 20 December 2004 -- Eric Biederman */ + +void x86_reset_vga(void) +{ + /* Hello */ + inb(0x3da); + outb(0, 0x3c0); + + /* Sequencer registers */ + outw(0x0300, 0x3c4); + outw(0x0001, 0x3c4); + outw(0x0302, 0x3c4); + outw(0x0003, 0x3c4); + outw(0x0204, 0x3c4); + + /* Ensure CRTC regs 0-7 are unlocked by clearing bit 7 of CRTC[17] */ + outw(0x0e11, 0x3d4); + /* CRTC registers */ + outw(0x5f00, 0x3d4); + outw(0x4f01, 0x3d4); + outw(0x5002, 0x3d4); + outw(0x8203, 0x3d4); + outw(0x5504, 0x3d4); + outw(0x8105, 0x3d4); + outw(0xbf06, 0x3d4); + outw(0x1f07, 0x3d4); + outw(0x0008, 0x3d4); + outw(0x4f09, 0x3d4); + outw(0x200a, 0x3d4); + outw(0x0e0b, 0x3d4); + outw(0x000c, 0x3d4); + outw(0x000d, 0x3d4); + outw(0x010e, 0x3d4); + outw(0xe00f, 0x3d4); + outw(0x9c10, 0x3d4); + outw(0x8e11, 0x3d4); + outw(0x8f12, 0x3d4); + outw(0x2813, 0x3d4); + outw(0x1f14, 0x3d4); + outw(0x9615, 0x3d4); + outw(0xb916, 0x3d4); + outw(0xa317, 0x3d4); + outw(0xff18, 0x3d4); + + /* Graphic registers */ + outw(0x0000, 0x3ce); + outw(0x0001, 0x3ce); + outw(0x0002, 0x3ce); + outw(0x0003, 0x3ce); + outw(0x0004, 0x3ce); + outw(0x1005, 0x3ce); + outw(0x0e06, 0x3ce); + outw(0x0007, 0x3ce); + outw(0xff08, 0x3ce); + + /* Attribute registers */ + inb(0x3da); + outb(0x00, 0x3c0); + outb(0x00, 0x3c0); + + inb(0x3da); + outb(0x01, 0x3c0); + outb(0x01, 0x3c0); + + inb(0x3da); + outb(0x02, 0x3c0); + outb(0x02, 0x3c0); + + inb(0x3da); + outb(0x03, 0x3c0); + outb(0x03, 0x3c0); + + inb(0x3da); + outb(0x04, 0x3c0); + outb(0x04, 0x3c0); + + inb(0x3da); + outb(0x05, 0x3c0); + outb(0x05, 0x3c0); + + inb(0x3da); + outb(0x06, 0x3c0); + outb(0x14, 0x3c0); + + inb(0x3da); + outb(0x07, 0x3c0); + outb(0x07, 0x3c0); + + inb(0x3da); + outb(0x08, 0x3c0); + outb(0x38, 0x3c0); + + inb(0x3da); + outb(0x09, 0x3c0); + outb(0x39, 0x3c0); + + inb(0x3da); + outb(0x0a, 0x3c0); + outb(0x3a, 0x3c0); + + inb(0x3da); + outb(0x0b, 0x3c0); + outb(0x3b, 0x3c0); + + inb(0x3da); + outb(0x0c, 0x3c0); + outb(0x3c, 0x3c0); + + inb(0x3da); + outb(0x0d, 0x3c0); + outb(0x3d, 0x3c0); + + inb(0x3da); + outb(0x0e, 0x3c0); + outb(0x3e, 0x3c0); + + inb(0x3da); + outb(0x0f, 0x3c0); + outb(0x3f, 0x3c0); + + inb(0x3da); + outb(0x10, 0x3c0); + outb(0x0c, 0x3c0); + + inb(0x3da); + outb(0x11, 0x3c0); + outb(0x00, 0x3c0); + + inb(0x3da); + outb(0x12, 0x3c0); + outb(0x0f, 0x3c0); + + inb(0x3da); + outb(0x13, 0x3c0); + outb(0x08, 0x3c0); + + inb(0x3da); + outb(0x14, 0x3c0); + outb(0x00, 0x3c0); + + /* Goodbye */ + inb(0x3da); + outb(0x20, 0x3c0); +} diff --git a/purgatory/arch/ia64/Makefile b/purgatory/arch/ia64/Makefile new file mode 100644 index 0000000..4a2564c --- /dev/null +++ b/purgatory/arch/ia64/Makefile @@ -0,0 +1,17 @@ +# +# Purgatory ia64 +# +ia64_PURGATORY_SRCS += purgatory/arch/ia64/entry.S +ia64_PURGATORY_SRCS += purgatory/arch/ia64/purgatory-ia64.c +ia64_PURGATORY_SRCS += purgatory/arch/ia64/console-ia64.c +ia64_PURGATORY_SRCS += purgatory/arch/ia64/vga.c + +ia64_PURGATORY_EXTRA_CFLAGS = -ffixed-r28 + +# sha256.c needs to be compiled without optimization, else +# purgatory fails to execute on ia64. +ia64_PURGATORY_SHA256_CFLAGS = -O0 + +dist += purgatory/arch/ia64/Makefile $(ia64_PURGATORY_SRCS) \ + purgatory/arch/ia64/io.h purgatory/arch/ia64/purgatory-ia64.h + diff --git a/purgatory/arch/ia64/console-ia64.c b/purgatory/arch/ia64/console-ia64.c new file mode 100644 index 0000000..99d97ca --- /dev/null +++ b/purgatory/arch/ia64/console-ia64.c @@ -0,0 +1,47 @@ +#include <purgatory.h> +#include "io.h" + +#define VGABASE UNCACHED(0xb8000) + +/* code based on i386 console code + * TODO add serial support + */ +#define MAX_YPOS 25 +#define MAX_XPOS 80 + +unsigned long current_ypos = 1, current_xpos = 0; + +static void putchar_vga(int ch) +{ + int i, k, j; + + if (current_ypos >= MAX_YPOS) { + /* scroll 1 line up */ + for (k = 1, j = 0; k < MAX_YPOS; k++, j++) { + for (i = 0; i < MAX_XPOS; i++) { + writew(readw(VGABASE + 2*(MAX_XPOS*k + i)), + VGABASE + 2*(MAX_XPOS*j + i)); + } + } + for (i = 0; i < MAX_XPOS; i++) + writew(0x720, VGABASE + 2*(MAX_XPOS*j + i)); + current_ypos = MAX_YPOS-1; + } + if (ch == '\n') { + current_xpos = 0; + current_ypos++; + } else if (ch != '\r') { + writew(((0x7 << 8) | (unsigned short) ch), + VGABASE + 2*(MAX_XPOS*current_ypos + + current_xpos++)); + if (current_xpos >= MAX_XPOS) { + current_xpos = 0; + current_ypos++; + } + } +} + +void putchar(int ch) +{ + putchar_vga(ch); +} diff --git a/purgatory/arch/ia64/entry.S b/purgatory/arch/ia64/entry.S new file mode 100644 index 0000000..f05434f --- /dev/null +++ b/purgatory/arch/ia64/entry.S @@ -0,0 +1,73 @@ +/* + * purgatory: setup code + * + * Copyright (C) 2005-2006 Zou Nan hai (nanhai.zou@intel.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation (version 2 of the License). + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#define DECLARE_DATA8(name) \ +.global name; \ +.size name, 8; \ +name: data8 0x0 + +.global __dummy_efi_function +.align 32 +.proc __dummy_efi_function +__dummy_efi_function: + mov r8=r0;; + br.ret.sptk.many rp;; +.global __dummy_efi_function_end +__dummy_efi_function_end: +.endp __dummy_efi_function + +.global purgatory_start +.align 32 +.proc purgatory_start +purgatory_start: + movl r2=__gp_value;; + ld8 gp=[r2];; + br.call.sptk.many b0=purgatory + ;; + alloc r2 = ar.pfs, 0, 0, 2, 0 + ;; + mov out0=r28 + movl out1=__vmcode_base; + br.call.sptk.many b0=ia64_env_setup + movl r10=__kernel_entry;; + ld8 r14=[r10];; + movl r10=__boot_param_base;; + ld8 r28=[r10];; + mov b6=r14;; + mov ar.lc=r0 + mov ar.ec=r0 + cover;; + invala;; + br.call.sptk.many b0=b6 +.endp purgatory_start + +DECLARE_DATA8(__kernel_entry) +DECLARE_DATA8(__vmcode_base) +DECLARE_DATA8(__vmcode_size) +DECLARE_DATA8(__ramdisk_base) +DECLARE_DATA8(__ramdisk_size) +DECLARE_DATA8(__command_line) +DECLARE_DATA8(__command_line_len) +DECLARE_DATA8(__efi_memmap_base) +DECLARE_DATA8(__efi_memmap_size) +DECLARE_DATA8(__boot_param_base) +DECLARE_DATA8(__loaded_segments) +DECLARE_DATA8(__loaded_segments_num) + +DECLARE_DATA8(__gp_value) +DECLARE_DATA8(__noio) diff --git a/purgatory/arch/ia64/io.h b/purgatory/arch/ia64/io.h new file mode 100644 index 0000000..0f78580 --- /dev/null +++ b/purgatory/arch/ia64/io.h @@ -0,0 +1,108 @@ +#ifndef IO_H +#define IO_H +#define UNCACHED(x) (void *)((x)|(1UL<<63)) +#define MF() asm volatile ("mf.a" ::: "memory") +#define IO_SPACE_ENCODING(p) ((((p) >> 2) << 12) | (p & 0xfff)) +extern long __noio; +static inline void *io_addr (unsigned long port) +{ + unsigned long offset; + unsigned long io_base; + asm volatile ("mov %0=ar.k0":"=r"(io_base)); + offset = IO_SPACE_ENCODING(port); + return UNCACHED(io_base | offset); +} + +static inline unsigned int inb (unsigned long port) +{ + volatile unsigned char *addr = io_addr(port); + unsigned char ret = 0; + if (!__noio) { + ret = *addr; + MF(); + } + return ret; +} + +static inline unsigned int inw (unsigned long port) +{ + volatile unsigned short *addr = io_addr(port); + unsigned short ret = 0; + + if (!__noio) { + ret = *addr; + MF(); + } + return ret; +} + +static inline unsigned int inl (unsigned long port) +{ + volatile unsigned int *addr = io_addr(port); + unsigned int ret ; + if (!__noio) { + ret = *addr; + MF(); + } + return ret; +} + +static inline void outb (unsigned char val, unsigned long port) +{ + volatile unsigned char *addr = io_addr(port); + + if (!__noio) { + *addr = val; + MF(); + } +} + +static inline void outw (unsigned short val, unsigned long port) +{ + volatile unsigned short *addr = io_addr(port); + + if (!__noio) { + *addr = val; + MF(); + } +} + +static inline void outl (unsigned int val, unsigned long port) +{ + volatile unsigned int *addr = io_addr(port); + + if (!__noio) { + *addr = val; + MF(); + } +} + +static inline unsigned char readb(const volatile void *addr) +{ + return __noio ? 0 :*(volatile unsigned char *) addr; +} +static inline unsigned short readw(const volatile void *addr) +{ + return __noio ? 0 :*(volatile unsigned short *) addr; +} +static inline unsigned int readl(const volatile void *addr) +{ + return __noio ? 0 :*(volatile unsigned int *) addr; +} + +static inline void writeb(unsigned char b, volatile void *addr) +{ + if (!__noio) + *(volatile unsigned char *) addr = b; +} +static inline void writew(unsigned short b, volatile void *addr) +{ + if (!__noio) + *(volatile unsigned short *) addr = b; +} +static inline void writel(unsigned int b, volatile void *addr) +{ + if (!__noio) + *(volatile unsigned int *) addr = b; +} +#endif diff --git a/purgatory/arch/ia64/purgatory-ia64.c b/purgatory/arch/ia64/purgatory-ia64.c new file mode 100644 index 0000000..e138e9c --- /dev/null +++ b/purgatory/arch/ia64/purgatory-ia64.c @@ -0,0 +1,307 @@ +/* + * purgatory: setup code + * + * Copyright (C) 2005-2006 Zou Nan hai (nanhai.zou@intel.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation (version 2 of the License). + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include <purgatory.h> +#include <stdint.h> +#include <string.h> +#include "purgatory-ia64.h" + +#define PAGE_OFFSET 0xe000000000000000UL + +#define EFI_PAGE_SHIFT 12 +#define EFI_PAGE_SIZE (1UL<<EFI_PAGE_SHIFT) +#define EFI_PAGE_ALIGN(x) ((x + EFI_PAGE_SIZE - 1)&~(EFI_PAGE_SIZE-1)) +/* Memory types: */ +#define EFI_RESERVED_TYPE 0 +#define EFI_LOADER_CODE 1 +#define EFI_LOADER_DATA 2 +#define EFI_BOOT_SERVICES_CODE 3 +#define EFI_BOOT_SERVICES_DATA 4 +#define EFI_RUNTIME_SERVICES_CODE 5 +#define EFI_RUNTIME_SERVICES_DATA 6 +#define EFI_CONVENTIONAL_MEMORY 7 +#define EFI_UNUSABLE_MEMORY 8 +#define EFI_ACPI_RECLAIM_MEMORY 9 +#define EFI_ACPI_MEMORY_NVS 10 +#define EFI_MEMORY_MAPPED_IO 11 +#define EFI_MEMORY_MAPPED_IO_PORT_SPACE 12 +#define EFI_PAL_CODE 13 +#define EFI_MAX_MEMORY_TYPE 14 + +typedef struct { + uint64_t signature; + uint32_t revision; + uint32_t headersize; + uint32_t crc32; + uint32_t reserved; +} efi_table_hdr_t; + +typedef struct { + efi_table_hdr_t hdr; + unsigned long get_time; + unsigned long set_time; + unsigned long get_wakeup_time; + unsigned long set_wakeup_time; + unsigned long set_virtual_address_map; + unsigned long convert_pointer; + unsigned long get_variable; + unsigned long get_next_variable; + unsigned long set_variable; + unsigned long get_next_high_mono_count; + unsigned long reset_system; +} efi_runtime_services_t; + +typedef struct { + efi_table_hdr_t hdr; + unsigned long fw_vendor; /* physical addr of + CHAR16 vendor string */ + uint32_t fw_revision; + unsigned long con_in_handle; + unsigned long con_in; + unsigned long con_out_handle; + unsigned long con_out; + unsigned long stderr_handle; + unsigned long stderr; + unsigned long runtime; + unsigned long boottime; + unsigned long nr_tables; + unsigned long tables; +} efi_system_table_t; + +struct ia64_boot_param { + uint64_t command_line; /* physical address of + command linearguments */ + uint64_t efi_systab; /* physical address of + EFI system table */ + uint64_t efi_memmap; /* physical address of + EFI memory map */ + uint64_t efi_memmap_size; /* size of EFI memory map */ + uint64_t efi_memdesc_size; /* size of an EFI memory map + descriptor */ + uint32_t efi_memdesc_version; /* memory descriptor version */ + struct { + uint16_t num_cols; /* number of columns on console + output device */ + uint16_t num_rows; /* number of rows on console + output device */ + uint16_t orig_x; /* cursor's x position */ + uint16_t orig_y; /* cursor's y position */ + } console_info; + uint64_t fpswa; /* physical address of + the fpswa interface */ + uint64_t initrd_start; + uint64_t initrd_size; + + uint64_t vmcode_start; + uint64_t vmcode_size; +}; + +typedef struct { + uint32_t type; + uint32_t pad; + uint64_t phys_addr; + uint64_t virt_addr; + uint64_t num_pages; + uint64_t attribute; +} efi_memory_desc_t; + +struct loaded_segment { + unsigned long start; + unsigned long end; +}; + +struct kexec_boot_params { + uint64_t vmcode_base; + uint64_t vmcode_size; + uint64_t ramdisk_base; + uint64_t ramdisk_size; + uint64_t command_line; + uint64_t command_line_len; + uint64_t efi_memmap_base; + uint64_t efi_memmap_size; + uint64_t boot_param_base; + struct loaded_segment *loaded_segments; + unsigned long loaded_segments_num; +}; + +void +setup_arch(void) +{ + reset_vga(); +} + +inline unsigned long PA(unsigned long addr) +{ + return addr & 0x0fffffffffffffffLL; +} + +void +patch_efi_memmap(struct kexec_boot_params *params, + struct ia64_boot_param *boot_param) +{ + void *dest = (void *)params->efi_memmap_base; + void *src = (void *)boot_param->efi_memmap; + uint64_t orig_type; + efi_memory_desc_t *src_md, *dst_md; + void *src_end = src + boot_param->efi_memmap_size; + unsigned long i; + for (; src < src_end; src += boot_param->efi_memdesc_size, + dest += boot_param->efi_memdesc_size) { + unsigned long mstart, mend; + src_md = src; + dst_md = dest; + if (src_md->num_pages == 0) + continue; + mstart = src_md->phys_addr; + mend = src_md->phys_addr + + (src_md->num_pages << EFI_PAGE_SHIFT); + *dst_md = *src_md; + if (src_md->type == EFI_LOADER_DATA) + dst_md->type = EFI_CONVENTIONAL_MEMORY; + /* segments are already sorted and aligned to 4K */ + orig_type = dst_md->type; + for (i = 0; i < params->loaded_segments_num; i++) { + struct loaded_segment *seg; + unsigned long start_pages, mid_pages, end_pages; + + seg = ¶ms->loaded_segments[i]; + if (seg->start < mstart || seg->start >= mend) + continue; + + while (seg->end > mend && src < src_end) { + src += boot_param->efi_memdesc_size; + src_md = src; + /* TODO check contig and attribute here */ + mend = src_md->phys_addr + + (src_md->num_pages << EFI_PAGE_SHIFT); + } + if (seg->end < mend && src < src_end) { + void *src_next; + efi_memory_desc_t *src_next_md; + src_next = src + boot_param->efi_memdesc_size; + src_next_md = src_next; + if (src_next_md->type == + EFI_CONVENTIONAL_MEMORY) { + /* TODO check contig and attribute */ + src += boot_param->efi_memdesc_size; + src_md = src; + mend = src_md->phys_addr + + (src_md->num_pages << + EFI_PAGE_SHIFT); + } + + } + start_pages = (seg->start - mstart) >> EFI_PAGE_SHIFT; + mid_pages = (seg->end - seg->start) >> EFI_PAGE_SHIFT; + end_pages = (mend - seg->end) >> EFI_PAGE_SHIFT; + if (start_pages) { + dst_md->num_pages = start_pages; + dest += boot_param->efi_memdesc_size; + dst_md = dest; + *dst_md = *src_md; + } + dst_md->phys_addr = seg->start; + dst_md->num_pages = mid_pages; + dst_md->type = EFI_LOADER_DATA; + if (!end_pages) + break; + dest += boot_param->efi_memdesc_size; + dst_md = dest; + *dst_md = *src_md; + dst_md->phys_addr = seg->end; + dst_md->num_pages = end_pages; + dst_md->type = orig_type; + mstart = seg->end; + } + } + + boot_param->efi_memmap_size = dest - (void *)params->efi_memmap_base; +} + +void +flush_icache_range(char *start, unsigned long len) +{ + unsigned long i, addr; + addr = (unsigned long)start & ~31UL; + len += (unsigned long)start - addr; + for (i = 0;i < len; i += 32) + asm volatile("fc.i %0"::"r"(start + i):"memory"); + asm volatile (";;sync.i;;":::"memory"); + asm volatile ("srlz.i":::"memory"); +} + +extern char __dummy_efi_function[], __dummy_efi_function_end[]; + + +void +ia64_env_setup(struct ia64_boot_param *boot_param, + struct kexec_boot_params *params) +{ + unsigned long len; + efi_system_table_t *systab; + efi_runtime_services_t *runtime; + unsigned long *set_virtual_address_map; + char *command_line = (char *)params->command_line; + uint64_t command_line_len = params->command_line_len; + struct ia64_boot_param *new_boot_param = + (struct ia64_boot_param *) params->boot_param_base; + memcpy(new_boot_param, boot_param, 4096); + + /* + * patch efi_runtime->set_virtual_address_map to a dummy function + * + * The EFI specification mandates that set_virtual_address_map only + * takes effect the first time that it is called, and that + * subsequent calls will return error. By replacing it with a + * dummy function the new OS can think it is calling it again + * without either the OS or any buggy EFI implementations getting + * upset. + * + * Note: as the EFI specification says that set_virtual_address_map + * will only take affect the first time it is called, the mapping + * can't be updated, and thus mapping of the old and new OS really + * needs to be the same. + */ + len = __dummy_efi_function_end - __dummy_efi_function; + memcpy(command_line + command_line_len, + __dummy_efi_function, len); + systab = (efi_system_table_t *)new_boot_param->efi_systab; + runtime = (efi_runtime_services_t *)PA(systab->runtime); + set_virtual_address_map = + (unsigned long *)PA(runtime->set_virtual_address_map); + *(set_virtual_address_map) = + (unsigned long)(command_line + command_line_len); + flush_icache_range(command_line + command_line_len, len); + + patch_efi_memmap(params, new_boot_param); + + new_boot_param->efi_memmap = params->efi_memmap_base; + new_boot_param->command_line = params->command_line; + new_boot_param->console_info.orig_x = 0; + new_boot_param->console_info.orig_y = 0; + new_boot_param->initrd_start = params->ramdisk_base; + new_boot_param->initrd_size = params->ramdisk_size; + new_boot_param->vmcode_start = params->vmcode_base; + new_boot_param->vmcode_size = params->vmcode_size; +} + +/* This function can be used to execute after the SHA256 verification. */ +void post_verification_setup_arch(void) +{ + /* Nothing for now */ +} diff --git a/purgatory/arch/ia64/purgatory-ia64.h b/purgatory/arch/ia64/purgatory-ia64.h new file mode 100644 index 0000000..8fd3af1 --- /dev/null +++ b/purgatory/arch/ia64/purgatory-ia64.h @@ -0,0 +1,5 @@ +#ifndef PURGATORY_IA64_H +#define PURGATORY_IA64_H + +void reset_vga(void); +#endif /* PURGATORY_IA64_H */ diff --git a/purgatory/arch/ia64/vga.c b/purgatory/arch/ia64/vga.c new file mode 100644 index 0000000..dceadb7 --- /dev/null +++ b/purgatory/arch/ia64/vga.c @@ -0,0 +1,143 @@ +#include "io.h" +void reset_vga(void) +{ + /* Hello */ + inb(0x3da); + outb(0, 0x3c0); + + /* Sequencer registers */ + outw(0x0300, 0x3c4); + outw(0x0001, 0x3c4); + outw(0x0302, 0x3c4); + outw(0x0003, 0x3c4); + outw(0x0204, 0x3c4); + + /* Ensure CRTC regs 0-7 are unlocked by clearing bit 7 of CRTC[17] */ + outw(0x0e11, 0x3d4); + /* CRTC registers */ + outw(0x5f00, 0x3d4); + outw(0x4f01, 0x3d4); + outw(0x5002, 0x3d4); + outw(0x8203, 0x3d4); + outw(0x5504, 0x3d4); + outw(0x8105, 0x3d4); + outw(0xbf06, 0x3d4); + outw(0x1f07, 0x3d4); + outw(0x0008, 0x3d4); + outw(0x4f09, 0x3d4); + outw(0x200a, 0x3d4); + outw(0x0e0b, 0x3d4); + outw(0x000c, 0x3d4); + outw(0x000d, 0x3d4); + outw(0x010e, 0x3d4); + outw(0xe00f, 0x3d4); + outw(0x9c10, 0x3d4); + outw(0x8e11, 0x3d4); + outw(0x8f12, 0x3d4); + outw(0x2813, 0x3d4); + outw(0x1f14, 0x3d4); + outw(0x9615, 0x3d4); + outw(0xb916, 0x3d4); + outw(0xa317, 0x3d4); + outw(0xff18, 0x3d4); + + /* Graphic registers */ + outw(0x0000, 0x3ce); + outw(0x0001, 0x3ce); + outw(0x0002, 0x3ce); + outw(0x0003, 0x3ce); + outw(0x0004, 0x3ce); + outw(0x1005, 0x3ce); + outw(0x0e06, 0x3ce); + outw(0x0007, 0x3ce); + outw(0xff08, 0x3ce); + + /* Attribute registers */ + inb(0x3da); + outb(0x00, 0x3c0); + outb(0x00, 0x3c0); + + inb(0x3da); + outb(0x01, 0x3c0); + outb(0x01, 0x3c0); + + inb(0x3da); + outb(0x02, 0x3c0); + outb(0x02, 0x3c0); + + inb(0x3da); + outb(0x03, 0x3c0); + outb(0x03, 0x3c0); + + inb(0x3da); + outb(0x04, 0x3c0); + outb(0x04, 0x3c0); + + inb(0x3da); + outb(0x05, 0x3c0); + outb(0x05, 0x3c0); + + inb(0x3da); + outb(0x06, 0x3c0); + outb(0x14, 0x3c0); + + inb(0x3da); + outb(0x07, 0x3c0); + outb(0x07, 0x3c0); + + inb(0x3da); + outb(0x08, 0x3c0); + outb(0x38, 0x3c0); + + inb(0x3da); + outb(0x09, 0x3c0); + outb(0x39, 0x3c0); + + inb(0x3da); + outb(0x0a, 0x3c0); + outb(0x3a, 0x3c0); + + inb(0x3da); + outb(0x0b, 0x3c0); + outb(0x3b, 0x3c0); + + inb(0x3da); + outb(0x0c, 0x3c0); + outb(0x3c, 0x3c0); + + inb(0x3da); + outb(0x0d, 0x3c0); + outb(0x3d, 0x3c0); + + inb(0x3da); + outb(0x0e, 0x3c0); + outb(0x3e, 0x3c0); + + inb(0x3da); + outb(0x0f, 0x3c0); + outb(0x3f, 0x3c0); + + inb(0x3da); + outb(0x10, 0x3c0); + outb(0x0c, 0x3c0); + + inb(0x3da); + outb(0x11, 0x3c0); + outb(0x00, 0x3c0); + + inb(0x3da); + outb(0x12, 0x3c0); + outb(0x0f, 0x3c0); + + inb(0x3da); + outb(0x13, 0x3c0); + outb(0x08, 0x3c0); + + inb(0x3da); + outb(0x14, 0x3c0); + outb(0x00, 0x3c0); + + /* Goodbye */ + inb(0x3da); + outb(0x20, 0x3c0); +} diff --git a/purgatory/arch/loongarch/Makefile b/purgatory/arch/loongarch/Makefile new file mode 100644 index 0000000..b0c47b2 --- /dev/null +++ b/purgatory/arch/loongarch/Makefile @@ -0,0 +1,10 @@ +# +# Purgatory loongarch +# + +loongarch_PURGATORY_SRCS+= purgatory/arch/loongarch/purgatory-loongarch.c +loongarch_PURGATORY_SRCS+= purgatory/arch/loongarch/console-loongarch.c + +dist += purgatory/arch/loongarch/Makefile $(loongarch_PURGATORY_SRCS) \ + purgatory/arch/loongarch/purgatory-loongarch.h + diff --git a/purgatory/arch/loongarch/console-loongarch.c b/purgatory/arch/loongarch/console-loongarch.c new file mode 100644 index 0000000..af34ecf --- /dev/null +++ b/purgatory/arch/loongarch/console-loongarch.c @@ -0,0 +1,7 @@ +#include <purgatory.h> +#include "unused.h" + +void putchar(int UNUSED(ch)) +{ + /* Nothing for now */ +} diff --git a/purgatory/arch/loongarch/purgatory-loongarch.c b/purgatory/arch/loongarch/purgatory-loongarch.c new file mode 100644 index 0000000..abe9297 --- /dev/null +++ b/purgatory/arch/loongarch/purgatory-loongarch.c @@ -0,0 +1,7 @@ +#include <purgatory.h> +#include "purgatory-loongarch.h" + +void setup_arch(void) +{ + /* Nothing for now */ +} diff --git a/purgatory/arch/loongarch/purgatory-loongarch.h b/purgatory/arch/loongarch/purgatory-loongarch.h new file mode 100644 index 0000000..cd1ab97 --- /dev/null +++ b/purgatory/arch/loongarch/purgatory-loongarch.h @@ -0,0 +1,6 @@ +#ifndef PURGATORY_LOONGARCH_H +#define PURGATORY_LOONGARCH_H + +/* nothing yet */ + +#endif /* PURGATORY_LOONGARCH_H */ diff --git a/purgatory/arch/mips/Makefile b/purgatory/arch/mips/Makefile new file mode 100644 index 0000000..87888f3 --- /dev/null +++ b/purgatory/arch/mips/Makefile @@ -0,0 +1,10 @@ +# +# Purgatory mips +# + +mips_PURGATORY_SRCS+= purgatory/arch/mips/purgatory-mips.c +mips_PURGATORY_SRCS+= purgatory/arch/mips/console-mips.c + +dist += purgatory/arch/mips/Makefile $(mips_PURGATORY_SRCS) \ + purgatory/arch/mips/purgatory-mips.h + diff --git a/purgatory/arch/mips/console-mips.c b/purgatory/arch/mips/console-mips.c new file mode 100644 index 0000000..af34ecf --- /dev/null +++ b/purgatory/arch/mips/console-mips.c @@ -0,0 +1,7 @@ +#include <purgatory.h> +#include "unused.h" + +void putchar(int UNUSED(ch)) +{ + /* Nothing for now */ +} diff --git a/purgatory/arch/mips/purgatory-mips.c b/purgatory/arch/mips/purgatory-mips.c new file mode 100644 index 0000000..cb25bf7 --- /dev/null +++ b/purgatory/arch/mips/purgatory-mips.c @@ -0,0 +1,7 @@ +#include <purgatory.h> +#include "purgatory-mips.h" + +void setup_arch(void) +{ + /* Nothing for now */ +} diff --git a/purgatory/arch/mips/purgatory-mips.h b/purgatory/arch/mips/purgatory-mips.h new file mode 100644 index 0000000..c57b402 --- /dev/null +++ b/purgatory/arch/mips/purgatory-mips.h @@ -0,0 +1,6 @@ +#ifndef PURGATORY_MIPSEL_H +#define PURGATORY_MIPSEL_H + +/* nothing yet */ + +#endif /* PURGATORY_MIPSEL_H */ diff --git a/purgatory/arch/ppc/Makefile b/purgatory/arch/ppc/Makefile new file mode 100644 index 0000000..c85c58a --- /dev/null +++ b/purgatory/arch/ppc/Makefile @@ -0,0 +1,14 @@ +# +# Purgatory ppc +# + +ppc_PURGATORY_SRCS += purgatory/arch/ppc/v2wrap_32.S +ppc_PURGATORY_SRCS += purgatory/arch/ppc/misc.S +ppc_PURGATORY_SRCS += purgatory/arch/ppc/purgatory-ppc.c +ppc_PURGATORY_SRCS += purgatory/arch/ppc/console-ppc.c + +ppc_PURGATORY_EXTRA_CFLAGS += -msoft-float + +dist += purgatory/arch/ppc/Makefile $(ppc_PURGATORY_SRCS) \ + purgatory/arch/ppc/purgatory-ppc.h purgatory/arch/ppc/ppc_asm.h + diff --git a/purgatory/arch/ppc/console-ppc.c b/purgatory/arch/ppc/console-ppc.c new file mode 100644 index 0000000..af34ecf --- /dev/null +++ b/purgatory/arch/ppc/console-ppc.c @@ -0,0 +1,7 @@ +#include <purgatory.h> +#include "unused.h" + +void putchar(int UNUSED(ch)) +{ + /* Nothing for now */ +} diff --git a/purgatory/arch/ppc/misc.S b/purgatory/arch/ppc/misc.S new file mode 100644 index 0000000..bc6a18d --- /dev/null +++ b/purgatory/arch/ppc/misc.S @@ -0,0 +1,301 @@ +/* + * This file contains miscellaneous low-level functions. + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Largely rewritten by Cort Dougan (cort@cs.nmt.edu) + * and Paul Mackerras. + * + * Rewrittten to work with /sbin/kexec 20 December 2004 Eric Biederman + * + * 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. + * + */ + +#include "ppc_asm.h" + +/* This is from linux-2.6/arch/powerpc/lib/crtsavres.S: + * + * Special support for eabi and SVR4 + * + * Copyright (C) 1995, 1996, 1998, 2000, 2001 Free Software Foundation, Inc. + * Copyright 2008 Freescale Semiconductor, Inc. + * Written By Michael Meissner + * + * Based on gcc/config/rs6000/crtsavres.asm from gcc + * + * This file 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, or (at your option) any + * later version. + * + * In addition to the permissions in the GNU General Public License, the + * Free Software Foundation gives you unlimited permission to link the + * compiled version of this file with other programs, and to distribute + * those programs without any restriction coming from the use of this + * file. (The General Public License restrictions do apply in other + * respects; for example, they cover modification of the file, and + * distribution when not linked into another program.) + * + * This file 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; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + * As a special exception, if you link this library with files + * compiled with GCC to produce an executable, this does not cause + * the resulting executable to be covered by the GNU General Public License. + * This exception does not however invalidate any other reasons why + * the executable file might be covered by the GNU General Public License. + */ +#include "config.h" + +#define _GLOBAL(name) \ + .type name,@function; \ + .globl name; \ +name: + +/* Routines for saving integer registers, called by the compiler. */ +/* Called with r11 pointing to the stack header word of the caller of the */ +/* function, just beyond the end of the integer save area. */ + +_GLOBAL(_savegpr_14) +_GLOBAL(_save32gpr_14) + stw 14,-72(11) /* save gp registers */ +_GLOBAL(_savegpr_15) +_GLOBAL(_save32gpr_15) + stw 15,-68(11) +_GLOBAL(_savegpr_16) +_GLOBAL(_save32gpr_16) + stw 16,-64(11) +_GLOBAL(_savegpr_17) +_GLOBAL(_save32gpr_17) + stw 17,-60(11) +_GLOBAL(_savegpr_18) +_GLOBAL(_save32gpr_18) + stw 18,-56(11) +_GLOBAL(_savegpr_19) +_GLOBAL(_save32gpr_19) + stw 19,-52(11) +_GLOBAL(_savegpr_20) +_GLOBAL(_save32gpr_20) + stw 20,-48(11) +_GLOBAL(_savegpr_21) +_GLOBAL(_save32gpr_21) + stw 21,-44(11) +_GLOBAL(_savegpr_22) +_GLOBAL(_save32gpr_22) + stw 22,-40(11) +_GLOBAL(_savegpr_23) +_GLOBAL(_save32gpr_23) + stw 23,-36(11) +_GLOBAL(_savegpr_24) +_GLOBAL(_save32gpr_24) + stw 24,-32(11) +_GLOBAL(_savegpr_25) +_GLOBAL(_save32gpr_25) + stw 25,-28(11) +_GLOBAL(_savegpr_26) +_GLOBAL(_save32gpr_26) + stw 26,-24(11) +_GLOBAL(_savegpr_27) +_GLOBAL(_save32gpr_27) + stw 27,-20(11) +_GLOBAL(_savegpr_28) +_GLOBAL(_save32gpr_28) + stw 28,-16(11) +_GLOBAL(_savegpr_29) +_GLOBAL(_save32gpr_29) + stw 29,-12(11) +_GLOBAL(_savegpr_30) +_GLOBAL(_save32gpr_30) + stw 30,-8(11) +_GLOBAL(_savegpr_31) +_GLOBAL(_save32gpr_31) + stw 31,-4(11) + blr + +/* Routines for restoring integer registers, called by the compiler. */ +/* Called with r11 pointing to the stack header word of the caller of the */ +/* function, just beyond the end of the integer restore area. */ + +_GLOBAL(_restgpr_14) +_GLOBAL(_rest32gpr_14) + lwz 14,-72(11) /* restore gp registers */ +_GLOBAL(_restgpr_15) +_GLOBAL(_rest32gpr_15) + lwz 15,-68(11) +_GLOBAL(_restgpr_16) +_GLOBAL(_rest32gpr_16) + lwz 16,-64(11) +_GLOBAL(_restgpr_17) +_GLOBAL(_rest32gpr_17) + lwz 17,-60(11) +_GLOBAL(_restgpr_18) +_GLOBAL(_rest32gpr_18) + lwz 18,-56(11) +_GLOBAL(_restgpr_19) +_GLOBAL(_rest32gpr_19) + lwz 19,-52(11) +_GLOBAL(_restgpr_20) +_GLOBAL(_rest32gpr_20) + lwz 20,-48(11) +_GLOBAL(_restgpr_21) +_GLOBAL(_rest32gpr_21) + lwz 21,-44(11) +_GLOBAL(_restgpr_22) +_GLOBAL(_rest32gpr_22) + lwz 22,-40(11) +_GLOBAL(_restgpr_23) +_GLOBAL(_rest32gpr_23) + lwz 23,-36(11) +_GLOBAL(_restgpr_24) +_GLOBAL(_rest32gpr_24) + lwz 24,-32(11) +_GLOBAL(_restgpr_25) +_GLOBAL(_rest32gpr_25) + lwz 25,-28(11) +_GLOBAL(_restgpr_26) +_GLOBAL(_rest32gpr_26) + lwz 26,-24(11) +_GLOBAL(_restgpr_27) +_GLOBAL(_rest32gpr_27) + lwz 27,-20(11) +_GLOBAL(_restgpr_28) +_GLOBAL(_rest32gpr_28) + lwz 28,-16(11) +_GLOBAL(_restgpr_29) +_GLOBAL(_rest32gpr_29) + lwz 29,-12(11) +_GLOBAL(_restgpr_30) +_GLOBAL(_rest32gpr_30) + lwz 30,-8(11) +_GLOBAL(_restgpr_31) +_GLOBAL(_rest32gpr_31) + lwz 31,-4(11) + blr + +/* Routines for restoring integer registers, called by the compiler. */ +/* Called with r11 pointing to the stack header word of the caller of the */ +/* function, just beyond the end of the integer restore area. */ + +_GLOBAL(_restgpr_14_x) +_GLOBAL(_rest32gpr_14_x) + lwz 14,-72(11) /* restore gp registers */ +_GLOBAL(_restgpr_15_x) +_GLOBAL(_rest32gpr_15_x) + lwz 15,-68(11) +_GLOBAL(_restgpr_16_x) +_GLOBAL(_rest32gpr_16_x) + lwz 16,-64(11) +_GLOBAL(_restgpr_17_x) +_GLOBAL(_rest32gpr_17_x) + lwz 17,-60(11) +_GLOBAL(_restgpr_18_x) +_GLOBAL(_rest32gpr_18_x) + lwz 18,-56(11) +_GLOBAL(_restgpr_19_x) +_GLOBAL(_rest32gpr_19_x) + lwz 19,-52(11) +_GLOBAL(_restgpr_20_x) +_GLOBAL(_rest32gpr_20_x) + lwz 20,-48(11) +_GLOBAL(_restgpr_21_x) +_GLOBAL(_rest32gpr_21_x) + lwz 21,-44(11) +_GLOBAL(_restgpr_22_x) +_GLOBAL(_rest32gpr_22_x) + lwz 22,-40(11) +_GLOBAL(_restgpr_23_x) +_GLOBAL(_rest32gpr_23_x) + lwz 23,-36(11) +_GLOBAL(_restgpr_24_x) +_GLOBAL(_rest32gpr_24_x) + lwz 24,-32(11) +_GLOBAL(_restgpr_25_x) +_GLOBAL(_rest32gpr_25_x) + lwz 25,-28(11) +_GLOBAL(_restgpr_26_x) +_GLOBAL(_rest32gpr_26_x) + lwz 26,-24(11) +_GLOBAL(_restgpr_27_x) +_GLOBAL(_rest32gpr_27_x) + lwz 27,-20(11) +_GLOBAL(_restgpr_28_x) +_GLOBAL(_rest32gpr_28_x) + lwz 28,-16(11) +_GLOBAL(_restgpr_29_x) +_GLOBAL(_rest32gpr_29_x) + lwz 29,-12(11) +_GLOBAL(_restgpr_30_x) +_GLOBAL(_rest32gpr_30_x) + lwz 30,-8(11) +_GLOBAL(_restgpr_31_x) +_GLOBAL(_rest32gpr_31_x) + lwz 0,4(11) + lwz 31,-4(11) + mtlr 0 + mr 1,11 + blr + + .text + +/* + * Extended precision shifts. + * + * Updated to be valid for shift counts from 0 to 63 inclusive. + * -- Gabriel + * + * R3/R4 has 64 bit value + * R5 has shift count + * result in R3/R4 + * + * ashrdi3: arithmetic right shift (sign propagation) + * lshrdi3: logical right shift + * ashldi3: left shift + */ + .globl __ashrdi3 +__ashrdi3: + subfic r6,r5,32 + srw r4,r4,r5 # LSW = count > 31 ? 0 : LSW >> count + addi r7,r5,32 # could be xori, or addi with -32 + slw r6,r3,r6 # t1 = count > 31 ? 0 : MSW << (32-count) + rlwinm r8,r7,0,32 # t3 = (count < 32) ? 32 : 0 + sraw r7,r3,r7 # t2 = MSW >> (count-32) + or r4,r4,r6 # LSW |= t1 + slw r7,r7,r8 # t2 = (count < 32) ? 0 : t2 + sraw r3,r3,r5 # MSW = MSW >> count + or r4,r4,r7 # LSW |= t2 + blr + + .globl __ashldi3 +__ashldi3: + subfic r6,r5,32 + slw r3,r3,r5 # MSW = count > 31 ? 0 : MSW << count + addi r7,r5,32 # could be xori, or addi with -32 + srw r6,r4,r6 # t1 = count > 31 ? 0 : LSW >> (32-count) + slw r7,r4,r7 # t2 = count < 32 ? 0 : LSW << (count-32) + or r3,r3,r6 # MSW |= t1 + slw r4,r4,r5 # LSW = LSW << count + or r3,r3,r7 # MSW |= t2 + blr + + .globl __lshrdi3 +__lshrdi3: + subfic r6,r5,32 + srw r4,r4,r5 # LSW = count > 31 ? 0 : LSW >> count + addi r7,r5,32 # could be xori, or addi with -32 + slw r6,r3,r6 # t1 = count > 31 ? 0 : MSW << (32-count) + srw r7,r3,r7 # t2 = count < 32 ? 0 : MSW >> (count-32) + or r4,r4,r6 # LSW |= t1 + srw r3,r3,r5 # MSW = MSW >> count + or r4,r4,r7 # LSW |= t2 + blr + diff --git a/purgatory/arch/ppc/ppc_asm.h b/purgatory/arch/ppc/ppc_asm.h new file mode 100644 index 0000000..36503a9 --- /dev/null +++ b/purgatory/arch/ppc/ppc_asm.h @@ -0,0 +1,506 @@ +/* + * ppc_asm.h - mainly bits stolen from Linux kernel asm/reg.h and asm/ppc_asm.h + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +/* Condition Register Bit Fields */ + +#define cr0 0 +#define cr1 1 +#define cr2 2 +#define cr3 3 +#define cr4 4 +#define cr5 5 +#define cr6 6 +#define cr7 7 + + +/* General Purpose Registers (GPRs) */ + +#define r0 0 +#define r1 1 +#define r2 2 +#define r3 3 +#define r4 4 +#define r5 5 +#define r6 6 +#define r7 7 +#define r8 8 +#define r9 9 +#define r10 10 +#define r11 11 +#define r12 12 +#define r13 13 +#define r14 14 +#define r15 15 +#define r16 16 +#define r17 17 +#define r18 18 +#define r19 19 +#define r20 20 +#define r21 21 +#define r22 22 +#define r23 23 +#define r24 24 +#define r25 25 +#define r26 26 +#define r27 27 +#define r28 28 +#define r29 29 +#define r30 30 +#define r31 31 + +/* Machine State Register (MSR) Fields */ +#define MSR_SF (1<<63) +#define MSR_ISF (1<<61) +#define MSR_VEC (1<<25) /* Enable AltiVec */ +#define MSR_POW (1<<18) /* Enable Power Management */ +#define MSR_WE (1<<18) /* Wait State Enable */ +#define MSR_TGPR (1<<17) /* TLB Update registers in use */ +#define MSR_CE (1<<17) /* Critical Interrupt Enable */ +#define MSR_ILE (1<<16) /* Interrupt Little Endian */ +#define MSR_EE (1<<15) /* External Interrupt Enable */ +#define MSR_PR (1<<14) /* Problem State / Privilege Level */ +#define MSR_FP (1<<13) /* Floating Point enable */ +#define MSR_ME (1<<12) /* Machine Check Enable */ +#define MSR_FE0 (1<<11) /* Floating Exception mode 0 */ +#define MSR_SE (1<<10) /* Single Step */ +#define MSR_BE (1<<9) /* Branch Trace */ +#define MSR_DE (1<<9) /* Debug Exception Enable */ +#define MSR_FE1 (1<<8) /* Floating Exception mode 1 */ +#define MSR_IP (1<<6) /* Exception prefix 0x000/0xFFF */ +#define MSR_IR (1<<5) /* Instruction Relocate */ +#define MSR_DR (1<<4) /* Data Relocate */ +#define MSR_PE (1<<3) /* Protection Enable */ +#define MSR_PX (1<<2) /* Protection Exclusive Mode */ +#define MSR_RI (1<<1) /* Recoverable Exception */ +#define MSR_LE (1<<0) /* Little Endian */ + +/* Special Purpose Registers (SPRNs)*/ +#define SPRN_CTR 0x009 /* Count Register */ +#define SPRN_DABR 0x3F5 /* Data Address Breakpoint Register */ +#define SPRN_DAR 0x013 /* Data Address Register */ +#define SPRN_TBRL 0x10C /* Time Base Read Lower Register (user, R/O) */ +#define SPRN_TBRU 0x10D /* Time Base Read Upper Register (user, R/O) */ +#define SPRN_TBWL 0x11C /* Time Base Lower Register (super, R/W) */ +#define SPRN_TBWU 0x11D /* Time Base Upper Register (super, R/W) */ +#define SPRN_HIOR 0x137 /* 970 Hypervisor interrupt offset */ +#define SPRN_DBAT0L 0x219 /* Data BAT 0 Lower Register */ +#define SPRN_DBAT0U 0x218 /* Data BAT 0 Upper Register */ +#define SPRN_DBAT1L 0x21B /* Data BAT 1 Lower Register */ +#define SPRN_DBAT1U 0x21A /* Data BAT 1 Upper Register */ +#define SPRN_DBAT2L 0x21D /* Data BAT 2 Lower Register */ +#define SPRN_DBAT2U 0x21C /* Data BAT 2 Upper Register */ +#define SPRN_DBAT3L 0x21F /* Data BAT 3 Lower Register */ +#define SPRN_DBAT3U 0x21E /* Data BAT 3 Upper Register */ +#define SPRN_DBAT4L 0x239 /* Data BAT 4 Lower Register */ +#define SPRN_DBAT4U 0x238 /* Data BAT 4 Upper Register */ +#define SPRN_DBAT5L 0x23B /* Data BAT 5 Lower Register */ +#define SPRN_DBAT5U 0x23A /* Data BAT 5 Upper Register */ +#define SPRN_DBAT6L 0x23D /* Data BAT 6 Lower Register */ +#define SPRN_DBAT6U 0x23C /* Data BAT 6 Upper Register */ +#define SPRN_DBAT7L 0x23F /* Data BAT 7 Lower Register */ +#define SPRN_DBAT7U 0x23E /* Data BAT 7 Upper Register */ + +#define SPRN_DEC 0x016 /* Decrement Register */ +#define SPRN_DER 0x095 /* Debug Enable Regsiter */ +#define DER_RSTE 0x40000000 /* Reset Interrupt */ +#define DER_CHSTPE 0x20000000 /* Check Stop */ +#define DER_MCIE 0x10000000 /* Machine Check Interrupt */ +#define DER_EXTIE 0x02000000 /* External Interrupt */ +#define DER_ALIE 0x01000000 /* Alignment Interrupt */ +#define DER_PRIE 0x00800000 /* Program Interrupt */ +#define DER_FPUVIE 0x00400000 /* FP Unavailable Interrupt */ +#define DER_DECIE 0x00200000 /* Decrementer Interrupt */ +#define DER_SYSIE 0x00040000 /* System Call Interrupt */ +#define DER_TRE 0x00020000 /* Trace Interrupt */ +#define DER_SEIE 0x00004000 /* FP SW Emulation Interrupt */ +#define DER_ITLBMSE 0x00002000 /* Imp. Spec. Instruction TLB Miss */ +#define DER_ITLBERE 0x00001000 /* Imp. Spec. Instruction TLB Error */ +#define DER_DTLBMSE 0x00000800 /* Imp. Spec. Data TLB Miss */ +#define DER_DTLBERE 0x00000400 /* Imp. Spec. Data TLB Error */ +#define DER_LBRKE 0x00000008 /* Load/Store Breakpoint Interrupt */ +#define DER_IBRKE 0x00000004 /* Instruction Breakpoint Interrupt */ +#define DER_EBRKE 0x00000002 /* External Breakpoint Interrupt */ +#define DER_DPIE 0x00000001 /* Dev. Port Nonmaskable Request */ +#define SPRN_DMISS 0x3D0 /* Data TLB Miss Register */ +#define SPRN_DSISR 0x012 /* Data Storage Interrupt Status Register */ +#define SPRN_EAR 0x11A /* External Address Register */ +#define SPRN_HASH1 0x3D2 /* Primary Hash Address Register */ +#define SPRN_HASH2 0x3D3 /* Secondary Hash Address Resgister */ +#define SPRN_HID0 0x3F0 /* Hardware Implementation Register 0 */ +#define HID0_EMCP (1<<31) /* Enable Machine Check pin */ +#define HID0_EBA (1<<29) /* Enable Bus Address Parity */ +#define HID0_EBD (1<<28) /* Enable Bus Data Parity */ +#define HID0_SBCLK (1<<27) +#define HID0_EICE (1<<26) +#define HID0_TBEN (1<<26) /* Timebase enable - 745x */ +#define HID0_ECLK (1<<25) +#define HID0_PAR (1<<24) +#define HID0_STEN (1<<24) /* Software table search enable - 745x */ +#define HID0_HIGH_BAT (1<<23) /* Enable high BATs - 7455 */ +#define HID0_DOZE (1<<23) +#define HID0_NAP (1<<22) +#define HID0_SLEEP (1<<21) +#define HID0_DPM (1<<20) +#define HID0_BHTCLR (1<<18) /* Clear branch history table - 7450 */ +#define HID0_XAEN (1<<17) /* Extended addressing enable - 7450 */ +#define HID0_NHR (1<<16) /* Not hard reset (software bit-7450)*/ +#define HID0_ICE (1<<15) /* Instruction Cache Enable */ +#define HID0_DCE (1<<14) /* Data Cache Enable */ +#define HID0_ILOCK (1<<13) /* Instruction Cache Lock */ +#define HID0_DLOCK (1<<12) /* Data Cache Lock */ +#define HID0_ICFI (1<<11) /* Instr. Cache Flash Invalidate */ +#define HID0_DCI (1<<10) /* Data Cache Invalidate */ +#define HID0_SPD (1<<9) /* Speculative disable */ +#define HID0_SGE (1<<7) /* Store Gathering Enable */ +#define HID0_SIED (1<<7) /* Serial Instr. Execution [Disable] */ +#define HID0_DFCA (1<<6) /* Data Cache Flush Assist */ +#define HID0_LRSTK (1<<4) /* Link register stack - 745x */ +#define HID0_BTIC (1<<5) /* Branch Target Instr Cache Enable */ +#define HID0_ABE (1<<3) /* Address Broadcast Enable */ +#define HID0_FOLD (1<<3) /* Branch Folding enable - 745x */ +#define HID0_BHTE (1<<2) /* Branch History Table Enable */ +#define HID0_BTCD (1<<1) /* Branch target cache disable */ +#define HID0_NOPDST (1<<1) /* No-op dst, dstt, etc. instr. */ +#define HID0_NOPTI (1<<0) /* No-op dcbt and dcbst instr. */ + +#define SPRN_HID1 0x3F1 /* Hardware Implementation Register 1 */ +#define HID1_EMCP (1<<31) /* 7450 Machine Check Pin Enable */ +#define HID1_PC0 (1<<16) /* 7450 PLL_CFG[0] */ +#define HID1_PC1 (1<<15) /* 7450 PLL_CFG[1] */ +#define HID1_PC2 (1<<14) /* 7450 PLL_CFG[2] */ +#define HID1_PC3 (1<<13) /* 7450 PLL_CFG[3] */ +#define HID1_SYNCBE (1<<11) /* 7450 ABE for sync, eieio */ +#define HID1_ABE (1<<10) /* 7450 Address Broadcast Enable */ +#define SPRN_HID2 0x3F8 /* Hardware Implementation Register 2 */ +#define SPRN_IABR 0x3F2 /* Instruction Address Breakpoint Register */ +#define SPRN_HID4 0x3F4 /* 970 HID4 */ +#define SPRN_HID5 0x3F6 /* 970 HID5 */ +#if !defined(SPRN_IAC1) && !defined(SPRN_IAC2) +#define SPRN_IAC1 0x3F4 /* Instruction Address Compare 1 */ +#define SPRN_IAC2 0x3F5 /* Instruction Address Compare 2 */ +#endif +#define SPRN_IBAT0L 0x211 /* Instruction BAT 0 Lower Register */ +#define SPRN_IBAT0U 0x210 /* Instruction BAT 0 Upper Register */ +#define SPRN_IBAT1L 0x213 /* Instruction BAT 1 Lower Register */ +#define SPRN_IBAT1U 0x212 /* Instruction BAT 1 Upper Register */ +#define SPRN_IBAT2L 0x215 /* Instruction BAT 2 Lower Register */ +#define SPRN_IBAT2U 0x214 /* Instruction BAT 2 Upper Register */ +#define SPRN_IBAT3L 0x217 /* Instruction BAT 3 Lower Register */ +#define SPRN_IBAT3U 0x216 /* Instruction BAT 3 Upper Register */ +#define SPRN_IBAT4L 0x231 /* Instruction BAT 4 Lower Register */ +#define SPRN_IBAT4U 0x230 /* Instruction BAT 4 Upper Register */ +#define SPRN_IBAT5L 0x233 /* Instruction BAT 5 Lower Register */ +#define SPRN_IBAT5U 0x232 /* Instruction BAT 5 Upper Register */ +#define SPRN_IBAT6L 0x235 /* Instruction BAT 6 Lower Register */ +#define SPRN_IBAT6U 0x234 /* Instruction BAT 6 Upper Register */ +#define SPRN_IBAT7L 0x237 /* Instruction BAT 7 Lower Register */ +#define SPRN_IBAT7U 0x236 /* Instruction BAT 7 Upper Register */ +#define SPRN_ICMP 0x3D5 /* Instruction TLB Compare Register */ +#define SPRN_ICTC 0x3FB /* Instruction Cache Throttling Control Reg */ +#define SPRN_ICTRL 0x3F3 /* 1011 7450 icache and interrupt ctrl */ +#define ICTRL_EICE 0x08000000 /* enable icache parity errs */ +#define ICTRL_EDC 0x04000000 /* enable dcache parity errs */ +#define ICTRL_EICP 0x00000100 /* enable icache par. check */ +#define SPRN_IMISS 0x3D4 /* Instruction TLB Miss Register */ +#define SPRN_IMMR 0x27E /* Internal Memory Map Register */ +#define SPRN_L2CR 0x3F9 /* Level 2 Cache Control Regsiter */ +#define SPRN_L2CR2 0x3f8 +#define L2CR_L2E 0x80000000 /* L2 enable */ +#define L2CR_L2PE 0x40000000 /* L2 parity enable */ +#define L2CR_L2SIZ_MASK 0x30000000 /* L2 size mask */ +#define L2CR_L2SIZ_256KB 0x10000000 /* L2 size 256KB */ +#define L2CR_L2SIZ_512KB 0x20000000 /* L2 size 512KB */ +#define L2CR_L2SIZ_1MB 0x30000000 /* L2 size 1MB */ +#define L2CR_L2CLK_MASK 0x0e000000 /* L2 clock mask */ +#define L2CR_L2CLK_DISABLED 0x00000000 /* L2 clock disabled */ +#define L2CR_L2CLK_DIV1 0x02000000 /* L2 clock / 1 */ +#define L2CR_L2CLK_DIV1_5 0x04000000 /* L2 clock / 1.5 */ +#define L2CR_L2CLK_DIV2 0x08000000 /* L2 clock / 2 */ +#define L2CR_L2CLK_DIV2_5 0x0a000000 /* L2 clock / 2.5 */ +#define L2CR_L2CLK_DIV3 0x0c000000 /* L2 clock / 3 */ +#define L2CR_L2RAM_MASK 0x01800000 /* L2 RAM type mask */ +#define L2CR_L2RAM_FLOW 0x00000000 /* L2 RAM flow through */ +#define L2CR_L2RAM_PIPE 0x01000000 /* L2 RAM pipelined */ +#define L2CR_L2RAM_PIPE_LW 0x01800000 /* L2 RAM pipelined latewr */ +#define L2CR_L2DO 0x00400000 /* L2 data only */ +#define L2CR_L2I 0x00200000 /* L2 global invalidate */ +#define L2CR_L2CTL 0x00100000 /* L2 RAM control */ +#define L2CR_L2WT 0x00080000 /* L2 write-through */ +#define L2CR_L2TS 0x00040000 /* L2 test support */ +#define L2CR_L2OH_MASK 0x00030000 /* L2 output hold mask */ +#define L2CR_L2OH_0_5 0x00000000 /* L2 output hold 0.5 ns */ +#define L2CR_L2OH_1_0 0x00010000 /* L2 output hold 1.0 ns */ +#define L2CR_L2SL 0x00008000 /* L2 DLL slow */ +#define L2CR_L2DF 0x00004000 /* L2 differential clock */ +#define L2CR_L2BYP 0x00002000 /* L2 DLL bypass */ +#define L2CR_L2IP 0x00000001 /* L2 GI in progress */ +#define SPRN_L3CR 0x3FA /* Level 3 Cache Control Regsiter */ +#define L3CR_L3E 0x80000000 /* L3 enable */ +#define L3CR_L3PE 0x40000000 /* L3 data parity enable */ +#define L3CR_L3APE 0x20000000 /* L3 addr parity enable */ +#define L3CR_L3SIZ 0x10000000 /* L3 size */ +#define L3CR_L3CLKEN 0x08000000 /* L3 clock enable */ +#define L3CR_L3RES 0x04000000 /* L3 special reserved bit */ +#define L3CR_L3CLKDIV 0x03800000 /* L3 clock divisor */ +#define L3CR_L3IO 0x00400000 /* L3 instruction only */ +#define L3CR_L3SPO 0x00040000 /* L3 sample point override */ +#define L3CR_L3CKSP 0x00030000 /* L3 clock sample point */ +#define L3CR_L3PSP 0x0000e000 /* L3 P-clock sample point */ +#define L3CR_L3REP 0x00001000 /* L3 replacement algorithm */ +#define L3CR_L3HWF 0x00000800 /* L3 hardware flush */ +#define L3CR_L3I 0x00000400 /* L3 global invalidate */ +#define L3CR_L3RT 0x00000300 /* L3 SRAM type */ +#define L3CR_L3NIRCA 0x00000080 /* L3 non-integer ratio clock adj. */ +#define L3CR_L3DO 0x00000040 /* L3 data only mode */ +#define L3CR_PMEN 0x00000004 /* L3 private memory enable */ +#define L3CR_PMSIZ 0x00000001 /* L3 private memory size */ +#define SPRN_MSSCR0 0x3f6 /* Memory Subsystem Control Register 0 */ +#define SPRN_MSSSR0 0x3f7 /* Memory Subsystem Status Register 1 */ +#define SPRN_LDSTCR 0x3f8 /* Load/Store control register */ +#define SPRN_LDSTDB 0x3f4 /* */ +#define SPRN_LR 0x008 /* Link Register */ +#define SPRN_MMCR0 0x3B8 /* Monitor Mode Control Register 0 */ +#define SPRN_MMCR1 0x3BC /* Monitor Mode Control Register 1 */ +#ifndef SPRN_PIR +#define SPRN_PIR 0x3FF /* Processor Identification Register */ +#endif +#define SPRN_PMC1 0x3B9 /* Performance Counter Register 1 */ +#define SPRN_PMC2 0x3BA /* Performance Counter Register 2 */ +#define SPRN_PMC3 0x3BD /* Performance Counter Register 3 */ +#define SPRN_PMC4 0x3BE /* Performance Counter Register 4 */ +#define SPRN_PTEHI 0x3D5 /* 981 7450 PTE HI word (S/W TLB load) */ +#define SPRN_PTELO 0x3D6 /* 982 7450 PTE LO word (S/W TLB load) */ +#define SPRN_PVR 0x11F /* Processor Version Register */ +#define SPRN_RPA 0x3D6 /* Required Physical Address Register */ +#define SPRN_SDA 0x3BF /* Sampled Data Address Register */ +#define SPRN_SDR1 0x019 /* MMU Hash Base Register */ +#define SPRN_SIA 0x3BB /* Sampled Instruction Address Register */ +#define SPRN_SPRG0 0x110 /* Special Purpose Register General 0 */ +#define SPRN_SPRG1 0x111 /* Special Purpose Register General 1 */ +#define SPRN_SPRG2 0x112 /* Special Purpose Register General 2 */ +#define SPRN_SPRG3 0x113 /* Special Purpose Register General 3 */ +#define SPRN_SPRG4 0x114 /* Special Purpose Register General 4 */ +#define SPRN_SPRG5 0x115 /* Special Purpose Register General 5 */ +#define SPRN_SPRG6 0x116 /* Special Purpose Register General 6 */ +#define SPRN_SPRG7 0x117 /* Special Purpose Register General 7 */ +#define SPRN_SRR0 0x01A /* Save/Restore Register 0 */ +#define SPRN_SRR1 0x01B /* Save/Restore Register 1 */ +#define SPRN_THRM1 0x3FC /* Thermal Management Register 1 */ +/* these bits were defined in inverted endian sense originally, ugh, confusing */ +#define THRM1_TIN (1 << 31) +#define THRM1_TIV (1 << 30) +#define THRM1_THRES(x) ((x&0x7f)<<23) +#define THRM3_SITV(x) ((x&0x3fff)<<1) +#define THRM1_TID (1<<2) +#define THRM1_TIE (1<<1) +#define THRM1_V (1<<0) +#define SPRN_THRM2 0x3FD /* Thermal Management Register 2 */ +#define SPRN_THRM3 0x3FE /* Thermal Management Register 3 */ +#define THRM3_E (1<<0) +#define SPRN_TLBMISS 0x3D4 /* 980 7450 TLB Miss Register */ +#define SPRN_UMMCR0 0x3A8 /* User Monitor Mode Control Register 0 */ +#define SPRN_UMMCR1 0x3AC /* User Monitor Mode Control Register 0 */ +#define SPRN_UPMC1 0x3A9 /* User Performance Counter Register 1 */ +#define SPRN_UPMC2 0x3AA /* User Performance Counter Register 2 */ +#define SPRN_UPMC3 0x3AD /* User Performance Counter Register 3 */ +#define SPRN_UPMC4 0x3AE /* User Performance Counter Register 4 */ +#define SPRN_USIA 0x3AB /* User Sampled Instruction Address Register */ +#define SPRN_VRSAVE 0x100 /* Vector Register Save Register */ +#define SPRN_XER 0x001 /* Fixed Point Exception Register */ + +/* Bit definitions for MMCR0 and PMC1 / PMC2. */ +#define MMCR0_PMC1_CYCLES (1 << 7) +#define MMCR0_PMC1_ICACHEMISS (5 << 7) +#define MMCR0_PMC1_DTLB (6 << 7) +#define MMCR0_PMC2_DCACHEMISS 0x6 +#define MMCR0_PMC2_CYCLES 0x1 +#define MMCR0_PMC2_ITLB 0x7 +#define MMCR0_PMC2_LOADMISSTIME 0x5 + +/* Short-hand versions for a number of the above SPRNs */ +#define CTR SPRN_CTR /* Counter Register */ +#define DAR SPRN_DAR /* Data Address Register */ +#define DABR SPRN_DABR /* Data Address Breakpoint Register */ +#define DBAT0L SPRN_DBAT0L /* Data BAT 0 Lower Register */ +#define DBAT0U SPRN_DBAT0U /* Data BAT 0 Upper Register */ +#define DBAT1L SPRN_DBAT1L /* Data BAT 1 Lower Register */ +#define DBAT1U SPRN_DBAT1U /* Data BAT 1 Upper Register */ +#define DBAT2L SPRN_DBAT2L /* Data BAT 2 Lower Register */ +#define DBAT2U SPRN_DBAT2U /* Data BAT 2 Upper Register */ +#define DBAT3L SPRN_DBAT3L /* Data BAT 3 Lower Register */ +#define DBAT3U SPRN_DBAT3U /* Data BAT 3 Upper Register */ +#define DBAT4L SPRN_DBAT4L /* Data BAT 4 Lower Register */ +#define DBAT4U SPRN_DBAT4U /* Data BAT 4 Upper Register */ +#define DBAT5L SPRN_DBAT5L /* Data BAT 5 Lower Register */ +#define DBAT5U SPRN_DBAT5U /* Data BAT 5 Upper Register */ +#define DBAT6L SPRN_DBAT6L /* Data BAT 6 Lower Register */ +#define DBAT6U SPRN_DBAT6U /* Data BAT 6 Upper Register */ +#define DBAT7L SPRN_DBAT7L /* Data BAT 7 Lower Register */ +#define DBAT7U SPRN_DBAT7U /* Data BAT 7 Upper Register */ +#define DEC SPRN_DEC /* Decrement Register */ +#define DMISS SPRN_DMISS /* Data TLB Miss Register */ +#define DSISR SPRN_DSISR /* Data Storage Interrupt Status Register */ +#define EAR SPRN_EAR /* External Address Register */ +#define HASH1 SPRN_HASH1 /* Primary Hash Address Register */ +#define HASH2 SPRN_HASH2 /* Secondary Hash Address Register */ +#define HID0 SPRN_HID0 /* Hardware Implementation Register 0 */ +#define HID1 SPRN_HID1 /* Hardware Implementation Register 1 */ +#define IABR SPRN_IABR /* Instruction Address Breakpoint Register */ +#define IBAT0L SPRN_IBAT0L /* Instruction BAT 0 Lower Register */ +#define IBAT0U SPRN_IBAT0U /* Instruction BAT 0 Upper Register */ +#define IBAT1L SPRN_IBAT1L /* Instruction BAT 1 Lower Register */ +#define IBAT1U SPRN_IBAT1U /* Instruction BAT 1 Upper Register */ +#define IBAT2L SPRN_IBAT2L /* Instruction BAT 2 Lower Register */ +#define IBAT2U SPRN_IBAT2U /* Instruction BAT 2 Upper Register */ +#define IBAT3L SPRN_IBAT3L /* Instruction BAT 3 Lower Register */ +#define IBAT3U SPRN_IBAT3U /* Instruction BAT 3 Upper Register */ +#define IBAT4L SPRN_IBAT4L /* Instruction BAT 4 Lower Register */ +#define IBAT4U SPRN_IBAT4U /* Instruction BAT 4 Upper Register */ +#define IBAT5L SPRN_IBAT5L /* Instruction BAT 5 Lower Register */ +#define IBAT5U SPRN_IBAT5U /* Instruction BAT 5 Upper Register */ +#define IBAT6L SPRN_IBAT6L /* Instruction BAT 6 Lower Register */ +#define IBAT6U SPRN_IBAT6U /* Instruction BAT 6 Upper Register */ +#define IBAT7L SPRN_IBAT7L /* Instruction BAT 7 Lower Register */ +#define IBAT7U SPRN_IBAT7U /* Instruction BAT 7 Upper Register */ +#define ICMP SPRN_ICMP /* Instruction TLB Compare Register */ +#define IMISS SPRN_IMISS /* Instruction TLB Miss Register */ +#define IMMR SPRN_IMMR /* PPC 860/821 Internal Memory Map Register */ +#define L2CR SPRN_L2CR /* Classic PPC L2 cache control register */ +#define L3CR SPRN_L3CR /* PPC 745x L3 cache control register */ +#define LR SPRN_LR +#define PVR SPRN_PVR /* Processor Version */ +#define RPA SPRN_RPA /* Required Physical Address Register */ +#define SDR1 SPRN_SDR1 /* MMU hash base register */ +#define SPR0 SPRN_SPRG0 /* Supervisor Private Registers */ +#define SPR1 SPRN_SPRG1 +#define SPR2 SPRN_SPRG2 +#define SPR3 SPRN_SPRG3 +#define SPR4 SPRN_SPRG4 +#define SPR5 SPRN_SPRG5 +#define SPR6 SPRN_SPRG6 +#define SPR7 SPRN_SPRG7 +#define SPRG0 SPRN_SPRG0 +#define SPRG1 SPRN_SPRG1 +#define SPRG2 SPRN_SPRG2 +#define SPRG3 SPRN_SPRG3 +#define SPRG4 SPRN_SPRG4 +#define SPRG5 SPRN_SPRG5 +#define SPRG6 SPRN_SPRG6 +#define SPRG7 SPRN_SPRG7 +#define SRR0 SPRN_SRR0 /* Save and Restore Register 0 */ +#define SRR1 SPRN_SRR1 /* Save and Restore Register 1 */ +#define SRR2 SPRN_SRR2 /* Save and Restore Register 2 */ +#define SRR3 SPRN_SRR3 /* Save and Restore Register 3 */ +#define ICTC SPRN_ICTC /* Instruction Cache Throttling Control Reg */ +#define THRM1 SPRN_THRM1 /* Thermal Management Register 1 */ +#define THRM2 SPRN_THRM2 /* Thermal Management Register 2 */ +#define THRM3 SPRN_THRM3 /* Thermal Management Register 3 */ +#define XER SPRN_XER +#define TBRL SPRN_TBRL /* Time Base Read Lower Register */ +#define TBRU SPRN_TBRU /* Time Base Read Upper Register */ +#define TBWL SPRN_TBWL /* Time Base Write Lower Register */ +#define TBWU SPRN_TBWU /* Time Base Write Upper Register */ + +/* Processor Version Register */ + +/* Processor Version Register (PVR) field extraction */ + +#define PVR_VER(pvr) (((pvr) >> 16) & 0xFFFF) /* Version field */ +#define PVR_REV(pvr) (((pvr) >> 0) & 0xFFFF) /* Revison field */ + +/* + * IBM has further subdivided the standard PowerPC 16-bit version and + * revision subfields of the PVR for the PowerPC 403s into the following: + */ + +#define PVR_FAM(pvr) (((pvr) >> 20) & 0xFFF) /* Family field */ +#define PVR_MEM(pvr) (((pvr) >> 16) & 0xF) /* Member field */ +#define PVR_CORE(pvr) (((pvr) >> 12) & 0xF) /* Core field */ +#define PVR_CFG(pvr) (((pvr) >> 8) & 0xF) /* Configuration field */ +#define PVR_MAJ(pvr) (((pvr) >> 4) & 0xF) /* Major revision field */ +#define PVR_MIN(pvr) (((pvr) >> 0) & 0xF) /* Minor revision field */ + +/* Processor Version Numbers */ + +#define PVR_403GA 0x00200000 +#define PVR_403GB 0x00200100 +#define PVR_403GC 0x00200200 +#define PVR_403GCX 0x00201400 +#define PVR_405GP 0x40110000 +#define PVR_STB03XXX 0x40310000 +#define PVR_NP405H 0x41410000 +#define PVR_NP405L 0x41610000 +#define PVR_440GP_RB 0x40120440 +#define PVR_440GP_RC1 0x40120481 +#define PVR_440GP_RC2 0x40200481 +#define PVR_440GX_RA 0x51b21850 +#define PVR_440GX_RB 0x51b21851 +#define PVR_440GX_RB1 0x51b21852 +#define PVR_601 0x00010000 +#define PVR_602 0x00050000 +#define PVR_603 0x00030000 +#define PVR_603e 0x00060000 +#define PVR_603ev 0x00070000 +#define PVR_603r 0x00071000 +#define PVR_604 0x00040000 +#define PVR_604e 0x00090000 +#define PVR_604r 0x000A0000 +#define PVR_620 0x00140000 +#define PVR_740 0x00080000 +#define PVR_750 PVR_740 +#define PVR_740P 0x10080000 +#define PVR_750P PVR_740P +#define PVR_7400 0x000C0000 +#define PVR_7410 0x800C0000 +#define PVR_7450 0x80000000 +/* + * For the 8xx processors, all of them report the same PVR family for + * the PowerPC core. The various versions of these processors must be + * differentiated by the version number in the Communication Processor + * Module (CPM). + */ +#define PVR_821 0x00500000 +#define PVR_823 PVR_821 +#define PVR_850 PVR_821 +#define PVR_860 PVR_821 +#define PVR_8240 0x00810100 +#define PVR_8245 0x80811014 +#define PVR_8260 PVR_8240 + +/* Segment Registers */ +#define SR0 0 +#define SR1 1 +#define SR2 2 +#define SR3 3 +#define SR4 4 +#define SR5 5 +#define SR6 6 +#define SR7 7 +#define SR8 8 +#define SR9 9 +#define SR10 10 +#define SR11 11 +#define SR12 12 +#define SR13 13 +#define SR14 14 +#define SR15 15 + + +/* returns r3 = relocated address of sym */ +/* modifies r0 */ +#define RELOC_SYM(sym) \ + mflr r3; \ + bl 1f; \ +1: mflr r0; \ + mtlr r3; \ + lis r3, 1b@ha; \ + ori r3, r3, 1b@l; \ + subf r0, r3, r0; \ + lis r3, sym@ha; \ + ori r3, r3, sym@l; \ + add r3, r3, r0 + diff --git a/purgatory/arch/ppc/purgatory-ppc.c b/purgatory/arch/ppc/purgatory-ppc.c new file mode 100644 index 0000000..3df3767 --- /dev/null +++ b/purgatory/arch/ppc/purgatory-ppc.c @@ -0,0 +1,48 @@ +/* + * kexec: Linux boots Linux + * + * Created by: Mohan Kumar M (mohan@in.ibm.com) + * + * Copyright (C) IBM Corporation, 2005. All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation (version 2 of the License). + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <purgatory.h> +#include "purgatory-ppc.h" + +unsigned int panic_kernel = 0; +unsigned long backup_start = 0; +unsigned long stack = 0; +unsigned long dt_offset = 0; +unsigned long my_thread_ptr = 0; +unsigned long kernel = 0; + +void setup_arch(void) +{ + return; +} + +void post_verification_setup_arch(void) +{ +#ifndef CONFIG_BOOKE + if (panic_kernel) + crashdump_backup_memory(); +#endif +} + +void crashdump_backup_memory(void) +{ + return; +} diff --git a/purgatory/arch/ppc/purgatory-ppc.h b/purgatory/arch/ppc/purgatory-ppc.h new file mode 100644 index 0000000..7eff8aa --- /dev/null +++ b/purgatory/arch/ppc/purgatory-ppc.h @@ -0,0 +1,6 @@ +#ifndef PURGATORY_PPC_H +#define PURGATORY_PPC_H + +void crashdump_backup_memory(void); +void post_verification_setup_arch(void); +#endif /* PURGATORY_PPC_H */ diff --git a/purgatory/arch/ppc/v2wrap_32.S b/purgatory/arch/ppc/v2wrap_32.S new file mode 100644 index 0000000..8b60677 --- /dev/null +++ b/purgatory/arch/ppc/v2wrap_32.S @@ -0,0 +1,95 @@ +# +# kexec: Linux boots Linux +# +# Copyright (C) 2004 - 2005, Milton D Miller II, IBM Corporation +# Copyright (C) 2006, Mohan Kumar M (mohan@in.ibm.com), IBM Corporation +# +# 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 (version 2 of the License). +# +# 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., 675 Mass Ave, Cambridge, MA 02139, USA. +# + +# v2wrap.S +# a wrapper to call purgatory code to backup first +# 32kB of first kernel into the backup region +# reserved by kexec-tools. +# Invokes powerpc kernel with the expected arguments +# of kernel(device-tree, phys-offset, 0) + +# +# calling convention: +# r3 = physical number of this cpu (all cpus) +# r4 = address of this chunk (master only) +# master enters at purgatory_start (aka first byte of this chunk) +# slaves (additional cpus), if any, enter a copy of the +# first 0x100 bytes of this code relocated to 0x0 +# +# in other words, +# a copy of the first 0x100 bytes of this code is copied to 0 +# and the slaves are sent to address 0x60 +# with r3 = their physical cpu number. + + .globl purgatory_start +purgatory_start: b master + .org purgatory_start + 0x60 # ABI: slaves start at 60 with r3=phys +slave: b $ + .org purgatory_start + 0x100 # ABI: end of copied region + .size purgatory_start, . - purgatory_start + +# +# The above 0x100 bytes at purgatory_start are replaced with the +# code from the kernel (or next stage) by kexec/arch/powerpc/kexec-powerpc.c +# + +master: + or 1,1,1 # low priority to let other threads catchup + isync + mr 17,3 # save cpu id to r17 + mr 15,4 # save physical address in reg15 + + lis 6,my_thread_ptr@h + ori 6,6,my_thread_ptr@l + lwz 2,0(6) # setup ThreadPointer(TP) + + lis 6,stack@h + ori 6,6,stack@l + lwz 1,0(6) #setup stack + + subi 1,1,112 + bl purgatory + nop + + or 3,3,3 # ok now to high priority, lets boot + lis 6,0x1 + mtctr 6 # delay a bit for slaves to catch up +83: bdnz 83b # before we overwrite 0-100 again + + lis 6,dt_offset@h + ori 6,6,dt_offset@l + lwz 3,0(6) # load device-tree address + lwz 6,20(3) # fetch version number + cmpwi 0,6,2 # v2 ? + blt 80f + stw 17,28(3) # save my cpu number as boot_cpu_phys +80: + lis 6,kernel@h + ori 6,6,kernel@l + lwz 4,0(6) # load the kernel address + li 5,0 # r5 will be 0 for kernel + li 6,0 # clear r6 for good measure + mtctr 4 # prepare branch too + + lwz 8,0(4) # get the first instruction that we stole + stw 8,0(0) # and put it in the slave loop at 0 + # skip cache flush, do we care? + + bctr # start kernel diff --git a/purgatory/arch/ppc64/Makefile b/purgatory/arch/ppc64/Makefile new file mode 100644 index 0000000..8ca2719 --- /dev/null +++ b/purgatory/arch/ppc64/Makefile @@ -0,0 +1,28 @@ +# +# Purgatory ppc64 +# + +ppc64_PURGATORY_SRCS += purgatory/arch/ppc64/v2wrap.S +ppc64_PURGATORY_SRCS += purgatory/arch/ppc64/hvCall.S +ppc64_PURGATORY_SRCS += purgatory/arch/ppc64/purgatory-ppc64.c +ppc64_PURGATORY_SRCS += purgatory/arch/ppc64/console-ppc64.c +ppc64_PURGATORY_SRCS += purgatory/arch/ppc64/crashdump_backup.c +ppc64_PURGATORY_SRCS += purgatory/arch/ppc64/misc.S + +ppc64_PURGATORY_EXTRA_CFLAGS += -m64 -msoft-float \ + -fno-exceptions +ppc64_PURGATORY_EXTRA_ASFLAGS += -m64 +ifeq ($(SUBARCH),BE) + ppc64_PURGATORY_EXTRA_LDFLAGS += -melf64ppc + ppc64_PURGATORY_EXTRA_CFLAGS += -mcall-aixdesc + ppc64_PURGATORY_EXTRA_ASFLAGS += -mcall-aixdesc +else + ppc64_PURGATORY_EXTRA_LDFLAGS += -melf64lppc + ppc64_PURGATORY_EXTRA_CFLAGS += -mlittle-endian + ppc64_PURGATORY_EXTRA_ASFLAGS += -mlittle-endian +endif + +dist += purgatory/arch/ppc64/Makefile $(ppc64_PURGATORY_SRCS) \ + purgatory/arch/ppc64/hvCall.h \ + purgatory/arch/ppc64/ppc64_asm.h \ + purgatory/arch/ppc64/purgatory-ppc64.h diff --git a/purgatory/arch/ppc64/console-ppc64.c b/purgatory/arch/ppc64/console-ppc64.c new file mode 100644 index 0000000..2403312 --- /dev/null +++ b/purgatory/arch/ppc64/console-ppc64.c @@ -0,0 +1,45 @@ +/* + * kexec: Linux boots Linux + * + * Created by: Mohan Kumar M (mohan@in.ibm.com) + * + * Copyright (C) IBM Corporation, 2005. All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation (version 2 of the License). + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <purgatory.h> +#include "hvCall.h" +#include <byteswap.h> +#include <endian.h> +#include <asm/byteorder.h> + +extern int debug; + +void putchar(int c) +{ + char buff[16] = ""; + unsigned long *lbuf = (unsigned long *)buff; + + if (!debug) /* running on non pseries */ + return; + + if (c == '\n') + putchar('\r'); + + buff[0] = c; + plpar_hcall_norets(H_PUT_TERM_CHAR, 0, 1, + __cpu_to_be64(lbuf[0]), __cpu_to_be64(lbuf[1])); + return; +} diff --git a/purgatory/arch/ppc64/crashdump_backup.c b/purgatory/arch/ppc64/crashdump_backup.c new file mode 100644 index 0000000..7ba5aff --- /dev/null +++ b/purgatory/arch/ppc64/crashdump_backup.c @@ -0,0 +1,39 @@ +/* + * kexec: Linux boots Linux + * + * Created by: Mohan Kumar M (mohan@in.ibm.com) + * + * Copyright (C) IBM Corporation, 2005. All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation (version 2 of the License). + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <stdint.h> +#include <string.h> +#include "../../../kexec/arch/ppc64/crashdump-ppc64.h" + +extern unsigned long backup_start; + +/* Backup first 32KB of memory to backup region reserved by kexec */ +void crashdump_backup_memory(void) +{ + void *dest, *src; + + src = (void *)BACKUP_SRC_START; + + if (backup_start) { + dest = (void *)(backup_start); + memcpy(dest, src, BACKUP_SRC_SIZE); + } +} diff --git a/purgatory/arch/ppc64/hvCall.S b/purgatory/arch/ppc64/hvCall.S new file mode 100644 index 0000000..a96c489 --- /dev/null +++ b/purgatory/arch/ppc64/hvCall.S @@ -0,0 +1,27 @@ +/* + * This file contains the generic function to perform a call to the + * pSeries LPAR hypervisor. + * + * Taken from linux/arch/powerpc/platforms/pseries/hvCall.S + * + * 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. + */ +#include "ppc64_asm.h" + +#define HVSC .long 0x44000022 +.text + .machine ppc64 +.globl DOTSYM(plpar_hcall_norets) +DOTSYM(plpar_hcall_norets): + or 6,6,6 # medium low priority + mfcr 0 + stw 0,8(1) + + HVSC /* invoke the hypervisor */ + + lwz 0,8(1) + mtcrf 0xff,0 + blr /* return r3 = status */ diff --git a/purgatory/arch/ppc64/hvCall.h b/purgatory/arch/ppc64/hvCall.h new file mode 100644 index 0000000..187e24d --- /dev/null +++ b/purgatory/arch/ppc64/hvCall.h @@ -0,0 +1,8 @@ +#ifndef HVCALL_H +#define HVCALL_H + +#define H_PUT_TERM_CHAR 0x58 + +long plpar_hcall_norets(unsigned long opcode, ...); + +#endif diff --git a/purgatory/arch/ppc64/misc.S b/purgatory/arch/ppc64/misc.S new file mode 100644 index 0000000..4ee188d --- /dev/null +++ b/purgatory/arch/ppc64/misc.S @@ -0,0 +1,197 @@ +/* + * This is from linux/arch/powerpc/lib/crtsavres.S: + * + * Special support for eabi and SVR4 + * + * Copyright (C) 1995, 1996, 1998, 2000, 2001 Free Software Foundation, Inc. + * Copyright 2008 Freescale Semiconductor, Inc. + * Written By Michael Meissner + * + * Based on gcc/config/rs6000/crtsavres.asm from gcc + * 64 bit additions from reading the PPC elf64abi document. + * + * This file 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, or (at your option) any + * later version. + * + * In addition to the permissions in the GNU General Public License, the + * Free Software Foundation gives you unlimited permission to link the + * compiled version of this file with other programs, and to distribute + * those programs without any restriction coming from the use of this + * file. (The General Public License restrictions do apply in other + * respects; for example, they cover modification of the file, and + * distribution when not linked into another program.) + * + * This file 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; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + * As a special exception, if you link this library with files + * compiled with GCC to produce an executable, this does not cause + * the resulting executable to be covered by the GNU General Public License. + * This exception does not however invalidate any other reasons why + * the executable file might be covered by the GNU General Public License. + */ + +#define r0 0 +#define r1 1 +#define r2 2 +#define r3 3 +#define r4 4 +#define r5 5 +#define r6 6 +#define r7 7 +#define r8 8 +#define r9 9 +#define r10 10 +#define r11 11 +#define r12 12 +#define r13 13 +#define r14 14 +#define r15 15 +#define r16 16 +#define r17 17 +#define r18 18 +#define r19 19 +#define r20 20 +#define r21 21 +#define r22 22 +#define r23 23 +#define r24 24 +#define r25 25 +#define r26 26 +#define r27 27 +#define r28 28 +#define r29 29 +#define r30 30 +#define r31 31 + + .text + +.globl _savegpr0_14 +_savegpr0_14: + std r14,-144(r1) +.globl _savegpr0_15 +_savegpr0_15: + std r15,-136(r1) +.globl _savegpr0_16 +_savegpr0_16: + std r16,-128(r1) +.globl _savegpr0_17 +_savegpr0_17: + std r17,-120(r1) +.globl _savegpr0_18 +_savegpr0_18: + std r18,-112(r1) +.globl _savegpr0_19 +_savegpr0_19: + std r19,-104(r1) +.globl _savegpr0_20 +_savegpr0_20: + std r20,-96(r1) +.globl _savegpr0_21 +_savegpr0_21: + std r21,-88(r1) +.globl _savegpr0_22 +_savegpr0_22: + std r22,-80(r1) +.globl _savegpr0_23 +_savegpr0_23: + std r23,-72(r1) +.globl _savegpr0_24 +_savegpr0_24: + std r24,-64(r1) +.globl _savegpr0_25 +_savegpr0_25: + std r25,-56(r1) +.globl _savegpr0_26 +_savegpr0_26: + std r26,-48(r1) +.globl _savegpr0_27 +_savegpr0_27: + std r27,-40(r1) +.globl _savegpr0_28 +_savegpr0_28: + std r28,-32(r1) +.globl _savegpr0_29 +_savegpr0_29: + std r29,-24(r1) +.globl _savegpr0_30 +_savegpr0_30: + std r30,-16(r1) +.globl _savegpr0_31 +_savegpr0_31: + std r31,-8(r1) + std r0,16(r1) + blr + +.globl _restgpr0_14 +_restgpr0_14: + ld r14,-144(r1) +.globl _restgpr0_15 +_restgpr0_15: + ld r15,-136(r1) +.globl _restgpr0_16 +_restgpr0_16: + ld r16,-128(r1) +.globl _restgpr0_17 +_restgpr0_17: + ld r17,-120(r1) +.globl _restgpr0_18 +_restgpr0_18: + ld r18,-112(r1) +.globl _restgpr0_19 +_restgpr0_19: + ld r19,-104(r1) +.globl _restgpr0_20 +_restgpr0_20: + ld r20,-96(r1) +.globl _restgpr0_21 +_restgpr0_21: + ld r21,-88(r1) +.globl _restgpr0_22 +_restgpr0_22: + ld r22,-80(r1) +.globl _restgpr0_23 +_restgpr0_23: + ld r23,-72(r1) +.globl _restgpr0_24 +_restgpr0_24: + ld r24,-64(r1) +.globl _restgpr0_25 +_restgpr0_25: + ld r25,-56(r1) +.globl _restgpr0_26 +_restgpr0_26: + ld r26,-48(r1) +.globl _restgpr0_27 +_restgpr0_27: + ld r27,-40(r1) +.globl _restgpr0_28 +_restgpr0_28: + ld r28,-32(r1) +.globl _restgpr0_29 +_restgpr0_29: + ld r0,16(r1) + ld r29,-24(r1) + mtlr r0 + ld r30,-16(r1) + ld r31,-8(r1) + blr + +.globl _restgpr0_30 +_restgpr0_30: + ld r30,-16(r1) +.globl _restgpr0_31 +_restgpr0_31: + ld r0,16(r1) + ld r31,-8(r1) + mtlr r0 + blr diff --git a/purgatory/arch/ppc64/ppc64_asm.h b/purgatory/arch/ppc64/ppc64_asm.h new file mode 100644 index 0000000..b8746fd --- /dev/null +++ b/purgatory/arch/ppc64/ppc64_asm.h @@ -0,0 +1,16 @@ +/* + * ppc64_asm.h - common defines for PPC64 assembly parts + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +/* + * ABIv1 requires dot symbol while ABIv2 does not. + */ +#if defined(_CALL_ELF) && _CALL_ELF == 2 +#define DOTSYM(a) a +#else +#define GLUE(a,b) a##b +#define DOTSYM(a) GLUE(.,a) +#endif diff --git a/purgatory/arch/ppc64/purgatory-ppc64.c b/purgatory/arch/ppc64/purgatory-ppc64.c new file mode 100644 index 0000000..7248ac8 --- /dev/null +++ b/purgatory/arch/ppc64/purgatory-ppc64.c @@ -0,0 +1,44 @@ +/* + * kexec: Linux boots Linux + * + * Created by: Mohan Kumar M (mohan@in.ibm.com) + * + * Copyright (C) IBM Corporation, 2005. All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation (version 2 of the License). + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <purgatory.h> +#include "purgatory-ppc64.h" + +unsigned int panic_kernel = 0; +unsigned long backup_start = 0; +unsigned long stack = 0; +unsigned long dt_offset = 0; +unsigned long my_toc = 0; +unsigned long kernel = 0; +unsigned int debug = 0; +unsigned long opal_base = 0; +unsigned long opal_entry = 0; + +void setup_arch(void) +{ + return; +} + +void post_verification_setup_arch(void) +{ + if (panic_kernel) + crashdump_backup_memory(); +} diff --git a/purgatory/arch/ppc64/purgatory-ppc64.h b/purgatory/arch/ppc64/purgatory-ppc64.h new file mode 100644 index 0000000..52eaf43 --- /dev/null +++ b/purgatory/arch/ppc64/purgatory-ppc64.h @@ -0,0 +1,6 @@ +#ifndef PURGATORY_PPC64_H +#define PURGATORY_PPC64_H + +void crashdump_backup_memory(void); + +#endif /* PURGATORY_PPC64_H */ diff --git a/purgatory/arch/ppc64/v2wrap.S b/purgatory/arch/ppc64/v2wrap.S new file mode 100644 index 0000000..3534080 --- /dev/null +++ b/purgatory/arch/ppc64/v2wrap.S @@ -0,0 +1,137 @@ +# +# kexec: Linux boots Linux +# +# Copyright (C) 2004 - 2005, Milton D Miller II, IBM Corporation +# Copyright (C) 2006, Mohan Kumar M (mohan@in.ibm.com), IBM Corporation +# +# 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 (version 2 of the License). +# +# 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., 675 Mass Ave, Cambridge, MA 02139, USA. +# + +#include "ppc64_asm.h" + +# v2wrap.S +# a wrapper to call purgatory code to backup first +# 32kB of first kernel into the backup region +# reserved by kexec-tools. +# Invokes ppc64 kernel with the expected arguments +# of kernel(device-tree, phys-offset, 0) + +# +# calling convention: +# r3 = physical number of this cpu (all cpus) +# r4 = address of this chunk (master only) +# master enters at purgatory_start (aka first byte of this chunk) +# slaves (additional cpus), if any, enter a copy of the +# first 0x100 bytes of this code relocated to 0x0 +# +# in other words, +# a copy of the first 0x100 bytes of this code is copied to 0 +# and the slaves are sent to address 0x60 +# with r3 = their physical cpu number. + +#define LOADADDR(rn,name) \ + lis rn,name##@highest; \ + ori rn,rn,name##@higher; \ + rldicr rn,rn,32,31; \ + oris rn,rn,name##@h; \ + ori rn,rn,name##@l + + .machine ppc64 + .align 8 + .globl purgatory_start +purgatory_start: b master + .org purgatory_start + 0x5c # ABI: possible run_at_load flag at 0x5c + .globl run_at_load +run_at_load: + .long 0 + .size run_at_load, . - run_at_load + .org purgatory_start + 0x60 # ABI: slaves start at 60 with r3=phys +slave: b $ + .org purgatory_start + 0x100 # ABI: end of copied region + .size purgatory_start, . - purgatory_start + +# +# The above 0x100 bytes at purgatory_start are replaced with the +# code from the kernel (or next stage) by kexec/arch/ppc64/kexec-elf-ppc64.c +# + +master: + or 1,1,1 # low priority to let other threads catchup + isync + mr 17,3 # save cpu id to r17 + mr 15,4 # save physical address in reg15 + + LOADADDR(6,my_toc) + ld 2,0(6) #setup toc + + LOADADDR(6,stack) + ld 1,0(6) #setup stack + + subi 1,1,112 + bl DOTSYM(purgatory) + nop + + or 3,3,3 # ok now to high priority, lets boot + lis 6,0x1 + mtctr 6 # delay a bit for slaves to catch up +83: bdnz 83b # before we overwrite 0-100 again + + LOADADDR(16, dt_offset) + ld 3,0(16) # load device-tree address + mr 16,3 # save dt address in reg16 +#ifdef __BIG_ENDIAN__ + lwz 6,20(3) # fetch version number +#else + li 4,20 + lwbrx 6,3,4 # fetch BE version number +#endif + cmpwi 0,6,2 # v2 ? + blt 80f +#ifdef __BIG_ENDIAN__ + stw 17,28(3) # save my cpu number as boot_cpu_phys +#else + li 4,28 + stwbrx 17,3,4 # Store my cpu as BE value +#endif +80: + LOADADDR(6,opal_base) # For OPAL early debug + ld 8,0(6) # load the OPAL base address in r8 + LOADADDR(6,opal_entry) # For OPAL early debug + ld 9,0(6) # load the OPAL entry address in r9 + LOADADDR(6,kernel) + ld 4,0(6) # load the kernel address + LOADADDR(6,run_at_load) # the load flag + lwz 7,0(6) # possibly patched by kexec-elf-ppc64 + stw 7,0x5c(4) # and patch it into the kernel + mr 3,16 # restore dt address + + mfmsr 5 + andi. 10,5,1 # test MSR_LE + bne little_endian + + li 5,0 # r5 will be 0 for kernel + mtctr 4 # prepare branch to + bctr # start kernel + +little_endian: # book3s-only + mtsrr0 4 # prepare branch to + + clrrdi 5,5,1 # clear MSR_LE + mtsrr1 5 + + li 5,0 # r5 will be 0 for kernel + + # skip cache flush, do we care? + + rfid # update MSR and start kernel diff --git a/purgatory/arch/s390/Makefile b/purgatory/arch/s390/Makefile new file mode 100644 index 0000000..c94cc3c --- /dev/null +++ b/purgatory/arch/s390/Makefile @@ -0,0 +1,10 @@ +# +# Purgatory s390 +# + +s390_PURGATORY_SRCS += purgatory/arch/s390/console-s390.c +s390_PURGATORY_SRCS += purgatory/arch/s390/setup-s390.S +s390_PURGATORY_SRCS += purgatory/arch/s390/purgatory-s390.c + +dist += purgatory/arch/s390/Makefile $(s390_PURGATORY_SRCS) + diff --git a/purgatory/arch/s390/console-s390.c b/purgatory/arch/s390/console-s390.c new file mode 100644 index 0000000..fe4bd91 --- /dev/null +++ b/purgatory/arch/s390/console-s390.c @@ -0,0 +1,14 @@ +/* + * S390 console code (currently not implemented) + * + * Copyright IBM Corp. 2011 + * + * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com> + */ + +#include <purgatory.h> +#include "unused.h" + +void putchar(int UNUSED(ch)) +{ +} diff --git a/purgatory/arch/s390/purgatory-s390.c b/purgatory/arch/s390/purgatory-s390.c new file mode 100644 index 0000000..f438cf6 --- /dev/null +++ b/purgatory/arch/s390/purgatory-s390.c @@ -0,0 +1,93 @@ +/* + * S390 purgatory + * + * Copyright IBM Corp. 2011 + * + * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com> + */ + +#include <stdint.h> +#include <stddef.h> +#include <string.h> +#include "../../../kexec/kexec-sha256.h" + +#define MIN(x, y) ((x) < (y) ? (x) : (y)) +#define MAX(x, y) ((x) > (y) ? (x) : (y)) + +extern struct sha256_region sha256_regions[SHA256_REGIONS]; + +unsigned long crash_base = (unsigned long) -1; +unsigned long crash_size = (unsigned long) -1; + +/* + * Implement memcpy using the mvcle instruction + */ +static void memcpy_fast(void *target, void *src, unsigned long size) +{ + register unsigned long __target asm("2") = (unsigned long) target; + register unsigned long __size1 asm("3") = size; + register unsigned long __src asm("4") = (unsigned long) src; + register unsigned long __size2 asm("5") = size; + + asm volatile ( + "0: mvcle %0,%2,0\n" + " jo 0b\n" + : "+d" (__target), "+d" (__size1), "+d" (__src), "+d" (__size2) + : + : "cc", "memory" + ); +} + +/* + * Swap memory areas + */ +static void memswap(void *addr1, void *addr2, unsigned long size) +{ + unsigned long off, copy_len; + static char buf[1024]; + + for (off = 0; off < size; off += sizeof(buf)) { + copy_len = MIN(size - off, sizeof(buf)); + memcpy_fast(buf, (void *) addr2 + off, copy_len); + memcpy_fast(addr2 + off, addr1 + off, copy_len); + memcpy_fast(addr1 + off, buf, copy_len); + } +} + +/* + * Nothing to do + */ +void setup_arch(void) +{ +} + +/* + * Do swap of [crash base - crash base + size] with [0 - crash size] + * + * We swap all kexec segments except of purgatory. The rest is copied + * from [0 - crash size] to [crash base - crash base + size]. + * We use [0x2000 - 0x10000] for purgatory. This area is never used + * by s390 Linux kernels. + * + * This functions assumes that the sha256_regions[] is sorted. + */ +void post_verification_setup_arch(void) +{ + unsigned long start, len, last = crash_base + 0x10000; + struct sha256_region *ptr, *end; + + end = &sha256_regions[sizeof(sha256_regions)/sizeof(sha256_regions[0])]; + for (ptr = sha256_regions; ptr < end; ptr++) { + if (!ptr->start) + continue; + start = MAX(ptr->start, crash_base + 0x10000); + len = ptr->len - (start - ptr->start); + memcpy_fast((void *) last, (void *) last - crash_base, + start - last); + memswap((void *) start - crash_base, (void *) start, len); + last = start + len; + } + memcpy_fast((void *) last, (void *) last - crash_base, + crash_base + crash_size - last); + memcpy_fast((void *) crash_base, (void *) 0, 0x2000); +} diff --git a/purgatory/arch/s390/setup-s390.S b/purgatory/arch/s390/setup-s390.S new file mode 100644 index 0000000..215478a --- /dev/null +++ b/purgatory/arch/s390/setup-s390.S @@ -0,0 +1,47 @@ +/* + * Purgatory setup code + * + * Copyright IBM Corp. 2011 + * + * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com> + */ + + .text + .globl purgatory_start + .balign 16 +purgatory_start: +#ifdef __s390x__ + larl %r5,gprs_save_area + stmg %r6,%r15,0(%r5) + larl %r15,lstack_end + aghi %r15,-160 + + cghi %r2,0 + je verify_checksums + + brasl %r14,purgatory + larl %r14,kdump_psw + lpswe 0(%r14) + +verify_checksums: + brasl %r14,verify_sha256_digest + larl %r5,gprs_save_area + lmg %r6,%r15,0(%r5) + br %r14 + .section ".data" + .balign 16 +kdump_psw: + .quad 0x0000000180000000 + .quad 0x0000000000010010 + + .bss +gprs_save_area: + .fill 80 + + .balign 4096 +lstack: + .skip 4096 +lstack_end: +#else +0: j 0 +#endif diff --git a/purgatory/arch/sh/Makefile b/purgatory/arch/sh/Makefile new file mode 100644 index 0000000..33ad4db --- /dev/null +++ b/purgatory/arch/sh/Makefile @@ -0,0 +1,8 @@ +# +# Purgatory sh +# + +sh_PURGATORY_SRCS = + +dist += purgatory/arch/sh/Makefile $(sh_PURGATORY_SRCS) + diff --git a/purgatory/arch/x86_64/Makefile b/purgatory/arch/x86_64/Makefile new file mode 100644 index 0000000..b0e277a --- /dev/null +++ b/purgatory/arch/x86_64/Makefile @@ -0,0 +1,28 @@ +# +# Purgatory x86_64 +# + +x86_64_PURGATORY_SRCS_native = purgatory/arch/x86_64/entry64-32.S +x86_64_PURGATORY_SRCS_native += purgatory/arch/x86_64/entry64.S +x86_64_PURGATORY_SRCS_native += purgatory/arch/x86_64/setup-x86_64.S +x86_64_PURGATORY_SRCS_native += purgatory/arch/x86_64/stack.S +x86_64_PURGATORY_SRCS_native += purgatory/arch/x86_64/purgatory-x86_64.c + +x86_64_PURGATORY_SRCS += $(x86_64_PURGATORY_SRCS_native) + +dist += purgatory/arch/x86_64/Makefile $(x86_64_PURGATORY_SRCS_native) \ + purgatory/arch/x86_64/include/arch/io.h \ + purgatory/arch/x86_64/include/arch/debug.h \ + purgatory/arch/x86_64/purgatory-x86_64.h + +# Don't add sources in i386/ to dist, as i386/Makefile adds them +x86_64_PURGATORY_SRCS += purgatory/arch/i386/entry32-16.S +x86_64_PURGATORY_SRCS += purgatory/arch/i386/entry32-16-debug.S +x86_64_PURGATORY_SRCS += purgatory/arch/i386/crashdump_backup.c +x86_64_PURGATORY_SRCS += purgatory/arch/i386/console-x86.c +x86_64_PURGATORY_SRCS += purgatory/arch/i386/vga.c +x86_64_PURGATORY_SRCS += purgatory/arch/i386/pic.c + +ifneq ($(SUBARCH),x32) +x86_64_PURGATORY_EXTRA_CFLAGS = -mcmodel=large +endif diff --git a/purgatory/arch/x86_64/entry64-32.S b/purgatory/arch/x86_64/entry64-32.S new file mode 100644 index 0000000..f2b6377 --- /dev/null +++ b/purgatory/arch/x86_64/entry64-32.S @@ -0,0 +1,164 @@ +/* + * purgatory: setup code + * + * Copyright (C) 2003,2004 Eric Biederman (ebiederm@xmission.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation (version 2 of the License). + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +.data + .equ MSR_K6_EFER, 0xC0000080 + .equ EFER_LME, 0x00000100 + .equ X86_CR4_PAE, 0x00000020 + .equ CR0_PG, 0x80000000 + + .text + .balign 16 + .globl entry32, entry32_regs +entry32: + .code64 + + /* Setup the 4G offset of entry32 lm_exit code segment */ + movq $0x00CF9A000000ffff, %rax + + leaq entry32(%rip), %rbx /* Low 24 bits */ + andq $0xffffff, %rbx + shlq $16, %rbx + orq %rbx, %rax + + leaq entry32(%rip), %rbx /* High 8 bits */ + movq $0xff000000, %rdx + andq %rdx, %rbx + shlq $32, %rbx + orq %rbx, %rax + + movq %rax, (gdt + 0x20)(%rip) + + /* Setup a gdt that is generally usefully */ + lgdt gdt(%rip) + + /* Setup the far pointer to the entry point */ + movl eip(%rip), %eax + movl %eax, entry32_addr(%rip) + + /* Switch to 32bit compatiblity mode */ + ljmp *lm_exit_addr(%rip) +lm_exit: + .code32 + + /* Disable paging */ + movl %cr0, %eax + andl $~CR0_PG, %eax + movl %eax, %cr0 + + /* Disable long mode */ + movl $MSR_K6_EFER, %ecx + rdmsr + andl $~EFER_LME, %eax + wrmsr + + /* Disable PAE */ + xorl %eax, %eax + movl %eax, %cr4 + + /* load the data segments */ + movl $0x18, %eax /* data segment */ + movl %eax, %ds + movl %eax, %es + movl %eax, %ss + movl %eax, %fs + movl %eax, %gs + + /* Load the registers */ + movl %cs:eax - entry32, %eax + movl %cs:ecx - entry32, %ecx + movl %cs:edx - entry32, %edx + movl %cs:esi - entry32, %esi + movl %cs:edi - entry32, %edi + movl %cs:esp - entry32, %esp + movl %cs:ebp - entry32, %ebp + movl %cs:ebx - entry32, %ebx + + /* Jump to the loaded image */ + ljmp *%cs:entry32_addr - entry32 + + .section ".data" + .balign 16 +gdt: /* 0x00 unusable segment + * 0x08 unused + * so use them as the gdt ptr + */ + .word gdt_end - gdt - 1 + .quad gdt + .word 0, 0, 0 + + /* Documented linux kernel segments */ + /* 0x10 4GB flat code segment */ + .word 0xFFFF, 0x0000, 0x9A00, 0x00CF + /* 0x18 4GB flat data segment */ + .word 0xFFFF, 0x0000, 0x9200, 0x00CF + + /* 0x20 4GB flat code segment base at entry32 */ + .word 0xFFFF, 0x0000, 0x9A00, 0x0CF + /* 0x28 dummy */ + .word 0x0000, 0x0000, 0x0000, 0x000 + /* 0x30 dummy */ + .word 0x0000, 0x0000, 0x0000, 0x000 + /* 0x38 dummy */ + .word 0x0000, 0x0000, 0x0000, 0x000 + /* 0x40 dummy */ + .word 0x0000, 0x0000, 0x0000, 0x000 + /* 0x48 dummy */ + .word 0x0000, 0x0000, 0x0000, 0x000 + /* 0x50 dummy */ + .word 0x0000, 0x0000, 0x0000, 0x000 + /* 0x58 dummy */ + .word 0x0000, 0x0000, 0x0000, 0x000 + + /* Segments used by the 2.5.x kernel */ + /* 0x60 4GB flat code segment */ + .word 0xFFFF, 0x0000, 0x9A00, 0x00CF + /* 0x68 4GB flat data segment */ + .word 0xFFFF, 0x0000, 0x9200, 0x00CF +gdt_end: + + .section ".rodata" + .balign 4 +lm_exit_addr: + .long lm_exit - entry32 + .long 0x20 + + .section ".data" + .balign 4 +entry32_addr: + .long 0x00000000 + .long 0x10 + + .section ".rodata" + .balign 4 +entry32_regs: +eax: .long 0x00000000 +ebx: .long 0x00000000 +ecx: .long 0x00000000 +edx: .long 0x00000000 +esi: .long 0x00000000 +edi: .long 0x00000000 +esp: .long 0x00000000 +ebp: .long 0x00000000 +eip: .quad entry16 /* low 32 bits address + * high 32bits zeros + * uses 64bit reloc + */ + .size entry32_regs, (. - 4) - entry32_regs + diff --git a/purgatory/arch/x86_64/entry64.S b/purgatory/arch/x86_64/entry64.S new file mode 100644 index 0000000..e3223b7 --- /dev/null +++ b/purgatory/arch/x86_64/entry64.S @@ -0,0 +1,107 @@ +/* + * kexec: Linux boots Linux + * + * Copyright (C) 2003,2004 Eric Biederman (ebiederm@xmission.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation (version 2 of the License). + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "arch/debug.h" + +.text +.code64 + .balign 16 + .globl entry64, entry64_regs +entry64: + /* Don't worry about special registers... */ + + /* Setup a gdt that should be preserved */ + lgdt gdt(%rip) + + /* load the data segments */ + movl $0x18, %eax /* data segment */ + movl %eax, %ds + movl %eax, %es + movl %eax, %ss + movl %eax, %fs + movl %eax, %gs + + leaq stack_init(%rip), %rsp + pushq $0x10 /* CS */ + leaq new_cs_exit(%rip), %rax + pushq %rax + lretq +new_cs_exit: + + /* Load the registers */ + movq rax(%rip), %rax + movq rbx(%rip), %rbx + movq rcx(%rip), %rcx + movq rdx(%rip), %rdx + movq rsi(%rip), %rsi + movq rdi(%rip), %rdi + movq rsp(%rip), %rsp + movq rbp(%rip), %rbp + movq r8(%rip), %r8 + movq r9(%rip), %r9 + movq r10(%rip), %r10 + movq r11(%rip), %r11 + movq r12(%rip), %r12 + movq r13(%rip), %r13 + movq r14(%rip), %r14 + movq r15(%rip), %r15 + + /* Jump to the new code... */ + jmpq *rip(%rip) + + .section ".rodata" + .balign 4 +entry64_regs: +rax: .quad 0x00000000 +rbx: .quad 0x00000000 +rcx: .quad 0x00000000 +rdx: .quad 0x00000000 +rsi: .quad 0x00000000 +rdi: .quad 0x00000000 +rsp: .quad 0x00000000 +rbp: .quad 0x00000000 +r8: .quad 0x00000000 +r9: .quad 0x00000000 +r10: .quad 0x00000000 +r11: .quad 0x00000000 +r12: .quad 0x00000000 +r13: .quad 0x00000000 +r14: .quad 0x00000000 +r15: .quad 0x00000000 +rip: .quad entry32 + .size entry64_regs, . - entry64_regs + + .section ".rodata" + .balign 16 +gdt: /* 0x00 unusable segment + * 0x08 unused + * so use them as the gdt ptr + */ + .word gdt_end - gdt - 1 + .quad gdt + .word 0, 0, 0 + + /* 0x10 4GB flat code segment */ + .word 0xFFFF, 0x0000, 0x9A00, 0x00AF + + /* 0x18 4GB flat data segment */ + .word 0xFFFF, 0x0000, 0x9200, 0x00CF +gdt_end: +stack: .quad 0, 0 +stack_init: diff --git a/purgatory/arch/x86_64/include/arch/debug.h b/purgatory/arch/x86_64/include/arch/debug.h new file mode 100644 index 0000000..e050942 --- /dev/null +++ b/purgatory/arch/x86_64/include/arch/debug.h @@ -0,0 +1,317 @@ +/* Base Address */ +#define TTYS0_BASE 0x3f8 +/* Data */ +#define TTYS0_RBR (TTYS0_BASE+0x00) +#define TTYS0_TBR (TTYS0_BASE+0x00) +/* Control */ +#define TTYS0_IER (TTYS0_BASE+0x01) +#define TTYS0_IIR (TTYS0_BASE+0x02) +#define TTYS0_FCR (TTYS0_BASE+0x02) +#define TTYS0_LCR (TTYS0_BASE+0x03) +#define TTYS0_MCR (TTYS0_BASE+0x04) + +#define TTYS0_DLL (TTYS0_BASE+0x00) +#define TTYS0_DLM (TTYS0_BASE+0x01) +/* Status */ +#define TTYS0_LSR (TTYS0_BASE+0x05) +#define TTYS0_MSR (TTYS0_BASE+0x06) +#define TTYS0_SCR (TTYS0_BASE+0x07) + +#define TTYS0_BAUD 9600 +#define TTYS0_DIV (115200/TTYS0_BAUD) +#define TTYS0_DIV_LO (TTYS0_DIV&0xFF) +#define TTYS0_DIV_HI ((TTYS0_DIV >> 8)&0xFF) + +#if ((115200%TTYS0_BAUD) != 0) +#error Bad ttyS0 baud rate +#endif + +#define TTYS0_INIT \ + /* disable interrupts */ \ + movb $0x00, %al ; \ + movw $TTYS0_IER, %dx ; \ + outb %al, %dx ; \ + ; \ + /* enable fifos */ \ + movb $0x01, %al ; \ + movw $TTYS0_FCR, %dx ; \ + outb %al, %dx ; \ + ; \ + /* Set Baud Rate Divisor to TTYS0_BAUD */ \ + movw $TTYS0_LCR, %dx ; \ + movb $0x83, %al ; \ + outb %al, %dx ; \ + ; \ + movw $TTYS0_DLL, %dx ; \ + movb $TTYS0_DIV_LO, %al ; \ + outb %al, %dx ; \ + ; \ + movw $TTYS0_DLM, %dx ; \ + movb $TTYS0_DIV_HI, %al ; \ + outb %al, %dx ; \ + ; \ + movw $TTYS0_LCR, %dx ; \ + movb $0x03, %al ; \ + outb %al, %dx + + + /* uses: ax, dx */ +#define TTYS0_TX_AL \ + mov %al, %ah ; \ +9: mov $TTYS0_LSR, %dx ; \ + inb %dx, %al ; \ + test $0x20, %al ; \ + je 9b ; \ + mov $TTYS0_TBR, %dx ; \ + mov %ah, %al ; \ + outb %al, %dx + + /* uses: ax, dx */ +#define TTYS0_TX_CHAR(byte) \ + mov byte, %al ; \ + TTYS0_TX_AL + + /* uses: eax, dx */ +#define TTYS0_TX_HEX32(lword) \ + mov lword, %eax ; \ + shr $28, %eax ; \ + add $'0', %al ; \ + cmp $'9', %al ; \ + jle 9f ; \ + add $39, %al ; \ +9: ; \ + TTYS0_TX_AL ; \ + ; \ + mov lword, %eax ; \ + shr $24, %eax ; \ + and $0x0f, %al ; \ + add $'0', %al ; \ + cmp $'9', %al ; \ + jle 9f ; \ + add $39, %al ; \ +9: ; \ + TTYS0_TX_AL ; \ + ; \ + mov lword, %eax ; \ + shr $20, %eax ; \ + and $0x0f, %al ; \ + add $'0', %al ; \ + cmp $'9', %al ; \ + jle 9f ; \ + add $39, %al ; \ +9: ; \ + TTYS0_TX_AL ; \ + ; \ + mov lword, %eax ; \ + shr $16, %eax ; \ + and $0x0f, %al ; \ + add $'0', %al ; \ + cmp $'9', %al ; \ + jle 9f ; \ + add $39, %al ; \ +9: ; \ + TTYS0_TX_AL ; \ + ; \ + mov lword, %eax ; \ + shr $12, %eax ; \ + and $0x0f, %al ; \ + add $'0', %al ; \ + cmp $'9', %al ; \ + jle 9f ; \ + add $39, %al ; \ +9: ; \ + TTYS0_TX_AL ; \ + ; \ + mov lword, %eax ; \ + shr $8, %eax ; \ + and $0x0f, %al ; \ + add $'0', %al ; \ + cmp $'9', %al ; \ + jle 9f ; \ + add $39, %al ; \ +9: ; \ + TTYS0_TX_AL ; \ + ; \ + mov lword, %eax ; \ + shr $4, %eax ; \ + and $0x0f, %al ; \ + add $'0', %al ; \ + cmp $'9', %al ; \ + jle 9f ; \ + add $39, %al ; \ +9: ; \ + TTYS0_TX_AL ; \ + ; \ + mov lword, %eax ; \ + and $0x0f, %al ; \ + add $'0', %al ; \ + cmp $'9', %al ; \ + jle 9f ; \ + add $39, %al ; \ +9: ; \ + TTYS0_TX_AL + + /* uses: rax, dx */ +#define TTYS0_TX_HEX64(lword) \ + mov lword, %rax ; \ + shr $60, %rax ; \ + add $'0', %al ; \ + cmp $'9', %al ; \ + jle 9f ; \ + add $39, %al ; \ +9: ; \ + TTYS0_TX_AL ; \ + ; \ + mov lword, %rax ; \ + shr $56, %rax ; \ + and $0x0f, %al ; \ + add $'0', %al ; \ + cmp $'9', %al ; \ + jle 9f ; \ + add $39, %al ; \ +9: ; \ + TTYS0_TX_AL ; \ + ; \ + mov lword, %rax ; \ + shr $52, %rax ; \ + and $0x0f, %al ; \ + add $'0', %al ; \ + cmp $'9', %al ; \ + jle 9f ; \ + add $39, %al ; \ +9: ; \ + TTYS0_TX_AL ; \ + ; \ + mov lword, %rax ; \ + shr $48, %rax ; \ + and $0x0f, %al ; \ + add $'0', %al ; \ + cmp $'9', %al ; \ + jle 9f ; \ + add $39, %al ; \ +9: ; \ + TTYS0_TX_AL ; \ + ; \ + mov lword, %rax ; \ + shr $44, %rax ; \ + and $0x0f, %al ; \ + add $'0', %al ; \ + cmp $'9', %al ; \ + jle 9f ; \ + add $39, %al ; \ +9: ; \ + TTYS0_TX_AL ; \ + ; \ + mov lword, %rax ; \ + shr $40, %rax ; \ + and $0x0f, %al ; \ + add $'0', %al ; \ + cmp $'9', %al ; \ + jle 9f ; \ + add $39, %al ; \ +9: ; \ + TTYS0_TX_AL ; \ + ; \ + mov lword, %rax ; \ + shr $36, %rax ; \ + and $0x0f, %al ; \ + add $'0', %al ; \ + cmp $'9', %al ; \ + jle 9f ; \ + add $39, %al ; \ +9: ; \ + TTYS0_TX_AL ; \ + ; \ + mov lword, %rax ; \ + shr $32, %rax ; \ + and $0x0f, %al ; \ + add $'0', %al ; \ + cmp $'9', %al ; \ + jle 9f ; \ + add $39, %al ; \ +9: ; \ + TTYS0_TX_AL ; \ + ; \ + mov lword, %rax ; \ + shr $28, %rax ; \ + add $'0', %al ; \ + cmp $'9', %al ; \ + jle 9f ; \ + add $39, %al ; \ +9: ; \ + TTYS0_TX_AL ; \ + ; \ + mov lword, %rax ; \ + shr $24, %rax ; \ + and $0x0f, %al ; \ + add $'0', %al ; \ + cmp $'9', %al ; \ + jle 9f ; \ + add $39, %al ; \ +9: ; \ + TTYS0_TX_AL ; \ + ; \ + mov lword, %rax ; \ + shr $20, %rax ; \ + and $0x0f, %al ; \ + add $'0', %al ; \ + cmp $'9', %al ; \ + jle 9f ; \ + add $39, %al ; \ +9: ; \ + TTYS0_TX_AL ; \ + ; \ + mov lword, %rax ; \ + shr $16, %rax ; \ + and $0x0f, %al ; \ + add $'0', %al ; \ + cmp $'9', %al ; \ + jle 9f ; \ + add $39, %al ; \ +9: ; \ + TTYS0_TX_AL ; \ + ; \ + mov lword, %rax ; \ + shr $12, %rax ; \ + and $0x0f, %al ; \ + add $'0', %al ; \ + cmp $'9', %al ; \ + jle 9f ; \ + add $39, %al ; \ +9: ; \ + TTYS0_TX_AL ; \ + ; \ + mov lword, %rax ; \ + shr $8, %rax ; \ + and $0x0f, %al ; \ + add $'0', %al ; \ + cmp $'9', %al ; \ + jle 9f ; \ + add $39, %al ; \ +9: ; \ + TTYS0_TX_AL ; \ + ; \ + mov lword, %rax ; \ + shr $4, %rax ; \ + and $0x0f, %al ; \ + add $'0', %al ; \ + cmp $'9', %al ; \ + jle 9f ; \ + add $39, %al ; \ +9: ; \ + TTYS0_TX_AL ; \ + ; \ + mov lword, %rax ; \ + and $0x0f, %al ; \ + add $'0', %al ; \ + cmp $'9', %al ; \ + jle 9f ; \ + add $39, %al ; \ +9: ; \ + TTYS0_TX_AL + + +#define DEBUG_CHAR(x) TTYS0_TX_CHAR($x) ; TTYS0_TX_CHAR($'\r') ; TTYS0_TX_CHAR($'\n') +#define DEBUG_TX_HEX32(x) TTYS0_TX_HEX32(x); TTYS0_TX_CHAR($'\r') ; TTYS0_TX_CHAR($'\n') +#define DEBUG_TX_HEX64(x) TTYS0_TX_HEX64(x); TTYS0_TX_CHAR($'\r') ; TTYS0_TX_CHAR($'\n') + diff --git a/purgatory/arch/x86_64/include/arch/io.h b/purgatory/arch/x86_64/include/arch/io.h new file mode 100644 index 0000000..464cd14 --- /dev/null +++ b/purgatory/arch/x86_64/include/arch/io.h @@ -0,0 +1,7 @@ +#ifndef ARCH_X86_64_IO_H +#define ARCH_X86_64_IO_H + +#include <stdint.h> +#include "../../../i386/include/arch/io.h" + +#endif /* ARCH_X86_64_IO_H */ diff --git a/purgatory/arch/x86_64/purgatory-x86_64.c b/purgatory/arch/x86_64/purgatory-x86_64.c new file mode 100644 index 0000000..c25a9c2 --- /dev/null +++ b/purgatory/arch/x86_64/purgatory-x86_64.c @@ -0,0 +1,30 @@ +#include <stdint.h> +#include <stddef.h> +#include <purgatory.h> +#include "purgatory-x86_64.h" + +uint8_t reset_vga = 0; +uint8_t legacy_pic = 0; +uint8_t panic_kernel = 0; +unsigned long jump_back_entry = 0; +char *cmdline_end = NULL; + +void setup_arch(void) +{ + if (reset_vga) x86_reset_vga(); + if (legacy_pic) x86_setup_legacy_pic(); +} + +void x86_setup_jump_back_entry(void) +{ + if (cmdline_end) + sprintf(cmdline_end, " kexec_jump_back_entry=0x%lx", + jump_back_entry); +} + +/* This function can be used to execute after the SHA256 verification. */ +void post_verification_setup_arch(void) +{ + if (panic_kernel) crashdump_backup_memory(); + if (jump_back_entry) x86_setup_jump_back_entry(); +} diff --git a/purgatory/arch/x86_64/purgatory-x86_64.h b/purgatory/arch/x86_64/purgatory-x86_64.h new file mode 100644 index 0000000..209edea --- /dev/null +++ b/purgatory/arch/x86_64/purgatory-x86_64.h @@ -0,0 +1,4 @@ +#ifndef PURGATORY_X86_64_H +#define PURGATORY_X86_64_H +#include "../i386/purgatory-x86.h" +#endif /* PURGATORY_X86_64_H */ diff --git a/purgatory/arch/x86_64/setup-x86_64.S b/purgatory/arch/x86_64/setup-x86_64.S new file mode 100644 index 0000000..95572d8 --- /dev/null +++ b/purgatory/arch/x86_64/setup-x86_64.S @@ -0,0 +1,76 @@ +/* + * purgatory: setup code + * + * Copyright (C) 2003,2004 Eric Biederman (ebiederm@xmission.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation (version 2 of the License). + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "arch/debug.h" + + +#undef i386 + + .text + .globl purgatory_start + .balign 16 +purgatory_start: + .code64 + + /* Load a gdt so I know what the segment registers are */ + lgdt gdt(%rip) + + /* load the data segments */ + movl $0x18, %eax /* data segment */ + movl %eax, %ds + movl %eax, %es + movl %eax, %ss + movl %eax, %fs + movl %eax, %gs + + /* In 64bit mode the code segment is meaningless */ + + movq 0(%rsp), %rax + movq %rax, jump_back_entry(%rip) + + /* Setup a stack */ + leaq lstack_end(%rip), %rsp + + /* Call the C code */ + call purgatory + jmp entry64 + + .section ".rodata" + .balign 16 +gdt: /* 0x00 unusable segment + * 0x08 unused + * so use them as the gdt ptr + */ + .word gdt_end - gdt - 1 + .quad gdt + .word 0, 0, 0 + + /* 0x10 4GB flat code segment */ + .word 0xFFFF, 0x0000, 0x9A00, 0x00AF + + /* 0x18 4GB flat data segment */ + .word 0xFFFF, 0x0000, 0x9200, 0x00CF +gdt_end: + + .bss + .balign 4096 +lstack: + .skip 4096 +lstack_end: + diff --git a/purgatory/arch/x86_64/stack.S b/purgatory/arch/x86_64/stack.S new file mode 100644 index 0000000..4188ea3 --- /dev/null +++ b/purgatory/arch/x86_64/stack.S @@ -0,0 +1,44 @@ +/* + * purgatory: stack + * + * Copyright (C) 2003,2004 Eric Biederman (ebiederm@xmission.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation (version 2 of the License). + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + /* A stack for the loaded kernel. + * Seperate and in the data section so it can be prepopulated. + */ + .data + .balign 4096 + .globl stack, stack_end + .globl stack_arg32_1, stack_arg32_2, stack_arg32_3 ,stack_arg32_4 + .globl stack_arg32_5, stack_arg32_6, stack_arg32_7 ,stack_arg32_8 + .globl stack_arg64_1, stack_arg64_2, stack_arg64_3 ,stack_arg64_4 + +stack: + .skip 4096 - (8*4) +stack_arg64_4: ; .size stack_arg64_4, 8 +stack_arg32_8: .long 0 ; .size stack_arg32_8, 4 +stack_arg32_7: .long 0 ; .size stack_arg32_7, 4 +stack_arg64_3: ; .size stack_arg64_3, 8 +stack_arg32_6: .long 0 ; .size stack_arg32_6, 4 +stack_arg32_5: .long 0 ; .size stack_arg32_5, 4 +stack_arg64_2: ; .size stack_arg64_2, 8 +stack_arg32_4: .long 0 ; .size stack_arg32_4, 4 +stack_arg32_3: .long 0 ; .size stack_arg32_3, 4 +stack_arg64_1: ; .size stack_arg64_1, 8 +stack_arg32_2: .long 0 ; .size stack_arg32_2, 4 +stack_arg32_1: .long 0 ; .size stack_arg32_1, 4 +stack_end: diff --git a/purgatory/include/purgatory.h b/purgatory/include/purgatory.h new file mode 100644 index 0000000..788ce49 --- /dev/null +++ b/purgatory/include/purgatory.h @@ -0,0 +1,11 @@ +#ifndef PURGATORY_H +#define PURGATORY_H + +void putchar(int ch); +void sprintf(char *buffer, const char *fmt, ...) + __attribute__ ((format (printf, 2, 3))); +void printf(const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); +void setup_arch(void); +void post_verification_setup_arch(void); + +#endif /* PURGATORY_H */ diff --git a/purgatory/include/string.h b/purgatory/include/string.h new file mode 100644 index 0000000..14e172d --- /dev/null +++ b/purgatory/include/string.h @@ -0,0 +1,12 @@ +#ifndef STRING_H +#define STRING_H + +#include <stddef.h> + +size_t strnlen(const char *s, size_t max); +void* memset(void* s, int c, size_t n); +void* memcpy(void *dest, const void *src, size_t len); +int memcmp(void *src1, void *src2, size_t len); + + +#endif /* STRING_H */ diff --git a/purgatory/printf.c b/purgatory/printf.c new file mode 100644 index 0000000..9a78243 --- /dev/null +++ b/purgatory/printf.c @@ -0,0 +1,154 @@ +#include <stdarg.h> +#include <limits.h> +#include <stdint.h> +#include <purgatory.h> +#include <string.h> + +/* + * Output + * ============================================================================= + */ + +#define LONG_LONG_SHIFT ((int)((sizeof(unsigned long long)*CHAR_BIT) - 4)) +#define LONG_SHIFT ((int)((sizeof(unsigned long)*CHAR_BIT) - 4)) +#define INT_SHIFT ((int)((sizeof(unsigned int)*CHAR_BIT) - 4)) +#define SHRT_SHIFT ((int)((sizeof(unsigned short)*CHAR_BIT) - 4)) +#define CHAR_SHIFT ((int)((sizeof(unsigned char)*CHAR_BIT) - 4)) + +/************************************************************************** +PRINTF and friends + + Formats: + %x - 4 bytes int (8 hex digits, lower case) + %X - 4 bytes int (8 hex digits, upper case) + %lx - 8 bytes long (16 hex digits, lower case) + %lX - 8 bytes long (16 hex digits, upper case) + %hx - 2 bytes int (4 hex digits, lower case) + %hX - 2 bytes int (4 hex digits, upper case) + %hhx - 1 byte int (2 hex digits, lower case) + %hhX - 1 byte int (2 hex digits, upper case) + - optional # prefixes 0x or 0X + %d - decimal int + %c - char + %s - string + Note: width specification not supported +**************************************************************************/ +void vsprintf(char *buffer, const char *fmt, va_list args) +{ + char *p; + for ( ; *fmt != '\0'; ++fmt) { + if (*fmt != '%') { + if (buffer) + *buffer++ = *fmt; + else + putchar(*fmt); + continue; + } + if (*++fmt == 's') { + for(p = va_arg(args, char *); *p != '\0'; p++) + if (buffer) + *buffer++ = *p; + else + putchar(*p); + } + else { /* Length of item is bounded */ + char tmp[40], *q = tmp; + int shift = INT_SHIFT; + if (*fmt == 'L') { + shift = LONG_LONG_SHIFT; + fmt++; + } + else if (*fmt == 'l') { + shift = LONG_SHIFT; + fmt++; + } + else if (*fmt == 'h') { + shift = SHRT_SHIFT; + fmt++; + if (*fmt == 'h') { + shift = CHAR_SHIFT; + fmt++; + } + } + + /* + * Before each format q points to tmp buffer + * After each format q points past end of item + */ + if ((*fmt | 0x20) == 'x') { + /* With x86 gcc, sizeof(long) == sizeof(int) */ + unsigned long long h; + int ncase; + if (shift > LONG_SHIFT) { + h = va_arg(args, unsigned long long); + } + else if (shift > INT_SHIFT) { + h = va_arg(args, unsigned long); + } else { + h = va_arg(args, unsigned int); + } + ncase = (*fmt & 0x20); + for ( ; shift >= 0; shift -= 4) + *q++ = "0123456789ABCDEF"[(h >> shift) & 0xF] | ncase; + } + else if (*fmt == 'd') { + char *r; + long i; + if (shift > LONG_SHIFT) { + i = va_arg(args, long long); + } + else if (shift > INT_SHIFT) { + i = va_arg(args, long); + } else { + i = va_arg(args, int); + } + if (i < 0) { + *q++ = '-'; + i = -i; + } + p = q; /* save beginning of digits */ + do { + *q++ = '0' + (i % 10); + i /= 10; + } while (i); + /* reverse digits, stop in middle */ + r = q; /* don't alter q */ + while (--r > p) { + i = *r; + *r = *p; + *p++ = i; + } + } + else if (*fmt == 'c') + *q++ = va_arg(args, int); + else + *q++ = *fmt; + /* now output the saved string */ + for (p = tmp; p < q; ++p) + if (buffer) + *buffer++ = *p; + else + putchar(*p); + } + } + if (buffer) + *buffer = '\0'; +} + +void sprintf(char *buffer, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vsprintf(buffer, fmt, args); + va_end(args); +} + +void printf(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vsprintf(0, fmt, args); + va_end(args); +} diff --git a/purgatory/purgatory.c b/purgatory/purgatory.c new file mode 100644 index 0000000..73930aa --- /dev/null +++ b/purgatory/purgatory.c @@ -0,0 +1,53 @@ + +#include <limits.h> +#include <stdint.h> +#include <purgatory.h> +#include <sha256.h> +#include <string.h> +#include "../kexec/kexec-sha256.h" + +struct sha256_region sha256_regions[SHA256_REGIONS] = {}; +sha256_digest_t sha256_digest = { }; +int skip_checks = 0; + +int verify_sha256_digest(void) +{ + struct sha256_region *ptr, *end; + sha256_digest_t digest; + size_t i; + sha256_context ctx; + sha256_starts(&ctx); + end = &sha256_regions[sizeof(sha256_regions)/sizeof(sha256_regions[0])]; + for(ptr = sha256_regions; ptr < end; ptr++) { + sha256_update(&ctx, (uint8_t *)((uintptr_t)ptr->start), + ptr->len); + } + sha256_finish(&ctx, digest); + if (memcmp(digest, sha256_digest, sizeof(digest)) != 0) { + printf("sha256 digests do not match :(\n"); + printf(" digest: "); + for(i = 0; i < sizeof(digest); i++) { + printf("%hhx ", digest[i]); + } + printf("\n"); + printf("sha256_digest: "); + for(i = 0; i < sizeof(sha256_digest); i++) { + printf("%hhx ", sha256_digest[i]); + } + printf("\n"); + return 1; + } + return 0; +} + +void purgatory(void) +{ + printf("I'm in purgatory\n"); + setup_arch(); + if (!skip_checks && verify_sha256_digest()) { + for(;;) { + /* loop forever */ + } + } + post_verification_setup_arch(); +} diff --git a/purgatory/string.c b/purgatory/string.c new file mode 100644 index 0000000..f06c460 --- /dev/null +++ b/purgatory/string.c @@ -0,0 +1,55 @@ +#include <stddef.h> +#include <string.h> + +size_t strnlen(const char *s, size_t max) +{ + size_t len = 0; + while(len < max && *s) { + len++; + s++; + } + return len; +} + +void* memset(void* s, int c, size_t n) +{ + size_t i; + char *ss = (char*)s; + + for (i=0;i<n;i++) ss[i] = c; + return s; +} + + +void* memcpy(void *dest, const void *src, size_t len) +{ + size_t i; + unsigned char *d; + const unsigned char *s; + d = dest; + s = src; + + for (i=0; i < len; i++) + d[i] = s[i]; + + return dest; +} + + +int memcmp(void *src1, void *src2, size_t len) +{ + unsigned char *s1, *s2; + size_t i; + s1 = src1; + s2 = src2; + for(i = 0; i < len; i++) { + if (*s1 != *s2) { + return *s2 - *s1; + } + s1++; + s2++; + } + return 0; + +} + diff --git a/util/Makefile b/util/Makefile new file mode 100644 index 0000000..948ee63 --- /dev/null +++ b/util/Makefile @@ -0,0 +1,12 @@ +BIN_TO_HEX:= bin/bin-to-hex + +$(BIN_TO_HEX): $(srcdir)/util/bin-to-hex.c + @$(MKDIR) -p $(@D) + $(LINK.o) $(CFLAGS) -o $@ $^ + +$(BIN_TO_HEX): CC=$(BUILD_CC) +$(BIN_TO_HEX): CFLAGS=$(BUILD_CFLAGS) +$(BIN_TO_HEX): LDFLAGS= + +dist += util/Makefile util/bin-to-hex.c +clean += util/bin-to-hex.o $(BIN_TO_HEX) diff --git a/util/bin-to-hex.c b/util/bin-to-hex.c new file mode 100644 index 0000000..48a71e7 --- /dev/null +++ b/util/bin-to-hex.c @@ -0,0 +1,25 @@ +#include <stdio.h> + +int main(int argc, char **argv) +{ + int c; + int i; + const char *name = argv[1]; + printf("#include <stddef.h>\n"); + printf("const char %s[] = {\n", name); + i = 0; + while((c = getchar()) != EOF) { + if ((i % 16) != 0) { + putchar(' '); + } + printf("0x%02x,", c); + i++; + if ((i %16) == 0) { + putchar('\n'); + } + } + putchar('\n'); + printf("};\n"); + printf("size_t %s_size = sizeof(%s);\n", name, name); + return 0; +} diff --git a/util_lib/Makefile b/util_lib/Makefile new file mode 100644 index 0000000..4a6213a --- /dev/null +++ b/util_lib/Makefile @@ -0,0 +1,24 @@ +# +# Utility function library +# +UTIL_LIB_SRCS += +UTIL_LIB_SRCS += util_lib/compute_ip_checksum.c +UTIL_LIB_SRCS += util_lib/elf_info.c +UTIL_LIB_SRCS += util_lib/sha256.c +UTIL_LIB_OBJS =$(call objify, $(UTIL_LIB_SRCS)) +UTIL_LIB_DEPS =$(call depify, $(UTIL_LIB_OBJS)) +UTIL_LIB = libutil.a + +-include $(UTIL_LIB_DEPS) + +dist += util_lib/Makefile $(UTIL_LIB_SRCS) \ + util_lib/include/elf_info.h util_lib/include/sha256.h \ + util_lib/include/ip_checksum.h +clean += $(UTIL_LIB_OBJS) $(UTIL_LIB_DEPS) $(UTIL_LIB) + +$(UTIL_LIB): CPPFLAGS += -I$(srcdir)/util_lib/include + +$(UTIL_LIB): $(UTIL_LIB_OBJS) + @$(MKDIR) -p $(@D) + $(AR) rs $(UTIL_LIB) $(UTIL_LIB_OBJS) + diff --git a/util_lib/compute_ip_checksum.c b/util_lib/compute_ip_checksum.c new file mode 100644 index 0000000..39a371b --- /dev/null +++ b/util_lib/compute_ip_checksum.c @@ -0,0 +1,102 @@ +/* + * kexec: Linux boots Linux + * + * Copyright (C) 2003,2004 Eric Biederman (ebiederm@xmission.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation (version 2 of the License). + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <stdint.h> +#include <ip_checksum.h> +#include <string.h> + +unsigned long compute_ip_checksum(void *addr, unsigned long length) +{ + uint16_t *ptr; + unsigned long sum; + unsigned long len; + unsigned long laddr; + /* compute an ip style checksum */ + laddr = (unsigned long )addr; + sum = 0; + if (laddr & 1) { + uint16_t buffer; + unsigned char *ptr; + /* copy the first byte into a 2 byte buffer. + * This way automatically handles the endian question + * of which byte (low or high) the last byte goes in. + */ + buffer = 0; + ptr = addr; + memcpy(&buffer, ptr, 1); + sum += buffer; + if (sum > 0xFFFF) + sum -= 0xFFFF; + length -= 1; + addr = ptr +1; + + } + len = length >> 1; + ptr = addr; + while (len--) { + sum += *(ptr++); + if (sum > 0xFFFF) + sum -= 0xFFFF; + } + addr = ptr; + if (length & 1) { + uint16_t buffer; + unsigned char *ptr; + /* copy the last byte into a 2 byte buffer. + * This way automatically handles the endian question + * of which byte (low or high) the last byte goes in. + */ + buffer = 0; + ptr = addr; + memcpy(&buffer, ptr, 1); + sum += buffer; + if (sum > 0xFFFF) + sum -= 0xFFFF; + } + return (~sum) & 0xFFFF; + +} + +unsigned long add_ip_checksums(unsigned long offset, unsigned long sum, unsigned long new) +{ + unsigned long checksum; + sum = ~sum & 0xFFFF; + new = ~new & 0xFFFF; + if (offset & 1) { + /* byte swap the sum if it came from an odd offset + * since the computation is endian independant this + * works. + */ + new = ((new >> 8) & 0xff) | ((new << 8) & 0xff00); + } + checksum = sum + new; + if (checksum > 0xFFFF) { + checksum -= 0xFFFF; + } + return (~checksum) & 0xFFFF; +} + +unsigned long negate_ip_checksum(unsigned long sum) +{ + sum = ~sum & 0xFFFF; + + sum = 0xFFFF - sum; + + return ~sum & 0xFFFF; +} diff --git a/util_lib/elf_info.c b/util_lib/elf_info.c new file mode 100644 index 0000000..ce71c60 --- /dev/null +++ b/util_lib/elf_info.c @@ -0,0 +1,1284 @@ +/* + * elf_info.c: Architecture independent code for parsing elf files. + * + * 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 (version 2 of the License). + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <elf_info.h> + +/* The 32bit and 64bit note headers make it clear we don't care */ +typedef Elf32_Nhdr Elf_Nhdr; + +const char *fname; +static Elf64_Ehdr ehdr; +static Elf64_Phdr *phdr; +static int num_pt_loads; + +static char osrelease[4096]; + +/* VMCOREINFO symbols for lockless printk ringbuffer */ +static loff_t prb_vaddr; +static size_t printk_ringbuffer_sz; +static size_t prb_desc_sz; +static size_t printk_info_sz; +static uint64_t printk_ringbuffer_desc_ring_offset; +static uint64_t printk_ringbuffer_text_data_ring_offset; +static uint64_t prb_desc_ring_count_bits_offset; +static uint64_t prb_desc_ring_descs_offset; +static uint64_t prb_desc_ring_infos_offset; +static uint64_t prb_data_ring_size_bits_offset; +static uint64_t prb_data_ring_data_offset; +static uint64_t prb_desc_ring_head_id_offset; +static uint64_t prb_desc_ring_tail_id_offset; +static uint64_t atomic_long_t_counter_offset; +static uint64_t prb_desc_state_var_offset; +static uint64_t prb_desc_info_offset; +static uint64_t prb_desc_text_blk_lpos_offset; +static uint64_t prb_data_blk_lpos_begin_offset; +static uint64_t prb_data_blk_lpos_next_offset; +static uint64_t printk_info_seq_offset; +static uint64_t printk_info_caller_id_offset; +static uint64_t printk_info_ts_nsec_offset; +static uint64_t printk_info_level_offset; +static uint64_t printk_info_text_len_offset; + +static loff_t log_buf_vaddr; +static loff_t log_end_vaddr; +static loff_t log_buf_len_vaddr; +static loff_t logged_chars_vaddr; + +/* record format logs */ +static loff_t log_first_idx_vaddr; +static loff_t log_next_idx_vaddr; + +/* struct printk_log (or older log) size */ +static uint64_t log_sz; + +/* struct printk_log (or older log) field offsets */ +static uint64_t log_offset_ts_nsec = UINT64_MAX; +static uint16_t log_offset_len = UINT16_MAX; +static uint16_t log_offset_text_len = UINT16_MAX; + +static uint64_t phys_offset = UINT64_MAX; + +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define ELFDATANATIVE ELFDATA2LSB +#elif __BYTE_ORDER == __BIG_ENDIAN +#define ELFDATANATIVE ELFDATA2MSB +#else +#error "Unknown machine endian" +#endif + +static uint16_t file16_to_cpu(uint16_t val) +{ + if (ehdr.e_ident[EI_DATA] != ELFDATANATIVE) + val = bswap_16(val); + return val; +} + +static uint32_t file32_to_cpu(uint32_t val) +{ + if (ehdr.e_ident[EI_DATA] != ELFDATANATIVE) + val = bswap_32(val); + return val; +} + +static uint64_t file64_to_cpu(uint64_t val) +{ + if (ehdr.e_ident[EI_DATA] != ELFDATANATIVE) + val = bswap_64(val); + return val; +} + +static uint64_t vaddr_to_offset(uint64_t vaddr) +{ + /* Just hand the simple case where kexec gets + * the virtual address on the program headers right. + */ + ssize_t i; + for (i = 0; i < ehdr.e_phnum; i++) { + if (phdr[i].p_vaddr > vaddr) + continue; + if ((phdr[i].p_vaddr + phdr[i].p_memsz) <= vaddr) + continue; + return (vaddr - phdr[i].p_vaddr) + phdr[i].p_offset; + } + fprintf(stderr, "No program header covering vaddr 0x%llxfound kexec bug?\n", + (unsigned long long)vaddr); + exit(30); +} + +static unsigned machine_pointer_bits(void) +{ + uint8_t bits = 0; + + /* Default to the size of the elf class */ + switch(ehdr.e_ident[EI_CLASS]) { + case ELFCLASS32: bits = 32; break; + case ELFCLASS64: bits = 64; break; + } + + /* Report the architectures pointer size */ + switch(ehdr.e_machine) { + case EM_386: bits = 32; break; + } + + return bits; +} + +void read_elf32(int fd) +{ + Elf32_Ehdr ehdr32; + Elf32_Phdr *phdr32; + size_t phdrs32_size; + ssize_t ret, i; + + ret = pread(fd, &ehdr32, sizeof(ehdr32), 0); + if (ret != sizeof(ehdr32)) { + fprintf(stderr, "Read of Elf header from %s failed: %s\n", + fname, strerror(errno)); + exit(10); + } + + ehdr.e_type = file16_to_cpu(ehdr32.e_type); + ehdr.e_machine = file16_to_cpu(ehdr32.e_machine); + ehdr.e_version = file32_to_cpu(ehdr32.e_version); + ehdr.e_entry = file32_to_cpu(ehdr32.e_entry); + ehdr.e_phoff = file32_to_cpu(ehdr32.e_phoff); + ehdr.e_shoff = file32_to_cpu(ehdr32.e_shoff); + ehdr.e_flags = file32_to_cpu(ehdr32.e_flags); + ehdr.e_ehsize = file16_to_cpu(ehdr32.e_ehsize); + ehdr.e_phentsize = file16_to_cpu(ehdr32.e_phentsize); + ehdr.e_phnum = file16_to_cpu(ehdr32.e_phnum); + ehdr.e_shentsize = file16_to_cpu(ehdr32.e_shentsize); + ehdr.e_shnum = file16_to_cpu(ehdr32.e_shnum); + ehdr.e_shstrndx = file16_to_cpu(ehdr32.e_shstrndx); + + if (ehdr.e_version != EV_CURRENT) { + fprintf(stderr, "Bad Elf header version %u\n", + ehdr.e_version); + exit(11); + } + if (ehdr.e_phentsize != sizeof(Elf32_Phdr)) { + fprintf(stderr, "Bad Elf progra header size %u expected %zu\n", + ehdr.e_phentsize, sizeof(Elf32_Phdr)); + exit(12); + } + phdrs32_size = ehdr.e_phnum * sizeof(Elf32_Phdr); + phdr32 = calloc(ehdr.e_phnum, sizeof(Elf32_Phdr)); + if (!phdr32) { + fprintf(stderr, "Calloc of %u phdrs32 failed: %s\n", + ehdr.e_phnum, strerror(errno)); + exit(14); + } + phdr = calloc(ehdr.e_phnum, sizeof(Elf64_Phdr)); + if (!phdr) { + fprintf(stderr, "Calloc of %u phdrs failed: %s\n", + ehdr.e_phnum, strerror(errno)); + exit(15); + } + ret = pread(fd, phdr32, phdrs32_size, ehdr.e_phoff); + if (ret < 0 || (size_t)ret != phdrs32_size) { + fprintf(stderr, "Read of program header @ 0x%llu for %zu bytes failed: %s\n", + (unsigned long long)ehdr.e_phoff, phdrs32_size, strerror(errno)); + exit(16); + } + for (i = 0; i < ehdr.e_phnum; i++) { + phdr[i].p_type = file32_to_cpu(phdr32[i].p_type); + phdr[i].p_offset = file32_to_cpu(phdr32[i].p_offset); + phdr[i].p_vaddr = file32_to_cpu(phdr32[i].p_vaddr); + phdr[i].p_paddr = file32_to_cpu(phdr32[i].p_paddr); + phdr[i].p_filesz = file32_to_cpu(phdr32[i].p_filesz); + phdr[i].p_memsz = file32_to_cpu(phdr32[i].p_memsz); + phdr[i].p_flags = file32_to_cpu(phdr32[i].p_flags); + phdr[i].p_align = file32_to_cpu(phdr32[i].p_align); + + if (phdr[i].p_type == PT_LOAD) + num_pt_loads++; + } + free(phdr32); +} + +void read_elf64(int fd) +{ + Elf64_Ehdr ehdr64; + Elf64_Phdr *phdr64; + size_t phdrs_size; + ssize_t ret, i; + + ret = pread(fd, &ehdr64, sizeof(ehdr64), 0); + if (ret < 0 || (size_t)ret != sizeof(ehdr)) { + fprintf(stderr, "Read of Elf header from %s failed: %s\n", + fname, strerror(errno)); + exit(10); + } + + ehdr.e_type = file16_to_cpu(ehdr64.e_type); + ehdr.e_machine = file16_to_cpu(ehdr64.e_machine); + ehdr.e_version = file32_to_cpu(ehdr64.e_version); + ehdr.e_entry = file64_to_cpu(ehdr64.e_entry); + ehdr.e_phoff = file64_to_cpu(ehdr64.e_phoff); + ehdr.e_shoff = file64_to_cpu(ehdr64.e_shoff); + ehdr.e_flags = file32_to_cpu(ehdr64.e_flags); + ehdr.e_ehsize = file16_to_cpu(ehdr64.e_ehsize); + ehdr.e_phentsize = file16_to_cpu(ehdr64.e_phentsize); + ehdr.e_phnum = file16_to_cpu(ehdr64.e_phnum); + ehdr.e_shentsize = file16_to_cpu(ehdr64.e_shentsize); + ehdr.e_shnum = file16_to_cpu(ehdr64.e_shnum); + ehdr.e_shstrndx = file16_to_cpu(ehdr64.e_shstrndx); + + if (ehdr.e_version != EV_CURRENT) { + fprintf(stderr, "Bad Elf header version %u\n", + ehdr.e_version); + exit(11); + } + if (ehdr.e_phentsize != sizeof(Elf64_Phdr)) { + fprintf(stderr, "Bad Elf progra header size %u expected %zu\n", + ehdr.e_phentsize, sizeof(Elf64_Phdr)); + exit(12); + } + phdrs_size = ehdr.e_phnum * sizeof(Elf64_Phdr); + phdr64 = calloc(ehdr.e_phnum, sizeof(Elf64_Phdr)); + if (!phdr64) { + fprintf(stderr, "Calloc of %u phdrs64 failed: %s\n", + ehdr.e_phnum, strerror(errno)); + exit(14); + } + phdr = calloc(ehdr.e_phnum, sizeof(Elf64_Phdr)); + if (!phdr) { + fprintf(stderr, "Calloc of %u phdrs failed: %s\n", + ehdr.e_phnum, strerror(errno)); + exit(15); + } + ret = pread(fd, phdr64, phdrs_size, ehdr.e_phoff); + if (ret < 0 || (size_t)ret != phdrs_size) { + fprintf(stderr, "Read of program header @ %llu for %zu bytes failed: %s\n", + (unsigned long long)(ehdr.e_phoff), phdrs_size, strerror(errno)); + exit(16); + } + for (i = 0; i < ehdr.e_phnum; i++) { + phdr[i].p_type = file32_to_cpu(phdr64[i].p_type); + phdr[i].p_flags = file32_to_cpu(phdr64[i].p_flags); + phdr[i].p_offset = file64_to_cpu(phdr64[i].p_offset); + phdr[i].p_vaddr = file64_to_cpu(phdr64[i].p_vaddr); + phdr[i].p_paddr = file64_to_cpu(phdr64[i].p_paddr); + phdr[i].p_filesz = file64_to_cpu(phdr64[i].p_filesz); + phdr[i].p_memsz = file64_to_cpu(phdr64[i].p_memsz); + phdr[i].p_align = file64_to_cpu(phdr64[i].p_align); + + if (phdr[i].p_type == PT_LOAD) + num_pt_loads++; + } + free(phdr64); +} + +int get_pt_load(int idx, + unsigned long long *phys_start, + unsigned long long *phys_end, + unsigned long long *virt_start, + unsigned long long *virt_end) +{ + Elf64_Phdr *pls; + + if (num_pt_loads <= idx) + return 0; + + pls = &phdr[idx]; + + if (phys_start) + *phys_start = pls->p_paddr; + if (phys_end) + *phys_end = pls->p_paddr + pls->p_memsz; + if (virt_start) + *virt_start = pls->p_vaddr; + if (virt_end) + *virt_end = pls->p_vaddr + pls->p_memsz; + + return 1; +} + +#define NOT_FOUND_LONG_VALUE (-1) + +void (*arch_scan_vmcoreinfo)(char *pos); + +void scan_vmcoreinfo(char *start, size_t size) +{ + char *last = start + size - 1; + char *pos, *eol; + char temp_buf[1024]; + bool last_line = false; + char *str, *endp; + +#define SYMBOL(sym) { \ + .str = "SYMBOL(" #sym ")=", \ + .name = #sym, \ + .len = sizeof("SYMBOL(" #sym ")=") - 1, \ + .vaddr = & sym ## _vaddr, \ + } + static struct symbol { + const char *str; + const char *name; + size_t len; + loff_t *vaddr; + } symbol[] = { + SYMBOL(prb), + SYMBOL(log_buf), + SYMBOL(log_end), + SYMBOL(log_buf_len), + SYMBOL(logged_chars), + SYMBOL(log_first_idx), + SYMBOL(log_next_idx), + }; + + for (pos = start; pos <= last; pos = eol + 1) { + size_t len, i; + /* Find the end of the current line */ + for (eol = pos; (eol <= last) && (*eol != '\n') ; eol++) + ; + if (eol > last) { + /* + * We did not find \n and note ended. Currently kernel + * is appending last field CRASH_TIME without \n. It + * is ugly but handle it. + */ + eol = last; + len = eol - pos + 1; + if (len >= sizeof(temp_buf)) + len = sizeof(temp_buf) - 1; + strncpy(temp_buf, pos, len); + temp_buf[len + 1] = '\0'; + + pos = temp_buf; + len = len + 1; + eol = pos + len -1; + last_line = true; + } else { + len = eol - pos + 1; + } + + /* Stomp the last character so I am guaranteed a terminating null */ + *eol = '\0'; + /* Copy OSRELEASE if I see it */ + if ((len >= 10) && (memcmp("OSRELEASE=", pos, 10) == 0)) { + size_t to_copy = len - 10; + if (to_copy >= sizeof(osrelease)) + to_copy = sizeof(osrelease) - 1; + memcpy(osrelease, pos + 10, to_copy); + osrelease[to_copy] = '\0'; + } + /* See if the line is mentions a symbol I am looking for */ + for (i = 0; i < sizeof(symbol)/sizeof(symbol[0]); i++ ) { + unsigned long long vaddr; + if (symbol[i].len >= len) + continue; + if (memcmp(symbol[i].str, pos, symbol[i].len) != 0) + continue; + /* Found a symbol now decode it */ + vaddr = strtoull(pos + symbol[i].len, NULL, 16); + /* Remember the virtual address */ + *symbol[i].vaddr = vaddr; + } + + str = "SIZE(printk_ringbuffer)="; + if (memcmp(str, pos, strlen(str)) == 0) + printk_ringbuffer_sz = strtoull(pos + strlen(str), + NULL, 10); + + str = "SIZE(prb_desc)="; + if (memcmp(str, pos, strlen(str)) == 0) + prb_desc_sz = strtoull(pos + strlen(str), NULL, 10); + + str = "SIZE(printk_info)="; + if (memcmp(str, pos, strlen(str)) == 0) + printk_info_sz = strtoull(pos + strlen(str), NULL, 10); + + str = "OFFSET(printk_ringbuffer.desc_ring)="; + if (memcmp(str, pos, strlen(str)) == 0) + printk_ringbuffer_desc_ring_offset = + strtoull(pos + strlen(str), NULL, 10); + + str = "OFFSET(printk_ringbuffer.text_data_ring)="; + if (memcmp(str, pos, strlen(str)) == 0) + printk_ringbuffer_text_data_ring_offset = + strtoull(pos + strlen(str), NULL, 10); + + str = "OFFSET(prb_desc_ring.count_bits)="; + if (memcmp(str, pos, strlen(str)) == 0) + prb_desc_ring_count_bits_offset = + strtoull(pos + strlen(str), NULL, 10); + + str = "OFFSET(prb_desc_ring.descs)="; + if (memcmp(str, pos, strlen(str)) == 0) + prb_desc_ring_descs_offset = + strtoull(pos + strlen(str), NULL, 10); + + str = "OFFSET(prb_desc_ring.infos)="; + if (memcmp(str, pos, strlen(str)) == 0) + prb_desc_ring_infos_offset = + strtoull(pos + strlen(str), NULL, 10); + + str = "OFFSET(prb_data_ring.size_bits)="; + if (memcmp(str, pos, strlen(str)) == 0) + prb_data_ring_size_bits_offset = + strtoull(pos + strlen(str), NULL, 10); + + str = "OFFSET(prb_data_ring.data)="; + if (memcmp(str, pos, strlen(str)) == 0) + prb_data_ring_data_offset = + strtoull(pos + strlen(str), NULL, 10); + + str = "OFFSET(prb_desc_ring.head_id)="; + if (memcmp(str, pos, strlen(str)) == 0) + prb_desc_ring_head_id_offset = + strtoull(pos + strlen(str), NULL, 10); + + str = "OFFSET(prb_desc_ring.tail_id)="; + if (memcmp(str, pos, strlen(str)) == 0) + prb_desc_ring_tail_id_offset = + strtoull(pos + strlen(str), NULL, 10); + + str = "OFFSET(atomic_long_t.counter)="; + if (memcmp(str, pos, strlen(str)) == 0) + atomic_long_t_counter_offset = + strtoull(pos + strlen(str), NULL, 10); + + str = "OFFSET(prb_desc.state_var)="; + if (memcmp(str, pos, strlen(str)) == 0) + prb_desc_state_var_offset = + strtoull(pos + strlen(str), NULL, 10); + + str = "OFFSET(prb_desc.info)="; + if (memcmp(str, pos, strlen(str)) == 0) + prb_desc_info_offset = + strtoull(pos + strlen(str), NULL, 10); + + str = "OFFSET(prb_desc.text_blk_lpos)="; + if (memcmp(str, pos, strlen(str)) == 0) + prb_desc_text_blk_lpos_offset = + strtoull(pos + strlen(str), NULL, 10); + + str = "OFFSET(prb_data_blk_lpos.begin)="; + if (memcmp(str, pos, strlen(str)) == 0) + prb_data_blk_lpos_begin_offset = + strtoull(pos + strlen(str), NULL, 10); + + str = "OFFSET(prb_data_blk_lpos.next)="; + if (memcmp(str, pos, strlen(str)) == 0) + prb_data_blk_lpos_next_offset = + strtoull(pos + strlen(str), NULL, 10); + + str = "OFFSET(printk_info.seq)="; + if (memcmp(str, pos, strlen(str)) == 0) + printk_info_seq_offset = + strtoull(pos + strlen(str), NULL, 10); + + str = "OFFSET(printk_info.caller_id)="; + if (memcmp(str, pos, strlen(str)) == 0) + printk_info_caller_id_offset = + strtoull(pos + strlen(str), NULL, 10); + + str = "OFFSET(printk_info.ts_nsec)="; + if (memcmp(str, pos, strlen(str)) == 0) + printk_info_ts_nsec_offset = + strtoull(pos + strlen(str), NULL, 10); + + str = "OFFSET(printk_info.level)="; + if (memcmp(str, pos, strlen(str)) == 0) + printk_info_level_offset = + strtoull(pos + strlen(str), NULL, 10); + + str = "OFFSET(printk_info.text_len)="; + if (memcmp(str, pos, strlen(str)) == 0) + printk_info_text_len_offset = + strtoull(pos + strlen(str), NULL, 10); + + /* Check for "SIZE(printk_log)" or older "SIZE(log)=" */ + str = "SIZE(log)="; + if (memcmp(str, pos, strlen(str)) == 0) + log_sz = strtoull(pos + strlen(str), NULL, 10); + + str = "SIZE(printk_log)="; + if (memcmp(str, pos, strlen(str)) == 0) + log_sz = strtoull(pos + strlen(str), NULL, 10); + + /* Check for struct printk_log (or older log) field offsets */ + str = "OFFSET(log.ts_nsec)="; + if (memcmp(str, pos, strlen(str)) == 0) + log_offset_ts_nsec = strtoull(pos + strlen(str), NULL, + 10); + str = "OFFSET(printk_log.ts_nsec)="; + if (memcmp(str, pos, strlen(str)) == 0) + log_offset_ts_nsec = strtoull(pos + strlen(str), NULL, + 10); + + str = "OFFSET(log.len)="; + if (memcmp(str, pos, strlen(str)) == 0) + log_offset_len = strtoul(pos + strlen(str), NULL, 10); + + str = "OFFSET(printk_log.len)="; + if (memcmp(str, pos, strlen(str)) == 0) + log_offset_len = strtoul(pos + strlen(str), NULL, 10); + + str = "OFFSET(log.text_len)="; + if (memcmp(str, pos, strlen(str)) == 0) + log_offset_text_len = strtoul(pos + strlen(str), NULL, + 10); + str = "OFFSET(printk_log.text_len)="; + if (memcmp(str, pos, strlen(str)) == 0) + log_offset_text_len = strtoul(pos + strlen(str), NULL, + 10); + + /* Check for PHYS_OFFSET number */ + str = "NUMBER(PHYS_OFFSET)="; + if (memcmp(str, pos, strlen(str)) == 0) { + phys_offset = strtoul(pos + strlen(str), &endp, + 10); + if (strlen(endp) != 0) + phys_offset = strtoul(pos + strlen(str), &endp, 16); + if ((phys_offset == LONG_MAX) || strlen(endp) != 0) { + fprintf(stderr, "Invalid data %s\n", + pos); + break; + } + } + + if (arch_scan_vmcoreinfo != NULL) + (*arch_scan_vmcoreinfo)(pos); + + if (last_line) + break; + } +} + +static int scan_notes(int fd, loff_t start, loff_t lsize) +{ + char *buf, *last, *note, *next; + size_t size; + ssize_t ret; + + if (lsize > SSIZE_MAX) { + fprintf(stderr, "Unable to handle note section of %llu bytes\n", + (unsigned long long)lsize); + exit(20); + } + + size = lsize; + buf = malloc(size); + if (!buf) { + fprintf(stderr, "Cannot malloc %zu bytes\n", size); + exit(21); + } + last = buf + size - 1; + ret = pread(fd, buf, size, start); + if (ret != (ssize_t)size) { + fprintf(stderr, "Cannot read note section @ 0x%llx of %zu bytes: %s\n", + (unsigned long long)start, size, strerror(errno)); + exit(22); + } + + for (note = buf; (note + sizeof(Elf_Nhdr)) < last; note = next) + { + Elf_Nhdr *hdr; + char *n_name, *n_desc; + size_t n_namesz, n_descsz, n_type; + + hdr = (Elf_Nhdr *)note; + n_namesz = file32_to_cpu(hdr->n_namesz); + n_descsz = file32_to_cpu(hdr->n_descsz); + n_type = file32_to_cpu(hdr->n_type); + + n_name = note + sizeof(*hdr); + n_desc = n_name + ((n_namesz + 3) & ~3); + next = n_desc + ((n_descsz + 3) & ~3); + + if (next > (last + 1)) + break; + + if ((memcmp(n_name, "VMCOREINFO", 11) != 0) || (n_type != 0)) + continue; + scan_vmcoreinfo(n_desc, n_descsz); + } + + if ((note + sizeof(Elf_Nhdr)) == last) { + free(buf); + return -1; + } + + free(buf); + + return 0; +} + +static int scan_note_headers(int fd) +{ + int i, ret = 0; + + for (i = 0; i < ehdr.e_phnum; i++) { + if (phdr[i].p_type != PT_NOTE) + continue; + ret = scan_notes(fd, phdr[i].p_offset, phdr[i].p_filesz); + } + + return ret; +} + +static uint64_t read_file_pointer(int fd, uint64_t addr) +{ + uint64_t result; + ssize_t ret; + + if (machine_pointer_bits() == 64) { + uint64_t scratch; + ret = pread(fd, &scratch, sizeof(scratch), addr); + if (ret != sizeof(scratch)) { + fprintf(stderr, "Failed to read pointer @ 0x%llx: %s\n", + (unsigned long long)addr, strerror(errno)); + exit(40); + } + result = file64_to_cpu(scratch); + } else { + uint32_t scratch; + ret = pread(fd, &scratch, sizeof(scratch), addr); + if (ret != sizeof(scratch)) { + fprintf(stderr, "Failed to read pointer @ 0x%llx: %s\n", + (unsigned long long)addr, strerror(errno)); + exit(40); + } + result = file32_to_cpu(scratch); + } + return result; +} + +static uint32_t read_file_u32(int fd, uint64_t addr) +{ + uint32_t scratch; + ssize_t ret; + ret = pread(fd, &scratch, sizeof(scratch), addr); + if (ret != sizeof(scratch)) { + fprintf(stderr, "Failed to read value @ 0x%llx: %s\n", + (unsigned long long)addr, strerror(errno)); + exit(41); + } + return file32_to_cpu(scratch); +} + +static int32_t read_file_s32(int fd, uint64_t addr) +{ + return read_file_u32(fd, addr); +} + +static void dump_dmesg_legacy(int fd, void (*handler)(char*, unsigned int)) +{ + uint64_t log_buf, log_buf_offset; + unsigned log_end, logged_chars, log_end_wrapped; + int log_buf_len, to_wrap; + char *buf; + ssize_t ret; + + if (!log_buf_vaddr) { + fprintf(stderr, "Missing the log_buf symbol\n"); + exit(50); + } + if (!log_end_vaddr) { + fprintf(stderr, "Missing the log_end symbol\n"); + exit(51); + } + if (!log_buf_len_vaddr) { + fprintf(stderr, "Missing the log_bug_len symbol\n"); + exit(52); + } + if (!logged_chars_vaddr) { + fprintf(stderr, "Missing the logged_chars symbol\n"); + exit(53); + } + + log_buf = read_file_pointer(fd, vaddr_to_offset(log_buf_vaddr)); + log_end = read_file_u32(fd, vaddr_to_offset(log_end_vaddr)); + log_buf_len = read_file_s32(fd, vaddr_to_offset(log_buf_len_vaddr)); + logged_chars = read_file_u32(fd, vaddr_to_offset(logged_chars_vaddr)); + + log_buf_offset = vaddr_to_offset(log_buf); + + buf = calloc(1, log_buf_len); + if (!buf) { + fprintf(stderr, "Failed to malloc %d bytes for the logbuf: %s\n", + log_buf_len, strerror(errno)); + exit(51); + } + + log_end_wrapped = log_end % log_buf_len; + to_wrap = log_buf_len - log_end_wrapped; + + ret = pread(fd, buf, to_wrap, log_buf_offset + log_end_wrapped); + if (ret != to_wrap) { + fprintf(stderr, "Failed to read the first half of the log buffer: %s\n", + strerror(errno)); + exit(52); + } + ret = pread(fd, buf + to_wrap, log_end_wrapped, log_buf_offset); + if (ret != log_end_wrapped) { + fprintf(stderr, "Faield to read the second half of the log buffer: %s\n", + strerror(errno)); + exit(53); + } + + /* + * To collect full dmesg including the part before `dmesg -c` is useful + * for later debugging. Use same logic as what crash utility is using. + */ + logged_chars = log_end < log_buf_len ? log_end : log_buf_len; + + if (handler) + handler(buf + (log_buf_len - logged_chars), logged_chars); +} + +static inline uint16_t struct_val_u16(char *ptr, unsigned int offset) +{ + return(file16_to_cpu(*(uint16_t *)(ptr + offset))); +} + +static inline uint32_t struct_val_u32(char *ptr, unsigned int offset) +{ + return(file32_to_cpu(*(uint32_t *)(ptr + offset))); +} + +static inline uint64_t struct_val_u64(char *ptr, unsigned int offset) +{ + return(file64_to_cpu(*(uint64_t *)(ptr + offset))); +} + +/* Read headers of log records and dump accordingly */ +static void dump_dmesg_structured(int fd, void (*handler)(char*, unsigned int)) +{ +#define OUT_BUF_SIZE 4096 + uint64_t log_buf, log_buf_offset, ts_nsec; + uint32_t log_buf_len, log_first_idx, log_next_idx, current_idx, len = 0, i; + char *buf, out_buf[OUT_BUF_SIZE]; + bool has_wrapped_around = false; + ssize_t ret; + char *msg; + uint16_t text_len; + imaxdiv_t imaxdiv_sec, imaxdiv_usec; + + if (!log_buf_vaddr) { + fprintf(stderr, "Missing the log_buf symbol\n"); + exit(60); + } + + if (!log_buf_len_vaddr) { + fprintf(stderr, "Missing the log_bug_len symbol\n"); + exit(61); + } + + if (!log_first_idx_vaddr) { + fprintf(stderr, "Missing the log_first_idx symbol\n"); + exit(62); + } + + if (!log_next_idx_vaddr) { + fprintf(stderr, "Missing the log_next_idx symbol\n"); + exit(63); + } + + if (!log_sz) { + fprintf(stderr, "Missing the struct log size export\n"); + exit(64); + } + + if (log_offset_ts_nsec == UINT64_MAX) { + fprintf(stderr, "Missing the log.ts_nsec offset export\n"); + exit(65); + } + + if (log_offset_len == UINT16_MAX) { + fprintf(stderr, "Missing the log.len offset export\n"); + exit(66); + } + + if (log_offset_text_len == UINT16_MAX) { + fprintf(stderr, "Missing the log.text_len offset export\n"); + exit(67); + } + + log_buf = read_file_pointer(fd, vaddr_to_offset(log_buf_vaddr)); + log_buf_len = read_file_s32(fd, vaddr_to_offset(log_buf_len_vaddr)); + + log_first_idx = read_file_u32(fd, vaddr_to_offset(log_first_idx_vaddr)); + log_next_idx = read_file_u32(fd, vaddr_to_offset(log_next_idx_vaddr)); + + log_buf_offset = vaddr_to_offset(log_buf); + + buf = calloc(1, log_sz); + if (!buf) { + fprintf(stderr, "Failed to malloc %" PRId64 " bytes for the log:" + " %s\n", log_sz, strerror(errno)); + exit(64); + } + + /* Parse records and write out data at standard output */ + + current_idx = log_first_idx; + len = 0; + while (current_idx != log_next_idx) { + uint16_t loglen; + + ret = pread(fd, buf, log_sz, log_buf_offset + current_idx); + if (ret != log_sz) { + fprintf(stderr, "Failed to read log of size %" PRId64 " bytes:" + " %s\n", log_sz, strerror(errno)); + exit(65); + } + ts_nsec = struct_val_u64(buf, log_offset_ts_nsec); + imaxdiv_sec = imaxdiv(ts_nsec, 1000000000); + imaxdiv_usec = imaxdiv(imaxdiv_sec.rem, 1000); + + len += sprintf(out_buf + len, "[%5llu.%06llu] ", + (long long unsigned int)imaxdiv_sec.quot, + (long long unsigned int)imaxdiv_usec.quot); + + /* escape non-printable characters */ + text_len = struct_val_u16(buf, log_offset_text_len); + msg = calloc(1, text_len); + if (!msg) { + fprintf(stderr, "Failed to malloc %u bytes for log text:" + " %s\n", text_len, strerror(errno)); + exit(64); + } + + ret = pread(fd, msg, text_len, log_buf_offset + current_idx + log_sz); + if (ret != text_len) { + fprintf(stderr, "Failed to read log text of size %u bytes:" + " %s\n", text_len, strerror(errno)); + exit(65); + } + for (i = 0; i < text_len; i++) { + unsigned char c = msg[i]; + + if (!isprint(c) && !isspace(c)) + len += sprintf(out_buf + len, "\\x%02x", c); + else + out_buf[len++] = c; + + if (len >= OUT_BUF_SIZE - 64) { + if (handler) + handler(out_buf, len); + len = 0; + } + } + + out_buf[len++] = '\n'; + free(msg); + /* + * A length == 0 record is the end of buffer marker. Wrap around + * and read the message at the start of the buffer. + */ + loglen = struct_val_u16(buf, log_offset_len); + if (!loglen) { + if (has_wrapped_around) { + if (len && handler) + handler(out_buf, len); + fprintf(stderr, "Cycle when parsing dmesg detected.\n"); + fprintf(stderr, "The prink log_buf is most likely corrupted.\n"); + fprintf(stderr, "log_buf = 0x%lx, idx = 0x%x\n", + log_buf, current_idx); + exit(68); + } + current_idx = 0; + has_wrapped_around = true; + } else { + /* Move to next record */ + current_idx += loglen; + if(current_idx > log_buf_len - log_sz) { + if (len && handler) + handler(out_buf, len); + fprintf(stderr, "Index outside log_buf detected.\n"); + fprintf(stderr, "The prink log_buf is most likely corrupted.\n"); + fprintf(stderr, "log_buf = 0x%lx, idx = 0x%x\n", + log_buf, current_idx); + exit(69); + } + } + } + free(buf); + if (len && handler) + handler(out_buf, len); +} + +/* convenience struct for passing many values to helper functions */ +struct prb_map { + char *prb; + + char *desc_ring; + unsigned long desc_ring_count; + char *descs; + + char *infos; + + char *text_data_ring; + unsigned long text_data_ring_size; + char *text_data; +}; + +/* + * desc_state and DESC_* definitions taken from kernel source: + * + * kernel/printk/printk_ringbuffer.h + * + * DESC_* definitions modified to provide 32-bit and 64-bit variants. + */ + +/* The possible responses of a descriptor state-query. */ +enum desc_state { + desc_miss = -1, /* ID mismatch (pseudo state) */ + desc_reserved = 0x0, /* reserved, in use by writer */ + desc_committed = 0x1, /* committed by writer, could get reopened */ + desc_finalized = 0x2, /* committed, no further modification allowed */ + desc_reusable = 0x3, /* free, not yet used by any writer */ +}; + +#define DESC_SV_BITS (sizeof(uint64_t) * 8) +#define DESC_FLAGS_SHIFT (DESC_SV_BITS - 2) +#define DESC_FLAGS_MASK (3ULL << DESC_FLAGS_SHIFT) +#define DESC_STATE(sv) (3ULL & (sv >> DESC_FLAGS_SHIFT)) +#define DESC_ID_MASK (~DESC_FLAGS_MASK) +#define DESC_ID(sv) ((sv) & DESC_ID_MASK) + +#define DESC32_SV_BITS (sizeof(uint32_t) * 8) +#define DESC32_FLAGS_SHIFT (DESC32_SV_BITS - 2) +#define DESC32_FLAGS_MASK (3UL << DESC32_FLAGS_SHIFT) +#define DESC32_STATE(sv) (3UL & (sv >> DESC32_FLAGS_SHIFT)) +#define DESC32_ID_MASK (~DESC32_FLAGS_MASK) +#define DESC32_ID(sv) ((sv) & DESC32_ID_MASK) + +/* + * get_desc_state() taken from kernel source: + * + * kernel/printk/printk_ringbuffer.c + * + * get_desc32_state() added as 32-bit variant. + */ + +/* Query the state of a descriptor. */ +static enum desc_state get_desc_state(unsigned long id, + uint64_t state_val) +{ + if (id != DESC_ID(state_val)) + return desc_miss; + + return DESC_STATE(state_val); +} + +static enum desc_state get_desc32_state(unsigned long id, + uint64_t state_val) +{ + if (id != DESC32_ID(state_val)) + return desc_miss; + + return DESC32_STATE(state_val); +} + +static bool record_committed(unsigned long id, uint64_t state_var) +{ + enum desc_state state; + + if (machine_pointer_bits() == 32) + state = get_desc32_state(id, state_var); + else + state = get_desc_state(id, state_var); + + return (state == desc_committed || state == desc_finalized); +} + +static uint64_t id_inc(uint64_t id) +{ + id++; + + if (machine_pointer_bits() == 32) + return (id & DESC32_ID_MASK); + + return (id & DESC_ID_MASK); +} + +static uint64_t get_ulong(char *addr) +{ + if (machine_pointer_bits() == 32) + return struct_val_u32(addr, 0); + return struct_val_u64(addr, 0); +} + +static uint64_t sizeof_ulong(void) +{ + return (machine_pointer_bits() >> 3); +} + +static void dump_record(struct prb_map *m, unsigned long id, + void (*handler)(char*, unsigned int)) +{ +#define OUT_BUF_SIZE 4096 + char out_buf[OUT_BUF_SIZE]; + imaxdiv_t imaxdiv_usec; + imaxdiv_t imaxdiv_sec; + uint32_t offset = 0; + unsigned short len; + uint64_t state_var; + uint64_t ts_nsec; + uint64_t begin; + uint64_t next; + char *info; + char *text; + char *desc; + int i; + + desc = m->descs + ((id % m->desc_ring_count) * prb_desc_sz); + info = m->infos + ((id % m->desc_ring_count) * printk_info_sz); + + /* skip non-committed record */ + state_var = get_ulong(desc + prb_desc_state_var_offset + + atomic_long_t_counter_offset); + if (!record_committed(id, state_var)) + return; + + begin = get_ulong(desc + prb_desc_text_blk_lpos_offset + + prb_data_blk_lpos_begin_offset) % + m->text_data_ring_size; + next = get_ulong(desc + prb_desc_text_blk_lpos_offset + + prb_data_blk_lpos_next_offset) % + m->text_data_ring_size; + + ts_nsec = struct_val_u64(info, printk_info_ts_nsec_offset); + imaxdiv_sec = imaxdiv(ts_nsec, 1000000000); + imaxdiv_usec = imaxdiv(imaxdiv_sec.rem, 1000); + + offset += sprintf(out_buf + offset, "[%5llu.%06llu] ", + (long long unsigned int)imaxdiv_sec.quot, + (long long unsigned int)imaxdiv_usec.quot); + + /* skip data-less text blocks */ + if (begin == next) + goto out; + + len = struct_val_u16(info, printk_info_text_len_offset); + + /* handle wrapping data block */ + if (begin > next) + begin = 0; + + /* skip over descriptor ID */ + begin += sizeof_ulong(); + + /* handle truncated messages */ + if (next - begin < len) + len = next - begin; + + text = m->text_data + begin; + + /* escape non-printable characters */ + for (i = 0; i < len; i++) { + unsigned char c = text[i]; + + if (!isprint(c) && !isspace(c)) + offset += sprintf(out_buf + offset, "\\x%02x", c); + else + out_buf[offset++] = c; + + if (offset >= OUT_BUF_SIZE - 64) { + if (handler) + handler(out_buf, offset); + offset = 0; + } + } +out: + out_buf[offset++] = '\n'; + + if (offset && handler) + handler(out_buf, offset); +} + +/* + * Handle the lockless printk_ringbuffer. + */ +static void dump_dmesg_lockless(int fd, void (*handler)(char*, unsigned int)) +{ + struct prb_map m; + uint64_t head_id; + uint64_t tail_id; + uint64_t kaddr; + uint64_t id; + int ret; + + /* setup printk_ringbuffer */ + kaddr = read_file_pointer(fd, vaddr_to_offset(prb_vaddr)); + m.prb = calloc(1, printk_ringbuffer_sz); + if (!m.prb) { + fprintf(stderr, "Failed to malloc %zu bytes for prb: %s\n", + printk_ringbuffer_sz, strerror(errno)); + exit(64); + } + ret = pread(fd, m.prb, printk_ringbuffer_sz, vaddr_to_offset(kaddr)); + if (ret != printk_ringbuffer_sz) { + fprintf(stderr, "Failed to read prb of size %zu bytes: %s\n", + printk_ringbuffer_sz, strerror(errno)); + exit(65); + } + + /* setup descriptor ring */ + m.desc_ring = m.prb + printk_ringbuffer_desc_ring_offset; + m.desc_ring_count = 1 << struct_val_u32(m.desc_ring, + prb_desc_ring_count_bits_offset); + kaddr = get_ulong(m.desc_ring + prb_desc_ring_descs_offset); + m.descs = calloc(1, prb_desc_sz * m.desc_ring_count); + if (!m.descs) { + fprintf(stderr, "Failed to malloc %lu bytes for descs: %s\n", + prb_desc_sz * m.desc_ring_count, strerror(errno)); + exit(64); + } + ret = pread(fd, m.descs, prb_desc_sz * m.desc_ring_count, + vaddr_to_offset(kaddr)); + if (ret != prb_desc_sz * m.desc_ring_count) { + fprintf(stderr, + "Failed to read descs of size %lu bytes: %s\n", + prb_desc_sz * m.desc_ring_count, strerror(errno)); + exit(65); + } + + /* setup info ring */ + kaddr = get_ulong(m.prb + prb_desc_ring_infos_offset); + m.infos = calloc(1, printk_info_sz * m.desc_ring_count); + if (!m.infos) { + fprintf(stderr, "Failed to malloc %lu bytes for infos: %s\n", + printk_info_sz * m.desc_ring_count, strerror(errno)); + exit(64); + } + ret = pread(fd, m.infos, printk_info_sz * m.desc_ring_count, + vaddr_to_offset(kaddr)); + if (ret != printk_info_sz * m.desc_ring_count) { + fprintf(stderr, + "Failed to read infos of size %lu bytes: %s\n", + printk_info_sz * m.desc_ring_count, strerror(errno)); + exit(65); + } + + /* setup text data ring */ + m.text_data_ring = m.prb + printk_ringbuffer_text_data_ring_offset; + m.text_data_ring_size = 1 << struct_val_u32(m.text_data_ring, + prb_data_ring_size_bits_offset); + kaddr = get_ulong(m.text_data_ring + prb_data_ring_data_offset); + m.text_data = calloc(1, m.text_data_ring_size); + if (!m.text_data) { + fprintf(stderr, + "Failed to malloc %lu bytes for text_data: %s\n", + m.text_data_ring_size, strerror(errno)); + exit(64); + } + ret = pread(fd, m.text_data, m.text_data_ring_size, + vaddr_to_offset(kaddr)); + if (ret != m.text_data_ring_size) { + fprintf(stderr, + "Failed to read text_data of size %lu bytes: %s\n", + m.text_data_ring_size, strerror(errno)); + exit(65); + } + + /* ready to go */ + + tail_id = get_ulong(m.desc_ring + prb_desc_ring_tail_id_offset + + atomic_long_t_counter_offset); + head_id = get_ulong(m.desc_ring + prb_desc_ring_head_id_offset + + atomic_long_t_counter_offset); + + for (id = tail_id; id != head_id; id = id_inc(id)) + dump_record(&m, id, handler); + + /* dump head record */ + dump_record(&m, id, handler); + + free(m.text_data); + free(m.infos); + free(m.descs); + free(m.prb); +} + +void dump_dmesg(int fd, void (*handler)(char*, unsigned int)) +{ + if (prb_vaddr) + dump_dmesg_lockless(fd, handler); + else if (log_first_idx_vaddr) + dump_dmesg_structured(fd, handler); + else + dump_dmesg_legacy(fd, handler); +} + +int read_elf(int fd) +{ + int ret; + + ret = pread(fd, ehdr.e_ident, EI_NIDENT, 0); + if (ret != EI_NIDENT) { + fprintf(stderr, "Read of e_ident from %s failed: %s\n", + fname, strerror(errno)); + return 3; + } + if (memcmp(ehdr.e_ident, ELFMAG, SELFMAG) != 0) { + fprintf(stderr, "Missing elf signature\n"); + return 4; + } + if (ehdr.e_ident[EI_VERSION] != EV_CURRENT) { + fprintf(stderr, "Bad elf version\n"); + return 5; + } + if ((ehdr.e_ident[EI_CLASS] != ELFCLASS32) && + (ehdr.e_ident[EI_CLASS] != ELFCLASS64)) + { + fprintf(stderr, "Unknown elf class %u\n", + ehdr.e_ident[EI_CLASS]); + return 6; + } + if ((ehdr.e_ident[EI_DATA] != ELFDATA2LSB) && + (ehdr.e_ident[EI_DATA] != ELFDATA2MSB)) + { + fprintf(stderr, "Unkown elf data order %u\n", + ehdr.e_ident[EI_DATA]); + return 7; + } + if (ehdr.e_ident[EI_CLASS] == ELFCLASS32) + read_elf32(fd); + else + read_elf64(fd); + + ret = scan_note_headers(fd); + if (ret < 0) + return ret; + + return 0; +} + +int read_phys_offset_elf_kcore(int fd, long *phys_off) +{ + int ret; + + *phys_off = ULONG_MAX; + + ret = read_elf(fd); + if (!ret) { + /* If we have a valid 'PHYS_OFFSET' by now, + * return it to the caller now. + */ + if (phys_offset != UINT64_MAX) { + *phys_off = phys_offset; + return ret; + } + } + + return 2; +} diff --git a/util_lib/include/elf_info.h b/util_lib/include/elf_info.h new file mode 100644 index 0000000..fdf4c3d --- /dev/null +++ b/util_lib/include/elf_info.h @@ -0,0 +1,36 @@ +#ifndef ELF_INFO_H +#define ELF_INFO_H + +#define _XOPEN_SOURCE 700 +#define _GNU_SOURCE +#define _LARGEFILE_SOURCE 1 +#define _FILE_OFFSET_BITS 64 + +#include <endian.h> +#include <byteswap.h> +#include <stdio.h> +#include <stdint.h> +#include <errno.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <limits.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <elf.h> +#include <stdbool.h> +#include <inttypes.h> +#include <ctype.h> + +int get_pt_load(int idx, + unsigned long long *phys_start, + unsigned long long *phys_end, + unsigned long long *virt_start, + unsigned long long *virt_end); +int read_phys_offset_elf_kcore(int fd, long *phys_off); +int read_elf(int fd); +void dump_dmesg(int fd, void (*handler)(char*, unsigned int)); +extern void (*arch_scan_vmcoreinfo)(char *pos); + +#endif /* ELF_INFO_H */ diff --git a/util_lib/include/ip_checksum.h b/util_lib/include/ip_checksum.h new file mode 100644 index 0000000..5559fa4 --- /dev/null +++ b/util_lib/include/ip_checksum.h @@ -0,0 +1,8 @@ +#ifndef IP_CHECKSUM_H +#define IP_CHECKSUM_H + +unsigned long compute_ip_checksum(void *addr, unsigned long length); +unsigned long add_ip_checksums(unsigned long offset, unsigned long sum, unsigned long new); +unsigned long negate_ip_checksum(unsigned long sum); + +#endif /* IP_CHECKSUM_H */ diff --git a/util_lib/include/sha256.h b/util_lib/include/sha256.h new file mode 100644 index 0000000..467fb22 --- /dev/null +++ b/util_lib/include/sha256.h @@ -0,0 +1,22 @@ +#ifndef SHA256_H +#define SHA256_H + +#include <sys/types.h> +#include <stdint.h> + +typedef struct +{ + size_t total[2]; + uint32_t state[8]; + uint8_t buffer[64]; +} +sha256_context; + +typedef uint8_t sha256_digest_t[32]; + +void sha256_starts( sha256_context *ctx ); +void sha256_update( sha256_context *ctx, const uint8_t *input, size_t length ); +void sha256_finish( sha256_context *ctx, sha256_digest_t digest ); + + +#endif /* SHA256_H */ diff --git a/util_lib/sha256.c b/util_lib/sha256.c new file mode 100644 index 0000000..2e61a31 --- /dev/null +++ b/util_lib/sha256.c @@ -0,0 +1,368 @@ +/* + * FIPS-180-2 compliant SHA-256 implementation + * + * Copyright (C) 2001-2003 Christophe Devine + * + * 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 <string.h> + +#include "sha256.h" + +#define GET_UINT32(n,b,i) \ +{ \ + (n) = ( (uint32_t) (b)[(i) ] << 24 ) | \ + ( (uint32_t) (b)[(i) + 1] << 16 ) | \ + ( (uint32_t) (b)[(i) + 2] << 8 ) | \ + ( (uint32_t) (b)[(i) + 3] ); \ +} + +#define PUT_UINT32(n,b,i) \ +{ \ + (b)[(i) ] = (uint8_t) ( (n) >> 24 ); \ + (b)[(i) + 1] = (uint8_t) ( (n) >> 16 ); \ + (b)[(i) + 2] = (uint8_t) ( (n) >> 8 ); \ + (b)[(i) + 3] = (uint8_t) ( (n) ); \ +} + +void sha256_starts( sha256_context *ctx ) +{ + ctx->total[0] = 0; + ctx->total[1] = 0; + + ctx->state[0] = 0x6A09E667; + ctx->state[1] = 0xBB67AE85; + ctx->state[2] = 0x3C6EF372; + ctx->state[3] = 0xA54FF53A; + ctx->state[4] = 0x510E527F; + ctx->state[5] = 0x9B05688C; + ctx->state[6] = 0x1F83D9AB; + ctx->state[7] = 0x5BE0CD19; +} + +void sha256_process( sha256_context *ctx, const uint8_t data[64] ) +{ + uint32_t temp1, temp2, W[64]; + uint32_t A, B, C, D, E, F, G, H; + + GET_UINT32( W[0], data, 0 ); + GET_UINT32( W[1], data, 4 ); + GET_UINT32( W[2], data, 8 ); + GET_UINT32( W[3], data, 12 ); + GET_UINT32( W[4], data, 16 ); + GET_UINT32( W[5], data, 20 ); + GET_UINT32( W[6], data, 24 ); + GET_UINT32( W[7], data, 28 ); + GET_UINT32( W[8], data, 32 ); + GET_UINT32( W[9], data, 36 ); + GET_UINT32( W[10], data, 40 ); + GET_UINT32( W[11], data, 44 ); + GET_UINT32( W[12], data, 48 ); + GET_UINT32( W[13], data, 52 ); + GET_UINT32( W[14], data, 56 ); + GET_UINT32( W[15], data, 60 ); + +#define SHR(x,n) ((x & 0xFFFFFFFF) >> n) +#define ROTR(x,n) (SHR(x,n) | (x << (32 - n))) + +#define S0(x) (ROTR(x, 7) ^ ROTR(x,18) ^ SHR(x, 3)) +#define S1(x) (ROTR(x,17) ^ ROTR(x,19) ^ SHR(x,10)) + +#define S2(x) (ROTR(x, 2) ^ ROTR(x,13) ^ ROTR(x,22)) +#define S3(x) (ROTR(x, 6) ^ ROTR(x,11) ^ ROTR(x,25)) + +#define F0(x,y,z) ((x & y) | (z & (x | y))) +#define F1(x,y,z) (z ^ (x & (y ^ z))) + +#define R(t) \ +( \ + W[t] = S1(W[t - 2]) + W[t - 7] + \ + S0(W[t - 15]) + W[t - 16] \ +) + +#define P(a,b,c,d,e,f,g,h,x,K) \ +{ \ + temp1 = h + S3(e) + F1(e,f,g) + K + x; \ + temp2 = S2(a) + F0(a,b,c); \ + d += temp1; h = temp1 + temp2; \ +} + + A = ctx->state[0]; + B = ctx->state[1]; + C = ctx->state[2]; + D = ctx->state[3]; + E = ctx->state[4]; + F = ctx->state[5]; + G = ctx->state[6]; + H = ctx->state[7]; + + P( A, B, C, D, E, F, G, H, W[ 0], 0x428A2F98 ); + P( H, A, B, C, D, E, F, G, W[ 1], 0x71374491 ); + P( G, H, A, B, C, D, E, F, W[ 2], 0xB5C0FBCF ); + P( F, G, H, A, B, C, D, E, W[ 3], 0xE9B5DBA5 ); + P( E, F, G, H, A, B, C, D, W[ 4], 0x3956C25B ); + P( D, E, F, G, H, A, B, C, W[ 5], 0x59F111F1 ); + P( C, D, E, F, G, H, A, B, W[ 6], 0x923F82A4 ); + P( B, C, D, E, F, G, H, A, W[ 7], 0xAB1C5ED5 ); + P( A, B, C, D, E, F, G, H, W[ 8], 0xD807AA98 ); + P( H, A, B, C, D, E, F, G, W[ 9], 0x12835B01 ); + P( G, H, A, B, C, D, E, F, W[10], 0x243185BE ); + P( F, G, H, A, B, C, D, E, W[11], 0x550C7DC3 ); + P( E, F, G, H, A, B, C, D, W[12], 0x72BE5D74 ); + P( D, E, F, G, H, A, B, C, W[13], 0x80DEB1FE ); + P( C, D, E, F, G, H, A, B, W[14], 0x9BDC06A7 ); + P( B, C, D, E, F, G, H, A, W[15], 0xC19BF174 ); + P( A, B, C, D, E, F, G, H, R(16), 0xE49B69C1 ); + P( H, A, B, C, D, E, F, G, R(17), 0xEFBE4786 ); + P( G, H, A, B, C, D, E, F, R(18), 0x0FC19DC6 ); + P( F, G, H, A, B, C, D, E, R(19), 0x240CA1CC ); + P( E, F, G, H, A, B, C, D, R(20), 0x2DE92C6F ); + P( D, E, F, G, H, A, B, C, R(21), 0x4A7484AA ); + P( C, D, E, F, G, H, A, B, R(22), 0x5CB0A9DC ); + P( B, C, D, E, F, G, H, A, R(23), 0x76F988DA ); + P( A, B, C, D, E, F, G, H, R(24), 0x983E5152 ); + P( H, A, B, C, D, E, F, G, R(25), 0xA831C66D ); + P( G, H, A, B, C, D, E, F, R(26), 0xB00327C8 ); + P( F, G, H, A, B, C, D, E, R(27), 0xBF597FC7 ); + P( E, F, G, H, A, B, C, D, R(28), 0xC6E00BF3 ); + P( D, E, F, G, H, A, B, C, R(29), 0xD5A79147 ); + P( C, D, E, F, G, H, A, B, R(30), 0x06CA6351 ); + P( B, C, D, E, F, G, H, A, R(31), 0x14292967 ); + P( A, B, C, D, E, F, G, H, R(32), 0x27B70A85 ); + P( H, A, B, C, D, E, F, G, R(33), 0x2E1B2138 ); + P( G, H, A, B, C, D, E, F, R(34), 0x4D2C6DFC ); + P( F, G, H, A, B, C, D, E, R(35), 0x53380D13 ); + P( E, F, G, H, A, B, C, D, R(36), 0x650A7354 ); + P( D, E, F, G, H, A, B, C, R(37), 0x766A0ABB ); + P( C, D, E, F, G, H, A, B, R(38), 0x81C2C92E ); + P( B, C, D, E, F, G, H, A, R(39), 0x92722C85 ); + P( A, B, C, D, E, F, G, H, R(40), 0xA2BFE8A1 ); + P( H, A, B, C, D, E, F, G, R(41), 0xA81A664B ); + P( G, H, A, B, C, D, E, F, R(42), 0xC24B8B70 ); + P( F, G, H, A, B, C, D, E, R(43), 0xC76C51A3 ); + P( E, F, G, H, A, B, C, D, R(44), 0xD192E819 ); + P( D, E, F, G, H, A, B, C, R(45), 0xD6990624 ); + P( C, D, E, F, G, H, A, B, R(46), 0xF40E3585 ); + P( B, C, D, E, F, G, H, A, R(47), 0x106AA070 ); + P( A, B, C, D, E, F, G, H, R(48), 0x19A4C116 ); + P( H, A, B, C, D, E, F, G, R(49), 0x1E376C08 ); + P( G, H, A, B, C, D, E, F, R(50), 0x2748774C ); + P( F, G, H, A, B, C, D, E, R(51), 0x34B0BCB5 ); + P( E, F, G, H, A, B, C, D, R(52), 0x391C0CB3 ); + P( D, E, F, G, H, A, B, C, R(53), 0x4ED8AA4A ); + P( C, D, E, F, G, H, A, B, R(54), 0x5B9CCA4F ); + P( B, C, D, E, F, G, H, A, R(55), 0x682E6FF3 ); + P( A, B, C, D, E, F, G, H, R(56), 0x748F82EE ); + P( H, A, B, C, D, E, F, G, R(57), 0x78A5636F ); + P( G, H, A, B, C, D, E, F, R(58), 0x84C87814 ); + P( F, G, H, A, B, C, D, E, R(59), 0x8CC70208 ); + P( E, F, G, H, A, B, C, D, R(60), 0x90BEFFFA ); + P( D, E, F, G, H, A, B, C, R(61), 0xA4506CEB ); + P( C, D, E, F, G, H, A, B, R(62), 0xBEF9A3F7 ); + P( B, C, D, E, F, G, H, A, R(63), 0xC67178F2 ); + + ctx->state[0] += A; + ctx->state[1] += B; + ctx->state[2] += C; + ctx->state[3] += D; + ctx->state[4] += E; + ctx->state[5] += F; + ctx->state[6] += G; + ctx->state[7] += H; +} + +void sha256_update( sha256_context *ctx, const uint8_t *input, size_t length ) +{ + size_t left, fill; + + if( ! length ) return; + + left = ctx->total[0] & 0x3F; + fill = 64 - left; + + ctx->total[0] += length; + ctx->total[0] &= 0xFFFFFFFF; + + if( ctx->total[0] < length ) + ctx->total[1]++; + + if( left && length >= fill ) + { + memcpy( ctx->buffer + left, input, fill ); + sha256_process( ctx, ctx->buffer ); + length -= fill; + input += fill; + left = 0; + } + + while( length >= 64 ) + { + sha256_process( ctx, input ); + length -= 64; + input += 64; + } + + if( length ) + { + memcpy(ctx->buffer + left, input, length); + } +} + +static uint8_t sha256_padding[64] = +{ + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +void sha256_finish( sha256_context *ctx, sha256_digest_t digest ) +{ + uint32_t last, padn; + uint32_t high, low; + uint8_t msglen[8]; + + high = ( ctx->total[0] >> 29 ) + | ( ctx->total[1] << 3 ); + low = ( ctx->total[0] << 3 ); + + PUT_UINT32( high, msglen, 0 ); + PUT_UINT32( low, msglen, 4 ); + + last = ctx->total[0] & 0x3F; + padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last ); + + sha256_update( ctx, sha256_padding, padn ); + sha256_update( ctx, msglen, 8 ); + + PUT_UINT32( ctx->state[0], digest, 0 ); + PUT_UINT32( ctx->state[1], digest, 4 ); + PUT_UINT32( ctx->state[2], digest, 8 ); + PUT_UINT32( ctx->state[3], digest, 12 ); + PUT_UINT32( ctx->state[4], digest, 16 ); + PUT_UINT32( ctx->state[5], digest, 20 ); + PUT_UINT32( ctx->state[6], digest, 24 ); + PUT_UINT32( ctx->state[7], digest, 28 ); +} + +#ifdef TEST + +#include <stdlib.h> +#include <stdio.h> + +/* + * those are the standard FIPS-180-2 test vectors + */ + +static char *msg[] = +{ + "abc", + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + NULL +}; + +static char *val[] = +{ + "ba7816bf8f01cfea414140de5dae2223" \ + "b00361a396177a9cb410ff61f20015ad", + "248d6a61d20638b8e5c026930c3e6039" \ + "a33ce45964ff2167f6ecedd419db06c1", + "cdc76e5c9914fb9281a1c7e284d73e67" \ + "f1809a48a497200e046d39ccc7112cd0" +}; + +int main( int argc, char *argv[] ) +{ + FILE *f; + int i, j; + char output[65]; + sha256_context ctx; + unsigned char buf[1000]; + unsigned char sha256sum[32]; + + if( argc < 2 ) + { + printf( "\n SHA-256 Validation Tests:\n\n" ); + + for( i = 0; i < 3; i++ ) + { + printf( " Test %d ", i + 1 ); + + sha256_starts( &ctx ); + + if( i < 2 ) + { + sha256_update( &ctx, (uint8_t *) msg[i], + strlen( msg[i] ) ); + } + else + { + memset( buf, 'a', 1000 ); + + for( j = 0; j < 1000; j++ ) + { + sha256_update( &ctx, (uint8_t *) buf, 1000 ); + } + } + + sha256_finish( &ctx, sha256sum ); + + for( j = 0; j < 32; j++ ) + { + sprintf( output + j * 2, "%02x", sha256sum[j] ); + } + + if( memcmp( output, val[i], 64 ) ) + { + printf( "failed!\n" ); + return( 1 ); + } + + printf( "passed.\n" ); + } + + printf( "\n" ); + } + else + { + if( ! ( f = fopen( argv[1], "rb" ) ) ) + { + perror( "fopen" ); + return( 1 ); + } + + sha256_starts( &ctx ); + + while( ( i = fread( buf, 1, sizeof( buf ), f ) ) > 0 ) + { + sha256_update( &ctx, buf, i ); + } + + sha256_finish( &ctx, sha256sum ); + + for( j = 0; j < 32; j++ ) + { + printf( "%02x", sha256sum[j] ); + } + + printf( " %s\n", argv[1] ); + } + + return( 0 ); +} + +#endif + diff --git a/vmcore-dmesg/Makefile b/vmcore-dmesg/Makefile new file mode 100644 index 0000000..b147f26 --- /dev/null +++ b/vmcore-dmesg/Makefile @@ -0,0 +1,29 @@ +# +# vmcore-dmesg (reading demsg from vmcore) +# + +VMCORE_DMESG_SRCS:= vmcore-dmesg/vmcore-dmesg.c + +VMCORE_DMESG_OBJS = $(call objify, $(VMCORE_DMESG_SRCS)) +VMCORE_DMESG_DEPS = $(call depify, $(VMCORE_DMESG_OBJS)) + +VMCORE_DMESG = $(SBINDIR)/vmcore-dmesg +VMCORE_DMESG_MANPAGE = $(MANDIR)/man8/vmcore-dmesg.8 + +dist += vmcore-dmesg/Makefile $(VMCORE_DMESG_SRCS) vmcore-dmesg/vmcore-dmesg.8 +clean += $(VMCORE_DMESG_OBJS) $(VMCORE_DMESG_DEPS) $(VMCORE_DMESG) $(VMCORE_DMESG_MANPAGE) + +-include $(VMCORE_DMESG_DEPS) + +$(VMCORE_DMESG): $(VMCORE_DMESG_OBJS) $(UTIL_LIB) + @$(MKDIR) -p $(@D) + $(LINK.o) -o $@ $^ $(CFLAGS) $(LIBS) + +$(VMCORE_DMESG_MANPAGE): vmcore-dmesg/vmcore-dmesg.8 + $(MKDIR) -p $(MANDIR)/man8 + cp $^ $(VMCORE_DMESG_MANPAGE) +echo:: + @echo "VMCORE_DMESG_SRCS $(VMCORE_DMESG_SRCS)" + @echo "VMCORE_DMESG_DEPS $(VMCORE_DMESG_DEPS)" + @echo "VMCORE_DMESG_OBJS $(VMCORE_DMESG_OBJS)" + diff --git a/vmcore-dmesg/vmcore-dmesg.8 b/vmcore-dmesg/vmcore-dmesg.8 new file mode 100644 index 0000000..ec594b0 --- /dev/null +++ b/vmcore-dmesg/vmcore-dmesg.8 @@ -0,0 +1,42 @@ +.\" Hey, EMACS: -*- nroff -*- +.\" First parameter, NAME, should be all caps +.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection +.\" other parameters are allowed: see man(7), man(1) +.TH VMCORE-DMESG 8 "Sep 21, 2020" +.\" Please adjust this date whenever revising the manpage. +.\" +.\" Some roff macros, for reference: +.\" .nh disable hyphenation +.\" .hy enable hyphenation +.\" .ad l left justify +.\" .ad b justify to both left and right margins +.\" .nf disable filling +.\" .fi enable filling +.\" .br insert line break +.\" .sp <n> insert n+1 empty lines +.\" for manpage-specific macros, see man(7) +.SH NAME +vmcore-dmesg +.SH SYNOPSIS +.B vmcore-dmesg +.RI " vmcore" +.SH DESCRIPTION +.PP +.\" TeX users may be more comfortable with the \fB<whatever>\fP and +.\" \fI<whatever>\fP escape sequences to invode bold face and italics, +.\" respectively. +\fBvmcore-dmesg\fP extracts the dmesg from a vmcore and write it to +standard out. \fBvmcore-dmesg\fP works against either +\fB/proc/vmcore\fP in a crash dump capture context or a copy +of \fB/proc/vmcore\fP that has been saved for later analysis. A +single build of \fBvmcore-dmesg\fP should work against any linux +vmcore written created on any architecture. + +.\"These programs follow the usual GNU command line syntax, with long +.\"options starting with two dashes (`-'). +.\"A summary of options is included below. +.\"For a complete description, see the Info files. +.SH SEE ALSO +kexec(8) +.SH AUTHOR +vmcore-dmesg was written by Eric Biederman. diff --git a/vmcore-dmesg/vmcore-dmesg.c b/vmcore-dmesg/vmcore-dmesg.c new file mode 100644 index 0000000..122e536 --- /dev/null +++ b/vmcore-dmesg/vmcore-dmesg.c @@ -0,0 +1,69 @@ +#include <elf_info.h> + +/* The 32bit and 64bit note headers make it clear we don't care */ +typedef Elf32_Nhdr Elf_Nhdr; + +extern const char *fname; + +/* stole this macro from kernel printk.c */ +#define LOG_BUF_LEN_MAX (uint32_t)(1U << 31) + +static void write_to_stdout(char *buf, unsigned int nr) +{ + ssize_t ret; + static uint32_t n_bytes = 0; + + n_bytes += nr; + if (n_bytes > LOG_BUF_LEN_MAX) { + fprintf(stderr, "The vmcore-dmesg.txt over 2G in size is not supported.\n"); + exit(53); + } + + ret = write(STDOUT_FILENO, buf, nr); + if (ret != nr) { + fprintf(stderr, "Failed to write out the dmesg log buffer!:" + " %s\n", strerror(errno)); + exit(54); + } +} + +static int read_vmcore_dmesg(int fd, void (*handler)(char*, unsigned int)) +{ + int ret; + + ret = read_elf(fd); + if (ret > 0) { + fprintf(stderr, "Unable to read ELF information" + " from vmcore\n"); + return ret; + } + + dump_dmesg(fd, handler); + + return 0; +} + +int main(int argc, char **argv) +{ + ssize_t ret; + int fd; + + if (argc != 2) { + fprintf(stderr, "usage: %s <kernel core file>\n", argv[0]); + return 1; + } + fname = argv[1]; + + fd = open(fname, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "Cannot open %s: %s\n", + fname, strerror(errno)); + return 2; + } + + ret = read_vmcore_dmesg(fd, write_to_stdout); + + close(fd); + + return ret; +} |