kexec-tools-1.101

- Initial import into git
  - initial nbi image formage support
  - ppc32 initial register setting fixes.
  - gzipped multiboot file support
This commit is contained in:
Eric W. Biederman
2006-07-27 02:36:23 -06:00
commit 283261998a
147 changed files with 28987 additions and 0 deletions

6
AUTHORS Normal file
View File

@@ -0,0 +1,6 @@
Eric Biederman <ebiederm@xmission.com>
Albert Herranz
Jesse Barnes <jbarnes@sgi.com>
Khalid Aziz <khalid.aziz@hp.com>
Hariprasad Nellitheertha <hari@in.ibm.com>
Tim Deegan <tjd21@cl.cam.ac.uk>

341
COPYING Normal file
View File

@@ -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.

249
Makefile Normal file
View File

@@ -0,0 +1,249 @@
# Hey Emacs this is a -*- makefile-*-
include Makefile.conf
VERSION=1.101
DATE=15 February 2005
PACKAGE=kexec-tools
pkgdatadir = $(datadir)/$(PACKAGE)
pkglibdir = $(libdir)/$(PACKAGE)
pkgincludedir = $(includedir)/$(PACKAGE)
# 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 =
CPPFLAGS:= -I./include -I./util_lib/include \
-DVERSION='"$(VERSION)"' -DRELEASE_DATE='"$(DATE)"' \
$(DEFS) $(EXTRA_CFLAGS)
PREFIX:=$(OBJDIR)/build
SBINDIR=$(PREFIX)/sbin
BINDIR=$(PREFIX)/bin
LIBEXECDIR=$(PREFIX)/libexec
DATADIR=$(PREFIX)/share
SYSCONFDIR=$(PREFIX)/etc
SHAREDSTATEDIR=$(PREFIX)/com
LOCALSTATEDIR=$(PREFIX)/var
LIBDIR=$(PREFIX)/lib
INFODIR=$(PREFIX)/info
MANDIR=$(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=$(PREFIX)/include
PKGDATADIR=$(DATADIR)/$(PACKAGE)
PKGLIBDIR=$(LIBDIR)/$(PACKAGE)
PKGINCLUDEIR=$(INCLUDEDIR)/$(PACKAGE)
MAN_PAGES:= kexec/kexec.8
BINARIES_i386:= $(SBINDIR)/kexec $(PKGLIBDIR)/kexec_test
BINARIES_x86_64:=$(SBINDIR)/kexec $(PKGLIBDIR)/kexec_test
BINARIES:=$(SBINDIR)/kexec $(SBINDIR)/kdump $(BINARIES_$(ARCH))
TARGETS:=$(BINARIES) $(MAN_PAGES)
all: $(TARGETS)
# cc-option
# Usage: cflags-y += $(call cc-option, -march=winchip-c6, -march=i586)
cc-option = $(shell if $(CC) $(CFLAGS) $(1) -S -o /dev/null -xc /dev/null \
> /dev/null 2>&1; then echo "$(1)"; else echo "$(2)"; fi ;)
# Utility function library
#
include util_lib/Makefile
#
# Stand alone utilities
#
include util/Makefile
#
# purgatory (code between kernels)
#
include purgatory/Makefile
#
# kexec (linux booting linux)
#
include kexec/Makefile
# kdump (read a crashdump from memory)
#
include kdump/Makefile
#
# kexec_test (test program)
#
ifeq ($(ARCH),i386)
include kexec_test/Makefile
endif
ifeq ($(ARCH),x86_64)
include kexec_test/Makefile
endif
GENERATED_SRCS:= ./configure
SPEC=$(OBJDIR)/$(PACKAGE)-$(VERSION).spec
TARBALL=$(OBJDIR)/$(PACKAGE)-$(VERSION).tar.gz
SRCS:=$(shell $(FIND) \
./AUTHORS ./COPYING ./News ./TODO \
./Makefile ./Makefile.conf.in ./configure.ac \
./kexec-tools.spec.in ./config ./doc \
./include ./kexec ./purgatory ./kexec_test ./kdump ./util ./util_lib \
! -path '*CVS*' ! -name '*~' ! -name '.*' \
-type f -print )
SRCS+=$(GENERATED_SRCS)
PSRCS:=$(patsubst ./%,$(PACKAGE)-$(VERSION)/%,$(SRCS))
PSRCS+=$(PACKAGE)-$(VERSION).spec
Makefile.conf: Makefile.conf.in configure
/bin/sh ./configure
configure: configure.ac
autoconf
$(RM) -rf autom4te.cache
tarball: $(TARBALL)
$(TARBALL): $(SRCS) $(SPEC)
$(MKDIR) -p $(OBJDIR)
$(RM) -f $(OBJDIR)/$(PACKAGE)-$(VERSION)
$(LN) -s .. $(OBJDIR)/$(PACKAGE)-$(VERSION)
(cd $(OBJDIR); $(TAR) -cf - $(PSRCS) | gzip -9) > $@
rpm: $(TARBALL)
$(MKDIR) -p $(OBJDIR)/RPM $(OBJDIR)/SRPM $(OBJDIR)/BUILD $(OBJDIR)/SPECS \
$(OBJDIR)/TMP $(OBJDIR)/SOURCES
unset MAKEFLAGS MAKELEVEL; \
$(RPMBUILD) -ta \
--define '_rpmdir $(OBJDIR)/RPM' \
--define '_srcrpmdir $(OBJDIR)/SRPM' \
--define '_builddir $(OBJDIR)/BUILD' \
--define '_specdir $(OBJDIR)/SPECS' \
--define '_tmppath $(OBJDIR)/TMP' \
--define '_sourcedir $(OBJDIR)/SOURCES' \
$(TARBALL)
$(SPEC): kexec-tools.spec.in Makefile
$(SED) -e 's,^Version: $$,Version: $(VERSION),' $< > $@
echo::
@echo ARCH=$(ARCH)
@echo BINARIES=$(BINARIES)
@echo TARGETS=$(TARGETS)
@echo CC=$(CC)
@echo AR=$(AR)
@echo LD=$(LD)
clean:
@$(FIND) $(OBJDIR) ! -name '*.d' -type f | $(XARGS) $(RM) rm -f
@$(RM) -rf rpm
@$(RM) -f config.log config.status config.cache
@$(RM) -f $(TARBALL)
dist-clean: clean
@$(RM) -rf $(OBJDIR)
@$(FIND) . -type f -name '*~' -o -name '*.orig' | $(XARGS) $(RM) -f
@$(RM) -f Makefile.conf
maintainer-clean: dist-clean
@$(RM) -f $(GENERATED_SRCS)
install: $(TARGETS)
for file in $(TARGETS) ; do \
if test `$(DIRNAME) $$file` = "$(SBINDIR)" ; then \
$(MKDIR) -p $(DESTDIR)/$(sbindir)/; \
$(INSTALL) -m 555 $$file $(DESTDIR)/$(sbindir)/; \
fi; \
if test `$(DIRNAME) $$file` = "$(BINDIR)" ; then \
$(MKDIR) -p $(DESTDIR)/$(bindir)/; \
$(INSTALL) -m 555 $$file $(DESTDIR)/$(bindir)/; \
fi; \
if test `$(DIRNAME) $$file` = "$(LIBEXECDIR)" ; then \
$(MKDIR) -p $(DESTDIR)/$(libexecdir)/; \
$(INSTALL) -m 555 $$file $(DESTDIR)/$(libexecdir)/; \
fi; \
if test `$(DIRNAME) $$file` = "$(DATADIR)" ; then \
$(MKDIR) -p $(DESTDIR)/$(datadir)/; \
$(INSTALL) -m 444 $$file $(DESTDIR)/$(datadir)/; \
fi; \
if test `$(DIRNAME) $$file` = "$(SYSCONFDIR)" ; then \
$(MKDIR) -p $(DESTDIR)/$(sysconfdir)/; \
$(INSTALL) -m 444 $$file $(DESTDIR)/$(sysconfdir)/; \
fi; \
if test `$(DIRNAME) $$file` = "$(SHAREDSTATEDIR)" ; then \
$(MKDIR) -p $(DESTDIR)/$(sharedstatedir)/; \
$(INSTALL) -m 444 $$file $(DESTDIR)/$(sharedstatedir)/; \
fi; \
if test `$(DIRNAME) $$file` = "$(LOCALSTATEDIR)" ; then \
$(MKDIR) -p $(DESTDIR)/$(localstatedir)/; \
$(INSTALL) -m 444 $$file $(DESTDIR)/$(localstatedir)/; \
fi; \
if test `$(DIRNAME) $$file` = "$(LIBDIR)" ; then \
$(MKDIR) -p $(DESTDIR)/$(libdir)/; \
$(INSTALL) -m 444 $$file $(DESTDIR)/$(libdir)/; \
fi; \
if test `$(DIRNAME) $$file` = "$(INFODIR)" ; then \
$(MKDIR) -p $(DESTDIR)/$(infodir)/; \
$(INSTALL) -m 444 $$file $(DESTDIR)/$(infodir)/; \
fi; \
if test `$(DIRNAME) $$file` = "$(MAN1DIR)" ; then \
$(MKDIR) -p $(DESTDIR)/$(mandir)/man1; \
$(INSTALL) -m 444 $$file $(DESTDIR)/$(mandir)/man1; \
fi; \
if test `$(DIRNAME) $$file` = "$(MAN2DIR)" ; then \
$(MKDIR) -p $(DESTDIR)/$(mandir)/man2; \
$(INSTALL) -m 444 $$file $(DESTDIR)/$(mandir)/man2; \
fi; \
if test `$(DIRNAME) $$file` = "$(MAN3DIR)" ; then \
$(MKDIR) -p $(DESTDIR)/$(mandir)/man3/; \
$(INSTALL) -m 444 $$file $(DESTDIR)/$(mandir)/man3/; \
fi; \
if test `$(DIRNAME) $$file` = "$(MAN4DIR)" ; then \
$(MKDIR) -p $(DESTDIR)/$(mandir)/man4/; \
$(INSTALL) -m 444 $$file $(DESTDIR)/$(mandir)/man4/; \
fi; \
if test `$(DIRNAME) $$file` = "$(MAN5DIR)" ; then \
$(MKDIR) -p $(DESTDIR)/$(mandir)/man5/; \
$(INSTALL) -m 444 $$file $(DESTDIR)/$(mandir)/man5/; \
fi; \
if test `$(DIRNAME) $$file` = "$(MAN6DIR)" ; then \
$(MKDIR) -p $(DESTDIR)/$(mandir)/man6/; \
$(INSTALL) -m 444 $$file $(DESTDIR)/$(mandir)/man6/; \
fi; \
if test `$(DIRNAME) $$file` = "$(MAN7DIR)" ; then \
$(MKDIR) -p $(DESTDIR)/$(mandir)/man7/; \
$(INSTALL) -m 444 $$file $(DESTDIR)/$(mandir)/man7/; \
fi; \
if test `$(DIRNAME) $$file` = "$(MAN8DIR)" ; then \
$(MKDIR) -p $(DESTDIR)/$(mandir)/man8/; \
$(INSTALL) -m 444 $$file $(DESTDIR)/$(mandir)/man8/; \
fi; \
if test `$(DIRNAME) $$file` = "$(INCLUDEDIR)" ; then \
$(MKDIR) -p $(DESTDIR)/$(includedir)/; \
$(INSTALL) -m 444 $$file $(DESTDIR)/$(includedir)/; \
fi; \
if test `$(DIRNAME) $$file` = "$(PKGDATADIR)" ; then \
$(MKDIR) -p $(DESTDIR)/$(pkgdatadir)/; \
$(INSTALL) -m 444 $$file $(DESTDIR)/$(pkgdatadir)/; \
fi; \
if test `$(DIRNAME) $$file` = "$(PKGLIBDIR)" ; then \
$(MKDIR) -p $(DESTDIR)/$(pkglibdir)/; \
$(INSTALL) -m 444 $$file $(DESTDIR)/$(pkglibdir)/; \
fi; \
if test `$(DIRNAME) $$file` = "$(PKGINCLUDEDIR)" ; then \
$(MKDIR) -p $(DESTDIR)/$(pkgincludedir)/; \
$(INSTALL) -m 444 $$file $(DESTDIR)/$(pkgincludedir)/; \
fi; \
done
.PHONY: echo install all clean dist-clean maintainer-clean tarball rpm

52
Makefile.conf.in Normal file
View File

@@ -0,0 +1,52 @@
# Hey Emacs this is a -*- makefile-*-
prefix=@prefix@
exec_prefix=@exec_prefix@
bindir=@bindir@
sbindir=@sbindir@
libexecdir=@libexecdir@
datadir=@datadir@
sysconfdir=@sysconfdir@
sharedstatedir=@sharedstatedir@
localstatedir=@localstatedir@
libdir=@libdir@
infodir=@infodir@
mandir=@mandir@
includedir=@includedir@
DEFS=@DEFS@
LIBS=@LIBS@
# The target architecture
ARCH =@ARCH@
OBJDIR=@OBJDIR@
# Compiler for building kexec
CC =@CC@
CPP =@CPP@
LD =@LD@
AS =@AS@
OBJCOPY =@OBJCOPY@
AR =@AR@
CFLAGS =@CFLAGS@
EXTRA_CFLAGS=@EXTRA_CFLAGS@
LDFLAGS =@LDFLAGS@
# Utilities called by the makefiles
INSTALL=@INSTALL@
MKDIR=@MKDIR@
RM=@RM@
CP=@CP@
LN=@LN@
TAR=@TAR@
RPMBUILD=@RPMBUILD@
SED=@SED@
FIND=@FIND@
XARGS=@XARGS@
DIRNAME=@DIRNAME@
# C compiler for building utilities to use
# during the build
BUILD_CC=@BUILD_CC@
BUILD_CFLAGS=@BUILD_CFLAGS@ $(DEFS)

73
News Normal file
View File

@@ -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.
###

11
TODO Normal file
View File

@@ -0,0 +1,11 @@
- 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
- In the kexec-on-panic case preserving memory the both kernels
must use.
- Finish the kexec-on-panic case.
- Improve the documentation
- Add support for loading a boot sector
- Autobuilding of initramfs
###

1441
config/config.guess vendored Executable file

File diff suppressed because it is too large Load Diff

1552
config/config.sub vendored Executable file

File diff suppressed because it is too large Load Diff

251
config/install-sh Executable file
View File

@@ -0,0 +1,251 @@
#!/bin/sh
#
# install - install a program, script, or datafile
# This comes from X11R5 (mit/util/scripts/install.sh).
#
# Copyright 1991 by the Massachusetts Institute of Technology
#
# Permission to use, copy, modify, distribute, and sell this software and its
# documentation for any purpose is hereby granted without fee, provided that
# the above copyright notice appear in all copies and that both that
# copyright notice and this permission notice appear in supporting
# documentation, and that the name of M.I.T. not be used in advertising or
# publicity pertaining to distribution of the software without specific,
# written prior permission. M.I.T. makes no representations about the
# suitability of this software for any purpose. It is provided "as is"
# without express or implied warranty.
#
# 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. It can only install one file at a time, a restriction
# shared with many OS's install programs.
# set DOITPROG to echo to test this script
# Don't use :- since 4.3BSD and earlier shells don't like it.
doit="${DOITPROG-}"
# put in absolute paths if you don't have them in your path; or use env. vars.
mvprog="${MVPROG-mv}"
cpprog="${CPPROG-cp}"
chmodprog="${CHMODPROG-chmod}"
chownprog="${CHOWNPROG-chown}"
chgrpprog="${CHGRPPROG-chgrp}"
stripprog="${STRIPPROG-strip}"
rmprog="${RMPROG-rm}"
mkdirprog="${MKDIRPROG-mkdir}"
transformbasename=""
transform_arg=""
instcmd="$mvprog"
chmodcmd="$chmodprog 0755"
chowncmd=""
chgrpcmd=""
stripcmd=""
rmcmd="$rmprog -f"
mvcmd="$mvprog"
src=""
dst=""
dir_arg=""
while [ x"$1" != x ]; do
case $1 in
-c) instcmd="$cpprog"
shift
continue;;
-d) dir_arg=true
shift
continue;;
-m) chmodcmd="$chmodprog $2"
shift
shift
continue;;
-o) chowncmd="$chownprog $2"
shift
shift
continue;;
-g) chgrpcmd="$chgrpprog $2"
shift
shift
continue;;
-s) stripcmd="$stripprog"
shift
continue;;
-t=*) transformarg=`echo $1 | sed 's/-t=//'`
shift
continue;;
-b=*) transformbasename=`echo $1 | sed 's/-b=//'`
shift
continue;;
*) if [ x"$src" = x ]
then
src=$1
else
# this colon is to work around a 386BSD /bin/sh bug
:
dst=$1
fi
shift
continue;;
esac
done
if [ x"$src" = x ]
then
echo "install: no input file specified"
exit 1
else
true
fi
if [ x"$dir_arg" != x ]; then
dst=$src
src=""
if [ -d $dst ]; then
instcmd=:
chmodcmd=""
else
instcmd=mkdir
fi
else
# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
# might cause directories to be created, which would be especially bad
# if $src (and thus $dsttmp) contains '*'.
if [ -f $src -o -d $src ]
then
true
else
echo "install: $src does not exist"
exit 1
fi
if [ x"$dst" = x ]
then
echo "install: no destination specified"
exit 1
else
true
fi
# If destination is a directory, append the input filename; if your system
# does not like double slashes in filenames, you may need to add some logic
if [ -d $dst ]
then
dst="$dst"/`basename $src`
else
true
fi
fi
## this sed command emulates the dirname command
dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
# Make sure that the destination directory exists.
# this part is taken from Noah Friedman's mkinstalldirs script
# Skip lots of stat calls in the usual case.
if [ ! -d "$dstdir" ]; then
defaultIFS='
'
IFS="${IFS-${defaultIFS}}"
oIFS="${IFS}"
# Some sh's can't handle IFS=/ for some reason.
IFS='%'
set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
IFS="${oIFS}"
pathcomp=''
while [ $# -ne 0 ] ; do
pathcomp="${pathcomp}${1}"
shift
if [ ! -d "${pathcomp}" ] ;
then
$mkdirprog "${pathcomp}"
else
true
fi
pathcomp="${pathcomp}/"
done
fi
if [ x"$dir_arg" != x ]
then
$doit $instcmd $dst &&
if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
else
# If we're going to rename the final executable, determine the name now.
if [ x"$transformarg" = x ]
then
dstfile=`basename $dst`
else
dstfile=`basename $dst $transformbasename |
sed $transformarg`$transformbasename
fi
# don't allow the sed command to completely eliminate the filename
if [ x"$dstfile" = x ]
then
dstfile=`basename $dst`
else
true
fi
# Make a temp file name in the proper directory.
dsttmp=$dstdir/#inst.$$#
# Move or copy the file name to the temp name
$doit $instcmd $src $dsttmp &&
trap "rm -f ${dsttmp}" 0 &&
# 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 $instcmd $src $dsttmp" command.
if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
# Now rename the file to the real destination.
$doit $rmcmd -f $dstdir/$dstfile &&
$doit $mvcmd $dsttmp $dstdir/$dstfile
fi &&
exit 0

4840
configure vendored Executable file

File diff suppressed because it is too large Load Diff

129
configure.ac Normal file
View File

@@ -0,0 +1,129 @@
dnl
dnl configure.ac for mkelfImage
dnl
dnl
dnl ---Required
AC_INIT(Makefile.conf.in)
AC_CONFIG_AUX_DIR(./config)
dnl -- Compilation platform configuration
AC_CANONICAL_HOST
dnl Currentl AC_CANONICAL_HOST is sufficient for my needs
dnl as there are only a small number of targets that kexec
dnl can support on a given host system. If it stops making
dnl sense compile support for all possible targets a given
dnl host can support AC_CANONICAL_TARGET may help
dnl AC_CANONICAL_TARGET
dnl Compute host cpu
case $host_cpu in
i?86 )
host_cpu="i386"
;;
powerpc )
host_cpu="ppc"
;;
* )
host_cpu="$host_cpu"
;;
esac
case $host_cpu in
i386|ppc|x86_64|alpha|ppc64|ia64)
;;
* )
AC_MSG_ERROR([ unsupported architecture $host_cpu])
;;
esac
ARCH=$host_cpu
dnl ---Options
OBJDIR=`pwd`/objdir
if test "${host_alias}" ; then
OBJDIR="$OBJDIR-${host_alias}"
fi
EXTRA_CFLAGS=""
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]),
[ EXTRA_CFLAGS="$EXTRA_CFLAGS -DCONFIG_GAMECUBE=1" ])
AC_ARG_WITH([zlib], AC_HELP_STRING([--without-zlib],[disable gamecube support]),
[ with_zlib="$withval"], [ with_zlib=yes ] )
dnl ---Programs
dnl To specify a different compiler, just 'export CC=/path/to/compiler'
AC_PROG_CC
if test "${build}" != "${host}" ; then
BUILD_CC=${CC_FOR_BUILD-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)
dnl Find the helper functions
AC_PROG_INSTALL
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_DEFINE(HAVE_ZLIB_H, 1) LIBS="$LIBS -lz"]))
fi
dnl ---Hard codes
CFLAGS='-Wall -g -fno-strict-aliasing $(CPPFLAGS)'
BUILD_CFLAGS='-O2 -Wall $(CPPFLAGS)'
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
dnl ---Output variables...
AC_SUBST([BUILD_CC])
AC_SUBST([BUILD_CFLAGS])
AC_SUBST([EXTRA_CFLAGS])
AC_SUBST([ARCH])
AC_SUBST([OBJDIR])
AC_SUBST([INSTALL])
dnl ---Output
AC_OUTPUT([Makefile.conf])

438
doc/linux-i386-boot.txt Normal file
View File

@@ -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.

View File

@@ -0,0 +1,79 @@
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
0x600 - 0x7ff EDDBUF (edd.S) for disk signature read sector
0x600 - 0x7eb EDDBUF (edd.S) for edd data

664
doc/multiboot.html Normal file
View File

@@ -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>

660
doc/nbi-spec.txt Normal file
View File

@@ -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

39
include/boot/beoboot.h Normal file
View File

@@ -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
/*------------------------------------------------------------------*/

104
include/boot/elf_boot.h Normal file
View File

@@ -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 */

View File

@@ -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 */

2367
include/elf.h Normal file

File diff suppressed because it is too large Load Diff

90
include/x86/mb_header.h Normal file
View File

@@ -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

219
include/x86/mb_info.h Normal file
View File

@@ -0,0 +1,219 @@
/*
* 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;
};
/*
* 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
/*
* The following value must be present in the EAX register.
*/
#define MULTIBOOT_VALID 0x2BADB002

212
include/x86/x86-linux.h Normal file
View File

@@ -0,0 +1,212 @@
#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 E820MAX 32 /* number of entries in E820MAP */
#define E820NR 0x1e8 /* # entries in E820MAP */
#ifndef ASSEMBLY
#define PACKED __attribute__((packed))
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
} PACKED;
/* FIXME expand on drive_info_)struct... */
struct drive_info_struct {
uint8_t dummy[32];
};
struct sys_desc_table {
uint16_t length;
uint8_t table[318];
};
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 */
uint8_t reserved[44]; /* 0x54 */
};
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 */
uint8_t reserved4[12]; /* 0x34 -- 0x3f reserved for future expansion */
struct apm_bios_info apm_bios_info; /* 0x40 */
struct drive_info_struct drive_info; /* 0x80 */
struct sys_desc_table sys_desc_table; /* 0xa0 */
uint32_t alt_mem_k; /* 0x1e0 */
uint8_t reserved5[4]; /* 0x1e4 */
uint8_t e820_map_nr; /* 0x1e8 */
uint8_t reserved6[8]; /* 0x1e9 */
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_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
uint8_t reserved15[0x2d0 - 0x230]; /* 0x230 */
#endif
struct e820entry e820_map[E820MAX]; /* 0x2d0 */
/* 0x550 */
#define COMMAND_LINE_SIZE 256
};
struct x86_linux_faked_param_header {
struct x86_linux_param_header hdr; /* 0x00 */
uint8_t reserved16[688]; /* 0x550 */
uint8_t command_line[COMMAND_LINE_SIZE]; /* 0x800 */
uint8_t reserved17[1792]; /* 0x900 - 0x1000 */
};
struct x86_linux_header {
uint8_t reserved1[0x1f1]; /* 0x000 */
uint8_t setup_sects; /* 0x1f1 */
uint16_t root_flags; /* 0x1f2 */
uint16_t syssize; /* 0x1f4 */
uint16_t swapdev; /* 0x1f6 */
uint16_t ramdisk_flags; /* 0x1f6 */
uint16_t vid_mode; /* 0x1fa */
uint16_t root_dev; /* 0x1fc */
uint16_t boot_sector_magic; /* 0x1fe */
/* 2.00+ */
uint8_t reserved3[2]; /* 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 */
uint8_t reserved4[4]; /* 0x220 */
/* 2.01+ */
uint16_t heap_end_ptr; /* 0x224 */
uint8_t reserved5[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 */
uint32_t tail[32*1024 - 0x258]; /* 0x258 */
#else
uint8_t tail[32*1024 - 0x230]; /* 0x230 */
#endif
} PACKED;
#endif /* ASSEMBLY */
#define DEFAULT_INITRD_ADDR_MAX 0x37FFFFFF
#endif /* X86_LINUX_H */

35
kdump/Makefile Normal file
View File

@@ -0,0 +1,35 @@
#
# kdump (reading a crashdump from memory)
#
KDUMP_C_SRCS:= kdump/kdump.c
KDUMP_C_OBJS:= $(patsubst %.c, $(OBJDIR)/%.o, $(KDUMP_C_SRCS))
KDUMP_C_DEPS:= $(patsubst %.c, $(OBJDIR)/%.d, $(KDUMP_C_SRCS))
KDUMP_SRCS:= $(KDUMP_C_SRCS)
KDUMP_OBJS:= $(KDUMP_C_OBJS)
KDUMP_DEPS:= $(KDUMP_C_DEPS)
KDUMP:= $(SBINDIR)/kdump
include $(KDUMP_DEPS)
$(KDUMP_C_DEPS): $(OBJDIR)/%.d: %.c
mkdir -p $(@D)
$(CC) $(CFLAGS) -M $< | sed -e 's|$(patsubst %.d,%.o,$(@F))|$(patsubst %.d,%.o,$(@))|' > $@
$(KDUMP_C_OBJS): $(OBJDIR)/%.o: %.c $(OBJDIR)/%.d
mkdir -p $(@D)
$(CC) $(CFLAGS) -o $@ -c $<
$(KDUMP): $(KDUMP_OBJS)
mkdir -p $(@D)
$(CC) $(CFLAGS) -o $@ $(KDUMP_OBJS)
echo::
@echo "KDUMP_C_SRCS $(KDUMP_C_SRCS)"
@echo "KDUMP_C_DEPS $(KDUMP_C_DEPS)"
@echo "KDUMP_C_OBJS $(KDUMP_C_OBJS)"
@echo "KDUMP_SRCS $(KDUMP_SRCS)"
@echo "KDUMP_DEPS $(KDUMP_DEPS)"
@echo "KDUMP_OBJS $(KDUMP_OBJS)"

311
kdump/kdump.c Normal file
View File

@@ -0,0 +1,311 @@
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <endian.h>
#include <elf.h>
#if !defined(__BYTE_ORDER) || !defined(__LITTLE_ENDIAN) || !defined(__BIG_ENDIAN)
#error Endian defines missing
#endif
#if __BYTE_ORDER == __LITTLE_ENDIAN
# define ELFDATALOCAL ELFDATA2LSB
#elif __BYTE_ORDER == __BIG_ENDIAN
# define ELFDATALOCAL ELFDATA2MSB
#else
# error Unknown byte order
#endif
#define MAP_WINDOW_SIZE (64*1024*1024)
#define DEV_MEM "/dev/mem"
static void *map_addr(int fd, unsigned long size, off_t offset)
{
void *result;
result = mmap(0, size, PROT_READ, MAP_SHARED, fd, offset);
if (result == MAP_FAILED) {
fprintf(stderr, "Cannot mmap " DEV_MEM " offset: %llu size: %lu: %s\n",
(unsigned long long)offset, size, strerror(errno));
exit(5);
}
return result;
}
static void unmap_addr(void *addr, unsigned long size)
{
int ret;
ret = munmap(addr, size);
if (ret < 0) {
fprintf(stderr, "munmap failed: %s\n",
strerror(errno));
exit(6);
}
}
static void *xmalloc(size_t size)
{
void *result;
result = malloc(size);
if (result == NULL) {
fprintf(stderr, "malloc of %u bytes failed: %s\n",
size, strerror(errno));
exit(7);
}
return result;
}
static void *collect_notes(
int fd, Elf64_Ehdr *ehdr, Elf64_Phdr *phdr, size_t *note_bytes)
{
int i;
size_t bytes, result_bytes;
char *notes;
result_bytes = 0;
/* Find the worst case note memory usage */
bytes = 0;
for(i = 0; i < ehdr->e_phnum; i++) {
if (phdr[i].p_type == PT_NOTE) {
bytes += phdr[i].p_filesz;
}
}
/* Allocate the worst case note array */
notes = xmalloc(bytes);
/* Walk through and capture the notes */
for(i = 0; i < ehdr->e_phnum; i++) {
Elf64_Nhdr *hdr, *lhdr, *nhdr;
void *pnotes;
if (phdr[i].p_type != PT_NOTE) {
continue;
}
/* First snapshot the notes */
pnotes = map_addr(fd, phdr[i].p_filesz, phdr[i].p_offset);
memcpy(notes + result_bytes, pnotes, phdr[i].p_filesz);
unmap_addr(pnotes, phdr[i].p_filesz);
/* Walk through the new notes and find the real length */
hdr = (Elf64_Nhdr *)(notes + result_bytes);
lhdr = (Elf64_Nhdr *)(notes + result_bytes + phdr[i].p_filesz);
for(; hdr < lhdr; hdr = nhdr) {
size_t hdr_size;
/* If there is not a name this is a invalid/reserved note
* stop here.
*/
if (hdr->n_namesz == 0) {
break;
}
hdr_size =
sizeof(*hdr) +
((hdr->n_namesz + 3) & ~3) +
((hdr->n_descsz + 3) & ~3);
nhdr = (Elf64_Nhdr *)(((char *)hdr) + hdr_size);
/* if the note does not fit in the segment stop here */
if (nhdr > lhdr) {
break;
}
/* Update result_bytes for after each good header */
result_bytes = ((char *)hdr) - notes;
}
}
*note_bytes = result_bytes;
return notes;
}
static void *generate_new_headers(
Elf64_Ehdr *ehdr, Elf64_Phdr *phdr, size_t note_bytes, size_t *header_bytes)
{
unsigned phnum;
size_t bytes;
char *headers;
Elf64_Ehdr *nehdr;
Elf64_Phdr *nphdr;
unsigned long long offset;
int i;
/* Count the number of program headers.
* When we are done there will be only one note header.
*/
phnum = 1;
for(i = 0; i < ehdr->e_phnum; i++) {
if (phdr[i].p_type == PT_NOTE) {
continue;
}
phnum++;
}
/* Compute how many bytes we will need for headers */
bytes = sizeof(*ehdr) + sizeof(*phdr)*phnum;
/* Allocate memory for the headers */
headers = xmalloc(bytes);
/* Setup pointers to the new headers */
nehdr = (Elf64_Ehdr *)headers;
nphdr = (Elf64_Phdr *)(headers + sizeof(*nehdr));
/* Copy and adjust the Elf header */
memcpy(nehdr, ehdr, sizeof(*nehdr));
nehdr->e_phoff = sizeof(*nehdr);
nehdr->e_phnum = phnum;
nehdr->e_shoff = 0;
nehdr->e_shentsize = 0;
nehdr->e_shnum = 0;
nehdr->e_shstrndx = 0;
/* Write the note program header */
nphdr->p_type = PT_NOTE;
nphdr->p_offset = bytes;
nphdr->p_vaddr = 0;
nphdr->p_paddr = 0;
nphdr->p_filesz = note_bytes;
nphdr->p_memsz = note_bytes;
nphdr->p_flags = 0;
nphdr->p_align = 0;
nphdr++;
/* Write the rest of the program headers */
offset = bytes + note_bytes;
for(i = 0; i < ehdr->e_phnum; i++) {
if (phdr[i].p_type == PT_NOTE) {
continue;
}
memcpy(nphdr, &phdr[i], sizeof(*nphdr));
nphdr->p_offset = offset;
offset += phdr[i].p_filesz;
}
*header_bytes = bytes;
return headers;
}
static void write_all(int fd, const void *buf, size_t count)
{
ssize_t result, written;
const char *ptr;
size_t left;
ptr = buf;
left = count;
do {
result = write(fd, ptr, left);
if (result >= 0) {
written += result;
ptr += result;
left -= result;
}
else if ((errno != EAGAIN) && (errno != EINTR)) {
fprintf(stderr, "write failed: %s\n",
strerror(errno));
exit(8);
}
} while(written < count);
}
int main(int argc, char **argv)
{
char *start_addr_str, *end;
unsigned long long start_addr;
Elf64_Ehdr *ehdr;
Elf64_Phdr *phdr;
void *notes, *headers;
size_t note_bytes, header_bytes;
int fd;
int i;
start_addr_str = 0;
if (argc > 2) {
fprintf(stderr, "Invalid argument count\n");
exit(9);
}
if (argc == 2) {
start_addr_str = argv[1];
}
if (!start_addr_str) {
start_addr_str = getenv("elfcorehdr");
}
if (!start_addr_str) {
fprintf(stderr, "Cannot find the start of the core dump\n");
exit(1);
}
start_addr = strtoull(start_addr_str, &end, 0);
if ((start_addr_str == end) || (*end != '\0')) {
fprintf(stderr, "Bad core dump start addres: %s\n",
start_addr_str);
exit(2);
}
fd = open(DEV_MEM, O_RDONLY);
if (fd < 0) {
fprintf(stderr, "Cannot open " DEV_MEM ": %s\n",
strerror(errno));
exit(3);
}
/* Get the elf header */
ehdr = map_addr(fd, sizeof(*ehdr), start_addr);
/* Verify the ELF header */
if ( (ehdr->e_ident[EI_MAG0] != ELFMAG0) ||
(ehdr->e_ident[EI_MAG1] != ELFMAG1) ||
(ehdr->e_ident[EI_MAG2] != ELFMAG2) ||
(ehdr->e_ident[EI_MAG3] != ELFMAG3) ||
(ehdr->e_ident[EI_CLASS] != ELFCLASS64) ||
(ehdr->e_ident[EI_DATA] != ELFDATALOCAL) ||
(ehdr->e_ident[EI_VERSION] != EV_CURRENT) ||
(ehdr->e_type != ET_CORE) ||
(ehdr->e_version != EV_CURRENT) ||
(ehdr->e_ehsize != sizeof(Elf64_Ehdr)) ||
(ehdr->e_phentsize != sizeof(Elf64_Phdr)) ||
(ehdr->e_phnum == 0))
{
fprintf(stderr, "Invalid Elf header\n");
exit(4);
}
/* Get the program header */
phdr = map_addr(fd, sizeof(*phdr)*(ehdr->e_phnum), ehdr->e_phoff);
/* Collect up the notes */
note_bytes = 0;
notes = collect_notes(fd, ehdr, phdr, &note_bytes);
/* Generate new headers */
header_bytes = 0;
headers = generate_new_headers(ehdr, phdr, note_bytes, &header_bytes);
/* Write out everything */
write_all(STDOUT_FILENO, headers, header_bytes);
write_all(STDOUT_FILENO, notes, note_bytes);
for(i = 0; i < ehdr->e_phnum; i++) {
unsigned long long offset, size;
size_t wsize;
if (phdr[i].p_type != PT_NOTE) {
continue;
}
offset = phdr[i].p_offset;
size = phdr[i].p_filesz;
wsize = MAP_WINDOW_SIZE;
if (wsize > size) {
wsize = size;
}
for(;size > 0; size -= wsize, offset += wsize) {
void *buf;
wsize = MAP_WINDOW_SIZE;
if (wsize > size) {
wsize = size;
}
buf = map_addr(fd, wsize, offset);
write_all(STDOUT_FILENO, buf, wsize);
unmap_addr(buf, wsize);
}
}
free(notes);
close(fd);
return 0;
}

40
kexec-tools.spec.in Normal file
View File

@@ -0,0 +1,40 @@
Summary: Load one kernel from another
Name: kexec-tools
Version:
Release: 0
Copyright: 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 utiltity 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
%doc News
%doc COPYING
%doc TODO
#%{_mandir}/man8/kexec.8.gz
%changelog
* Tue Dec 16 2004 Eric Biederman <ebiederman@lnxi.com>
- kexec-tools initialy packaged as an rpm.

63
kexec/Makefile Normal file
View File

@@ -0,0 +1,63 @@
#
# kexec (linux booting linux)
#
PURGATORY_HEX_C:= $(OBJDIR)/kexec/purgatory.c
$(PURGATORY_HEX_C): $(PURGATORY) $(BIN_TO_HEX)
$(MKDIR) -p $(@D)
$(BIN_TO_HEX) purgatory < $(PURGATORY) > $@
KCFLAGS:= $(CFLAGS) -Ikexec/arch/$(ARCH)/include
KEXEC_C_SRCS:= kexec/kexec.c
KEXEC_C_SRCS+= kexec/ifdown.c
KEXEC_C_SRCS+= kexec/kexec-elf.c
KEXEC_C_SRCS+= kexec/kexec-elf-exec.c
KEXEC_C_SRCS+= kexec/kexec-elf-rel.c
KEXEC_C_SRCS+= kexec/kexec-elf-boot.c
KEXEC_C_SRCS+= $(PURGATORY_HEX_C)
KEXEC_S_SRCS:=
include kexec/arch/$(ARCH)/Makefile
KEXEC_C_OBJS:= $(patsubst %.c, $(OBJDIR)/%.o, $(KEXEC_C_SRCS))
KEXEC_C_DEPS:= $(patsubst %.c, $(OBJDIR)/%.d, $(KEXEC_C_SRCS))
KEXEC_S_OBJS:= $(patsubst %.S, $(OBJDIR)/%.o, $(KEXEC_S_SRCS))
KEXEC_S_DEPS:= $(patsubst %.S, $(OBJDIR)/%.d, $(KEXEC_S_SRCS))
KEXEC_SRCS:= $(KEXEC_C_SRCS) $(KEXEC_S_SRCS)
KEXEC_OBJS:= $(KEXEC_C_OBJS) $(KEXEC_S_OBJS)
KEXEC_DEPS:= $(KEXEC_C_DEPS) $(KEXEC_S_DEPS)
KEXEC:= $(SBINDIR)/kexec
include $(KEXEC_DEPS)
$(KEXEC_C_DEPS): $(OBJDIR)/%.d: %.c
mkdir -p $(@D)
$(CC) $(KCFLAGS) -M $< | sed -e 's|$(patsubst %.d,%.o,$(@F))|$(patsubst %.d,%.o,$(@))|' > $@
$(KEXEC_S_DEPS): $(OBJDIR)/%.d: %.S
mkdir -p $(@D)
$(CC) $(KCFLAGS) -M $< | sed -e 's|$(patsubst %.d,%.o,$(@F))|$(patsubst %.d,%.o,$(@))|' > $@
$(KEXEC_C_OBJS): $(OBJDIR)/%.o: %.c $(OBJDIR)/%.d
mkdir -p $(@D)
$(CC) $(KCFLAGS) -o $@ -c $<
$(KEXEC_S_OBJS): $(OBJDIR)/%.o: %.S $(OBJDIR)/%.d
mkdir -p $(@D)
$(CC) $(KCFLAGS) -o $@ -c $<
$(KEXEC): $(KEXEC_OBJS) $(UTIL_LIB)
mkdir -p $(@D)
$(CC) $(KCFLAGS) -o $@ $(KEXEC_OBJS) $(UTIL_LIB) $(LIBS)
echo::
@echo "KEXEC_C_SRCS $(KEXEC_C_SRCS)"
@echo "KEXEC_C_DEPS $(KEXEC_C_DEPS)"
@echo "KEXEC_C_OBJS $(KEXEC_C_OBJS)"
@echo "KEXEC_S_SRCS $(KEXEC_S_SRCS)"
@echo "KEXEC_S_DEPS $(KEXEC_S_DEPS)"
@echo "KEXEC_S_OBJS $(KEXEC_S_OBJS)"
@echo "KEXEC_SRCS $(KEXEC_SRCS)"
@echo "KEXEC_DEPS $(KEXEC_DEPS)"
@echo "KEXEC_OBJS $(KEXEC_OBJS)"

View File

@@ -0,0 +1,11 @@
#ifndef KEXEC_ARCH_ALPHA_OPTIONS_H
#define KEXEC_ARCH_ALPHA_OPTIONS_H
#define OPT_ARCH_MAX (OPT_MAX+0)
#define KEXEC_ARCH_OPTIONS \
KEXEC_OPTIONS \
#define KEXEC_ARCH_OPT_STR KEXEC_OPT_STR ""
#endif /* KEXEC_ARCH_ALPHA_OPTIONS_H */

11
kexec/arch/i386/Makefile Normal file
View File

@@ -0,0 +1,11 @@
#
# kexec i386 (linux booting linux)
#
KEXEC_C_SRCS+= kexec/arch/i386/kexec-x86.c
KEXEC_C_SRCS+= kexec/arch/i386/kexec-elf-x86.c
KEXEC_C_SRCS+= kexec/arch/i386/kexec-elf-rel-x86.c
KEXEC_C_SRCS+= kexec/arch/i386/kexec-bzImage.c
KEXEC_C_SRCS+= kexec/arch/i386/kexec-multiboot-x86.c
KEXEC_C_SRCS+= kexec/arch/i386/kexec-beoboot-x86.c
KEXEC_C_SRCS+= kexec/arch/i386/kexec-nbi.c
KEXEC_C_SRCS+= kexec/arch/i386/x86-linux-setup.c

View File

@@ -0,0 +1,131 @@
/*
* 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.
*/
#define USE_LRET 0
.data
.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_size, compat_x86_64_entry32
.code64
.balign 16
compat_x86_64:
/* Compute where I am running at */
leaq compat_x86_64(%rip), %rbx
/* Relocate the code */
addq %rbx, gdt_addr(%rip)
#if !USE_LRET
addl %ebx, lm_exit_addr(%rip)
#endif
/* Lookup the 32bit start address */
movl compat_x86_64_entry32(%rip), %ebx
pushq %rbx
#if USE_LRET
/* Push the 64bit start address */
pushq $0x10
pushq lm_exit(%rip)
#endif
/* This also acts as a serializing instruction ensuring
* my self modifying code works.
*/
lgdt gdt(%rip)
#if USE_LRET
lret
#else
/* Switch to 32bit compatiblity mode */
ljmp *lm_exit_addr(%rip)
#endif
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
/* Remove the 32 bits of the 64 bit start address */
popl %eax
/* 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
ret
.balign 16
gdt: /* 0x00 unusable segment
* 0x08 unused
* so use them as the gdt ptr
*/
.word gdt_end - gdt - 1
gdt_addr:
.quad gdt - compat_x86_64
.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:
#if !USE_LRET
lm_exit_addr:
.long lm_exit - compat_x86_64
.long 0x10
#endif
compat_x86_64_entry32:
.long 0
compat_x86_64_end:
compat_x86_64_size:
.long compat_x86_64_end - compat_x86_64

View File

@@ -0,0 +1,22 @@
#ifndef KEXEC_ARCH_I386_OPTIONS_H
#define KEXEC_ARCH_I386_OPTIONS_H
#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_ARCH_MAX (OPT_MAX+5)
#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 }, \
#define KEXEC_ARCH_OPT_STR KEXEC_OPT_STR ""
#endif /* KEXEC_ARCH_I386_OPTIONS_H */

View File

@@ -0,0 +1,140 @@
/*------------------------------------------------------------ -*- 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 (len < 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( "-d, --debug Enable debugging to help spot a failure.\n"
" --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 len,
struct kexec_info *info)
{
struct beoboot_header bb_header;
const unsigned char *command_line, *kernel, *initrd;
int debug, real_mode_entry;
int opt;
int result;
#define OPT_REAL_MODE (OPT_ARCH_MAX+0)
static const struct option options[] = {
KEXEC_ARCH_OPTIONS
{ "debug", 0, 0, OPT_DEBUG },
{ "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
*/
debug = 0;
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 '?':
usage();
return -1;
case OPT_DEBUG:
debug = 1;
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,
real_mode_entry, debug);
return result;
}

View File

@@ -0,0 +1,296 @@
/*
* 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 <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.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-x86.h"
#include "x86-linux-setup.h"
#include <arch/options.h>
static const int probe_debug = 0;
int bzImage_probe(const char *buf, off_t len)
{
struct x86_linux_header header;
if (len < sizeof(header)) {
return -1;
}
memcpy(&header, buf, sizeof(header));
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( "-d, --debug Enable debugging to help spot a failure.\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"
" --initrd=FILE Use FILE as the kernel's initial ramdisk.\n"
" --ramdisk=FILE Use FILE as the kernel's initial ramdisk.\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,
int real_mode_entry, int debug)
{
struct x86_linux_header setup_header;
struct x86_linux_param_header *real_mode;
int setup_sects;
char *kernel_version;
size_t size;
int kern16_size;
unsigned long setup_base, setup_size;
struct entry32_regs regs32;
struct entry16_regs regs16;
/*
* Find out about the file I am about to load.
*/
if (kernel_len < sizeof(setup_header)) {
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;
kernel_version = ((unsigned char *)&setup_header) + 512 + setup_header.kver_addr;
if (kernel_len < kern16_size) {
fprintf(stderr, "BzImage truncated?\n");
return -1;
}
/* Load the trampoline. This must load at a higher address
* the the argument/parameter segment or the kernel will stomp
* it's gdt.
*/
elf_rel_build_load(info, &info->rhdr, purgatory, purgatory_size,
0x3000, 640*1024, -1);
/* The argument/parameter segment */
setup_size = kern16_size + command_line_len;
real_mode = xmalloc(setup_size);
memcpy(real_mode, kernel, kern16_size);
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;
}
/* 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;
add_segment(info, kernel + kern16_size, size, KERN32_BASE, size);
/* Tell the kernel what is going on */
setup_linux_bootloader_parameters(info, real_mode, setup_base,
kern16_size, command_line, command_line_len,
initrd, initrd_len);
/* Get the initial register values */
elf_rel_get_symbol(&info->rhdr, "entry16_regs", &regs16, sizeof(regs16));
elf_rel_get_symbol(&info->rhdr, "entry32_regs", &regs32, sizeof(regs32));
/*
* 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 = KERN32_BASE; /* kernel entry point */
/*
* Initialize the 16bit start information.
*/
regs16.cs = setup_base + 0x20;
regs16.ip = 0;
regs16.ss = (elf_rel_get_addr(&info->rhdr, "stack_end") - 64*1024) >> 4;
regs16.esp = 0xFFFC;
if (real_mode_entry) {
printf("Starting the kernel in real mode\n");
regs32.eip = elf_rel_get_addr(&info->rhdr, "entry16");
}
if (real_mode && 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;
}
elf_rel_set_symbol(&info->rhdr, "entry16_regs", &regs16, sizeof(regs16));
elf_rel_set_symbol(&info->rhdr, "entry16_debug_regs", &regs16, sizeof(regs16));
elf_rel_set_symbol(&info->rhdr, "entry32_regs", &regs32, sizeof(regs32));
/* Fill in the information BIOS calls would normally provide. */
if (!real_mode_entry) {
setup_linux_system_parameters(real_mode);
}
return 0;
}
int bzImage_load(int argc, char **argv, const char *buf, off_t len,
struct kexec_info *info)
{
const char *command_line;
const char *ramdisk;
char *ramdisk_buf;
off_t ramdisk_length;
int command_line_len;
int debug, real_mode_entry;
int opt;
int result;
#define OPT_APPEND (OPT_ARCH_MAX+0)
#define OPT_RAMDISK (OPT_ARCH_MAX+1)
#define OPT_REAL_MODE (OPT_ARCH_MAX+2)
static const struct option options[] = {
KEXEC_ARCH_OPTIONS
{ "debug", 0, 0, OPT_DEBUG },
{ "command-line", 1, 0, OPT_APPEND },
{ "append", 1, 0, OPT_APPEND },
{ "initrd", 1, 0, OPT_RAMDISK },
{ "ramdisk", 1, 0, OPT_RAMDISK },
{ "real-mode", 0, 0, OPT_REAL_MODE },
{ 0, 0, 0, 0 },
};
static const char short_options[] = KEXEC_ARCH_OPT_STR "d";
/*
* Parse the command line arguments
*/
debug = 0;
real_mode_entry = 0;
command_line = 0;
ramdisk = 0;
ramdisk_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 '?':
usage();
return -1;
case OPT_DEBUG:
debug = 1;
break;
case OPT_APPEND:
command_line = optarg;
break;
case OPT_RAMDISK:
ramdisk = optarg;
break;
case OPT_REAL_MODE:
real_mode_entry = 1;
break;
}
}
command_line_len = 0;
if (command_line) {
command_line_len = strlen(command_line) +1;
}
ramdisk_buf = 0;
if (ramdisk) {
ramdisk_buf = slurp_file(ramdisk, &ramdisk_length);
}
result = do_bzImage_load(info,
buf, len,
command_line, command_line_len,
ramdisk_buf, ramdisk_length,
real_mode_entry, debug);
return result;
}

View File

@@ -0,0 +1,35 @@
#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 *ehdr, 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;
}
}

View File

@@ -0,0 +1,245 @@
/*
* 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 <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <getopt.h>
#include <elf.h>
#include <x86/x86-linux.h>
#include "../../kexec.h"
#include "../../kexec-elf.h"
#include "../../kexec-elf-boot.h"
#include "x86-linux-setup.h"
#include "kexec-x86.h"
#include <arch/options.h>
static const int probe_debug = 0;
int elf_x86_probe(const char *buf, off_t len)
{
struct mem_ehdr ehdr;
int result;
result = build_elf_exec_info(buf, len, &ehdr);
if (result < 0) {
if (probe_debug) {
fprintf(stderr, "Not an ELF executable\n");
}
goto out;
}
/* Verify the architecuture specific bits */
if ((ehdr.e_machine != EM_386) && (ehdr.e_machine != EM_486)) {
/* for a different architecture */
if (probe_debug) {
fprintf(stderr, "Not x86_64 ELF executable\n");
}
result = -1;
goto out;
}
result = 0;
out:
free_elf_info(&ehdr);
return result;
}
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"
" --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;
const char *command_line;
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;
#define OPT_APPEND (OPT_ARCH_MAX+0)
#define OPT_RAMDISK (OPT_ARCH_MAX+1)
#define OPT_ARGS_ELF (OPT_ARCH_MAX+2)
#define OPT_ARGS_LINUX (OPT_ARCH_MAX+3)
#define OPT_ARGS_NONE (OPT_ARCH_MAX+4)
static const struct option options[] = {
KEXEC_ARCH_OPTIONS
{ "command-line", 1, NULL, OPT_APPEND },
{ "append", 1, NULL, OPT_APPEND },
{ "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;
command_line = 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 '?':
usage();
return -1;
case OPT_APPEND:
command_line = optarg;
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_len = 0;
if (command_line) {
command_line_len = strlen(command_line) +1;
}
/* Load the ELF executable */
elf_exec_build_load(info, &ehdr, buf, len);
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);
}
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", &regs, 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", &regs, sizeof(regs));
if (ramdisk) {
die("Ramdisks not supported with generic elf arguments");
}
}
else if (arg_style == ARG_STYLE_LINUX) {
struct x86_linux_faked_param_header *hdr;
unsigned long param_base;
const unsigned char *ramdisk_buf;
off_t ramdisk_length;
struct entry32_regs regs;
/* 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 = NULL;
ramdisk_length = 0;
if (ramdisk) {
unsigned char *ramdisk_buf;
ramdisk_buf = slurp_file(ramdisk, &ramdisk_length);
}
/* 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(&hdr->hdr);
/* Initialize the registers */
elf_rel_get_symbol(&info->rhdr, "entry32_regs", &regs, 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", &regs, sizeof(regs));
}
else {
die("Unknown argument style\n");
}
return 0;
}

View File

@@ -0,0 +1,393 @@
/*
* 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
* - support for the MULTIBOOT_AOUT_KLUDGE 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 <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <getopt.h>
#include <elf.h>
#include <boot/elf_boot.h>
#include <asm/page.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>
/* Static storage */
static char headerbuf[MULTIBOOT_SEARCH];
static struct multiboot_header *mbh = NULL;
#define BOOTLOADER "kexec"
#define BOOTLOADER_VERSION VERSION
#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;
/* First of all, check that this is an ELF file */
if ((i=elf_x86_probe(buf, buf_len)) < 0) {
return i;
}
/* 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 (i = 0; i <= (len - 12); i += 4)
{
/* Search for a multiboot header */
mbh = (struct multiboot_header *)(headerbuf + i);
if (mbh->magic != MULTIBOOT_MAGIC
|| ((mbh->magic+mbh->flags+mbh->checksum) & 0xffffffff))
{
/* Not a multiboot header */
continue;
}
if (mbh->flags & MULTIBOOT_AOUT_KLUDGE) {
/* Requires options we don't support */
fprintf(stderr,
"Found a multiboot header, but it uses "
"a non-ELF header layout,\n"
"and I can't do that (yet). Sorry.\n");
return -1;
}
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;
}
if (mbh->flags & MULTIBOOT_VIDEO_MODE) {
/* Asked for screen mode information */
/* XXX carry on regardless */
fprintf(stderr,
"BEWARE! Found a multiboot header which asks "
"for screen mode information.\n"
"BEWARE! I am NOT supplying screen mode "
"information, but loading it regardless.\n");
}
/* 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(" --module=\"MOD arg1 arg2...\" Load module MOD with command-line \"arg1...\"\n");
printf(" (can be used multiple times).\n");
}
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;
const char *command_line=NULL;
char *imagename, *cp;
struct memory_range *range;
int ranges;
struct AddrRangeDesc *mmap;
int command_line_len;
int i;
int opt;
int modules, mod_command_line_space;
#define OPT_CL (OPT_ARCH_MAX+0)
#define OPT_MOD (OPT_ARCH_MAX+1)
#define OPT_VGA (OPT_ARCH_MAX+2)
static const struct option options[] = {
KEXEC_ARCH_OPTIONS
{ "command-line", 1, 0, OPT_CL },
{ "append", 1, 0, OPT_CL },
{ "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 = "";
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 '?':
usage();
return -1;
case OPT_CL:
command_line = optarg;
break;
case OPT_MOD:
modules++;
mod_command_line_space += strlen(optarg) + 1;
break;
}
}
imagename = argv[optind];
command_line_len = strlen(command_line) + strlen(imagename) + 2;
/* Load the ELF executable */
elf_exec_build_load(info, &ehdr, buf, len);
/* Load the setup code */
elf_rel_build_load(info, &info->rhdr, purgatory, purgatory_size, 0, ULONG_MAX, 1);
/* The first segment will contain the multiboot headers:
* =============
* multiboot information (mbi)
* -------------
* kernel command line
* -------------
* bootloader name
* -------------
* module information entries
* -------------
* module command lines
* ==============
*/
mbi_bytes = (sizeof(*mbi) + command_line_len
+ strlen (BOOTLOADER " " BOOTLOADER_VERSION) + 1
+ 3) & ~3;
mbi_buf = xmalloc(mbi_bytes);
mbi = mbi_buf;
memset(mbi, 0, sizeof(*mbi));
sprintf(((char *)mbi) + sizeof(*mbi), "%s %s",
imagename, 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 */
if ((get_memory_ranges(&range, &ranges) < 0) || ranges == 0) {
fprintf(stderr, "Cannot get memory information\n");
return -1;
}
mmap = xmalloc(ranges * sizeof(*mmap));
for (i=0; i<ranges; i++) {
unsigned long long length;
length = range[i].end - range[i].start;
/* 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;
if (range[i].type == RANGE_RAM) {
mmap[i].Type = 1; /* RAM */
/* Is this the "low" memory? */
if ((range[i].start == 0)
&& (range[i].end > mem_lower))
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
mmap[i].Type = 0xbad; /* Not RAM */
}
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 */
}
/* 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,
PAGE_SIZE, 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) {
return -1;
}
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 (i=0; i<mbi->mods_count; i++) {
modp[i].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", &regs, sizeof(regs));
regs.eax = 0x2BADB002;
regs.ebx = mbi_offset;
regs.eip = ehdr.e_entry;
elf_rel_set_symbol(&info->rhdr, "entry32_regs", &regs, sizeof(regs));
return 0;
}
/*
* EOF (kexec-multiboot-x86.c)
*/

249
kexec/arch/i386/kexec-nbi.c Normal file
View File

@@ -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 <sys/mman.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 ((len < 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 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;
}
case '?':
usage();
return -1;
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;
}
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", &regs32, sizeof(regs32));
regs32.eip = hdr.execaddr.linear;
elf_rel_set_symbol(&info->rhdr, "entry32_regs32", &regs32, sizeof(regs32));
}
else {
struct entry32_regs regs32;
struct entry16_regs regs16;
/* Initialize the 16 bit registers */
elf_rel_get_symbol(&info->rhdr, "entry16_regs", &regs16, sizeof(regs16));
regs16.cs = hdr.execaddr.segoff.cs;
regs16.ip = hdr.execaddr.segoff.ip;
elf_rel_set_symbol(&info->rhdr, "entry16_regs", &regs16, sizeof(regs16));
/* Initialize the 32 bit registers */
elf_rel_get_symbol(&info->rhdr, "entry32_regs", &regs32, sizeof(regs32));
regs32.eip = elf_rel_get_addr(&info->rhdr, "entry16");
elf_rel_set_symbol(&info->rhdr, "entry32_regs", &regs32, sizeof(regs32));
}
return 0;
}

262
kexec/arch/i386/kexec-x86.c Normal file
View File

@@ -0,0 +1,262 @@
/*
* 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 <sys/utsname.h>
#include "../../kexec.h"
#include "../../kexec-elf.h"
#include "../../kexec-syscall.h"
#include "kexec-x86.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)
{
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, "%Lx-%Lx : %n",
&start, &end, &consumed);
if (count != 2)
continue;
str = line + consumed;
end = end + 1;
#if 0
printf("%016Lx-%016Lx : %s",
start, end, str);
#endif
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, "ACPI Tables\n", 12) == 0) {
type = RANGE_ACPI;
}
else if (memcmp(str, "ACPI Non-volatile Storage\n", 26) == 0) {
type = RANGE_ACPI_NVS;
}
else {
continue;
}
/* Don't report the interrupt table as ram */
if (type == RANGE_RAM && (start < 0x100)) {
start = 0x100;
}
memory_range[memory_ranges].start = start;
memory_range[memory_ranges].end = end;
memory_range[memory_ranges].type = type;
#if 0
printf("%016Lx-%016Lx : %x\n",
start, end, type);
#endif
memory_ranges++;
}
fclose(fp);
*range = memory_range;
*ranges = memory_ranges;
return 0;
}
struct file_type file_type[] = {
{ "multiboot-x86", multiboot_x86_probe, multiboot_x86_load,
multiboot_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=<buad_rate> Specify the serial port baud rate\n"
" --console-vga Enable the vga console\n"
" --console-serial Enable the serial console\n"
);
}
static struct {
uint8_t reset_vga;
uint16_t serial_base;
uint32_t serial_baud;
uint8_t console_vga;
uint8_t console_serial;
} arch_options = {
.reset_vga = 0,
.serial_base = 0x3f8,
.serial_baud = 0,
.console_vga = 0,
.console_serial = 0,
};
int arch_process_options(int argc, char **argv)
{
static const struct option options[] = {
KEXEC_ARCH_OPTIONS
{ 0, 0, NULL, 0 },
};
static const char short_options[] = KEXEC_ARCH_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;
}
}
/* Reset getopt for the next pass; called in other source modules */
opterr = 1;
optind = 1;
return 0;
}
int arch_compat_trampoline(struct kexec_info *info, unsigned long *flags)
{
int result;
struct utsname utsname;
result = uname(&utsname);
if (result < 0) {
fprintf(stderr, "uname failed: %s\n",
strerror(errno));
return -1;
}
if ( (strcmp(utsname.machine, "i386") == 0) ||
(strcmp(utsname.machine, "i486") == 0) ||
(strcmp(utsname.machine, "i586") == 0) ||
(strcmp(utsname.machine, "i686") == 0))
{
/* For compatibility with older patches
* use KEXEC_ARCH_DEFAULT instead of KEXEC_ARCH_386 here.
*/
*flags |= KEXEC_ARCH_DEFAULT;
}
else if (strcmp(utsname.machine, "x86_64") == 0)
{
*flags |= 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 = elf_rel_get_addr(&info->rhdr, "compat_x86_64");
}
else {
fprintf(stderr, "Unsupported machine type: %s\n",
utsname.machine);
return -1;
}
return 0;
}
void arch_update_purgatory(struct kexec_info *info)
{
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));
}

View File

@@ -0,0 +1,67 @@
#ifndef KEXEC_X86_H
#define KEXEC_X86_H
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;
};
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 elf_x86_probe(const char *buf, off_t len);
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,
int real_mode_entry, int debug);
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);
#endif /* KEXEC_X86_H */

View File

@@ -0,0 +1,178 @@
/*
* 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 <stdlib.h>
#include <errno.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <x86/x86-linux.h>
#include "../../kexec.h"
#include "kexec-x86.h"
#include "x86-linux-setup.h"
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 = 0x0203;
real_mode->initrd_addr_max = DEFAULT_INITRD_ADDR_MAX;
}
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 unsigned char *initrd_buf, off_t initrd_size)
{
char *cmdline_ptr;
unsigned long initrd_base, initrd_addr_max;
/* Say I'm a boot loader */
real_mode->loader_type = LOADER_TYPE_UNKNOWN;
/* No loader flags */
real_mode->loader_flags = 0;
/* Find the maximum initial ramdisk address */
initrd_addr_max = DEFAULT_INITRD_ADDR_MAX;
if (real_mode->protocol_version >= 0x0203) {
initrd_addr_max = real_mode->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);
} else {
initrd_base = 0;
initrd_size = 0;
}
/* Ramdisk address and size */
real_mode->initrd_start = initrd_base;
real_mode->initrd_size = initrd_size;
/* 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) {
real_mode->cmd_line_ptr = real_mode_base + cmdline_offset;
}
/* 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';
}
void setup_linux_system_parameters(struct x86_linux_param_header *real_mode)
{
/* Fill in information the BIOS would usually provide */
struct memory_range *range;
int i, ranges;
/* 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;
/* 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;
/* Fill in the memory info */
if ((get_memory_ranges(&range, &ranges) < 0) || ranges == 0) {
die("Cannot get memory information\n");
}
if (ranges > E820MAX) {
fprintf(stderr, "Too many memory ranges, truncating...\n");
ranges = E820MAX;
}
real_mode->e820_map_nr = ranges;
for(i = 0; i < ranges; i++) {
real_mode->e820_map[i].addr = range[i].start;
real_mode->e820_map[i].size = range[i].end - range[i].start;
switch (range[i].type) {
case RANGE_RAM:
real_mode->e820_map[i].type = E820_RAM;
break;
case RANGE_ACPI:
real_mode->e820_map[i].type = E820_ACPI;
break;
case RANGE_ACPI_NVS:
real_mode->e820_map[i].type = E820_NVS;
break;
default:
case RANGE_RESERVED:
real_mode->e820_map[i].type = E820_RESERVED;
break;
}
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;
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;
}
}
}
}

View File

@@ -0,0 +1,17 @@
#ifndef X86_LINUX_SETUP_H
#define X86_LINUX_SETUP_H
void init_linux_parameters(struct x86_linux_param_header *real_mode);
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 unsigned char *initrd_buf, off_t initrd_size);
void setup_linux_system_parameters(struct x86_linux_param_header *real_mode);
#define SETUP_BASE 0x90000
#define KERN32_BASE 0x100000 /* 1MB */
#define INITRD_BASE 0x1000000 /* 16MB */
#endif /* X86_LINUX_SETUP_H */

6
kexec/arch/ia64/Makefile Normal file
View File

@@ -0,0 +1,6 @@
#
# kexec ia64 (linux booting linux)
#
KEXEC_C_SRCS+= kexec/arch/ia64/kexec-ia64.c
KEXEC_C_SRCS+= kexec/arch/ia64/kexec-elf-ia64.c
KEXEC_C_SRCS+= kexec/arch/ia64/kexec-elf-rel-ia64.c

View File

@@ -0,0 +1,11 @@
#ifndef KEXEC_ARCH_IA64_OPTIONS_H
#define KEXEC_ARCH_IA64_OPTIONS_H
#define OPT_ARCH_MAX (OPT_MAX+0)
#define KEXEC_ARCH_OPTIONS \
KEXEC_OPTIONS \
#define KEXEC_ARCH_OPT_STR KEXEC_OPT_STR ""
#endif /* KEXEC_ARCH_IA64_OPTIONS_H */

View File

@@ -0,0 +1,141 @@
/*
* 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
*
* 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 <sys/mman.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;
/*
* 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);
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");
}
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;
int command_line_len;
unsigned long entry, max_addr;
int result;
int opt;
#define OPT_APPEND (OPT_ARCH_MAX+0)
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 "";
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 '?':
usage();
return -1;
case OPT_APPEND:
command_line = optarg;
break;
}
}
command_line_len = 0;
if (command_line) {
command_line_len = strlen(command_line) + 1;
}
/* Parse the Elf file */
result = build_elf_exec_info(buf, len, &ehdr);
if (result < 0) {
fprintf(stderr, "ELF parse failed\n");
free_elf_info(&ehdr);
return result;
}
entry = ehdr.e_entry;
max_addr = elf_max_addr(&ehdr);
/* Load the Elf data */
result = elf_exec_load(&ehdr, info);
free_elf_info(&ehdr);
if (result < 0) {
fprintf(stderr, "ELF load failed\n");
return result;
}
/* For now we don't have arguments to pass :( */
info->entry = (void *)entry;
return 0;
}

View File

@@ -0,0 +1,45 @@
#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) {
return 0;
}
if (ehdr->e_machine != EM_IA_64) {
return 0;
}
return 1;
}
void machine_apply_elf_rel(struct mem_ehdr *ehdr, unsigned long r_type,
void *location, unsigned long address, unsigned long value)
{
switch(r_type) {
case R_IA64_NONE:
break;
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_PCREL21B:
case R_IA64_LTOFF22:
case R_IA64_SEGREL64LSB:
default:
die("Unknown rela relocation: %lu\n", r_type);
break;
}
return;
overflow:
die("overflow in relocation type %lu val %Lx\n",
r_type, value);
}

View File

@@ -0,0 +1,156 @@
/*
* 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 <sys/utsname.h>
#include "../../kexec.h"
#include "../../kexec-syscall.h"
#include "kexec-ia64.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 available memory ranges. */
int get_memory_ranges(struct memory_range **range, int *ranges)
{
int memory_ranges;
/*
* /proc/iomem on ia64 does not show where all memory is. If
* that is fixed up, we can make use of that to validate
* the memory range kernel will be loade din. Until then.....
* -- Khalid Aziz
*/
/* Note that the ia64 architecture mandates all systems will
* have at least 64MB at 0-64M. The SGI altix does not follow
* that restriction, but a reasonable guess is better than nothing
* at all.
* -- Eric Biederman
*/
fprintf(stderr, "Warning assuming memory at 0-64MB is present\n");
memory_ranges = 0;
memory_range[memory_ranges].start = 0x00010000;
memory_range[memory_ranges].end = 0x10000000;
memory_range[memory_ranges].type = RANGE_RAM;
memory_ranges++;
*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)
{
}
static struct {
} arch_options = {
};
int arch_process_options(int argc, char **argv)
{
static const struct option options[] = {
KEXEC_ARCH_OPTIONS
{ 0, 0, NULL, 0 },
};
static const char short_options[] = KEXEC_ARCH_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;
}
}
/* Reset getopt for the next pass; called in other source modules */
opterr = 1;
optind = 1;
return 0;
}
int arch_compat_trampoline(struct kexec_info *info, unsigned long *flags)
{
int result;
struct utsname utsname;
result = uname(&utsname);
if (result < 0) {
fprintf(stderr, "uname failed: %s\n",
strerror(errno));
return -1;
}
if (strcmp(utsname.machine, "ia64") == 0)
{
*flags |= KEXEC_ARCH_X86_64;
}
else {
fprintf(stderr, "Unsupported machine type: %s\n",
utsname.machine);
return -1;
}
return 0;
}
int arch_compat_trampoline(struct kexec_info *info, unsigned long *flags)
{
int result;
struct utsname utsname;
result = uname(&utsname);
if (result < 0) {
fprintf(stderr, "uname failed: %s\n",
strerror(errno));
return -1;
}
if (strcmp(utsname.machine, "ia64") == 0)
{
/* For compatibility with older patches
* use KEXEC_ARCH_DEFAULT instead of KEXEC_ARCH_IA64 here.
*/
*flags |= KEXEC_ARCH_DEFAULT;
}
else {
fprintf(stderr, "Unsupported machine type: %s\n",
utsname.machine);
return -1;
}
return 0;
}
void arch_update_purgatory(struct kexec_info *info)
{
}

View File

@@ -0,0 +1,9 @@
#ifndef KEXEC_IA64_H
#define KEXEC_IA64_H
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);
#endif /* KEXEC_IA64_H */

9
kexec/arch/ppc/Makefile Normal file
View File

@@ -0,0 +1,9 @@
#
# kexec ppc (linux booting linux)
#
KEXEC_C_SRCS+= kexec/arch/ppc/kexec-ppc.c
KEXEC_C_SRCS+= kexec/arch/ppc/kexec-elf-ppc.c
KEXEC_C_SRCS+= kexec/arch/ppc/kexec-elf-rel-ppc.c
KEXEC_C_SRCS+= kexec/arch/ppc/kexec-dol-ppc.c
KEXEC_S_SRCS+= kexec/arch/ppc/ppc-setup-simple.S
KEXEC_S_SRCS+= kexec/arch/ppc/ppc-setup-dol.S

View File

@@ -0,0 +1,11 @@
#ifndef KEXEC_ARCH_PPC_OPTIONS_H
#define KEXEC_ARCH_PPC_OPTIONS_H
#define OPT_ARCH_MAX (OPT_MAX+0)
#define KEXEC_ARCH_OPTIONS \
KEXEC_OPTIONS \
#define KEXEC_ARCH_OPT_STR KEXEC_OPT_STR ""
#endif /* KEXEC_ARCH_PPC_OPTIONS_H */

View File

@@ -0,0 +1,479 @@
/*
* 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 <sys/mman.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) (((addr) + PAGE_SIZE - 1) & PAGE_MASK)
#define BOOTLOADER "kexec"
#define BOOTLOADER_VERSION VERSION
#define MAX_COMMAND_LINE 256
#define UPSZ(X) ((sizeof(X) + 3) & ~3)
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 (dol_sect_offset(h, i) + dol_sect_size(h, i) > 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
("-d, --debug Enable debugging to help spot a failure.\n"
" --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 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;
#define OPT_APPEND (OPT_ARCH_MAX+0)
static const struct option options[] = {
KEXEC_ARCH_OPTIONS
{"debug", 0, 0, OPT_DEBUG},
{"command-line", 1, 0, OPT_APPEND},
{"append", 1, 0, OPT_APPEND},
{0, 0, 0, 0},
};
static const char short_options[] = KEXEC_ARCH_OPT_STR "d";
/*
* Parse the command line arguments
*/
debug = 0;
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 '?':
usage();
return -1;
case OPT_DEBUG:
debug = 1;
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) + ((command_line_len + 3) & ~3);
arg_bytes = note_bytes + ((setup_dol_size + 3) & ~3);
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 + ((setup_dol_size + 3) & ~3));
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;
}

View File

@@ -0,0 +1,225 @@
/*
* 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 <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.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>
static const int probe_debug = 0;
#define BOOTLOADER "kexec"
#define BOOTLOADER_VERSION VERSION
#define MAX_COMMAND_LINE 256
#define UPSZ(X) ((sizeof(X) + 3) & ~3)
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,
},
};
int elf_ppc_probe(const char *buf, off_t len)
{
struct mem_ehdr ehdr;
int result;
result = build_elf_exec_info(buf, len, &ehdr);
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;
}
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"
" --gamecube=1|0 Enable/disable support for ELFs with changed\n"
" addresses suitable for the GameCube.\n");
}
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 */
}
}
int elf_ppc_load(int argc, char **argv, const char *buf, off_t len,
struct kexec_info *info)
{
struct mem_ehdr ehdr;
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 char *setup_start;
uint32_t setup_size;
int result;
#ifdef CONFIG_GAMECUBE
int target_is_gamecube = 1;
#else
int target_is_gamecube = 0;
#endif
int opt;
#define OPT_APPEND (OPT_ARCH_MAX+0)
#define OPT_GAMECUBE (OPT_ARCH_MAX+1)
static const struct option options[] = {
KEXEC_ARCH_OPTIONS
{"command-line", 1, 0, OPT_APPEND},
{"append", 1, 0, OPT_APPEND},
{"gamecube", 1, 0, OPT_GAMECUBE},
{0, 0, 0, 0},
};
static const char short_options[] = KEXEC_ARCH_OPT_STR "d";
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 '?':
usage();
return -1;
case OPT_APPEND:
command_line = optarg;
break;
case OPT_GAMECUBE:
target_is_gamecube = atoi(optarg);
break;
}
}
command_line_len = 0;
if (command_line) {
command_line_len = strlen(command_line) + 1;
}
/* Parse the Elf file */
result = build_elf_exec_info(buf, len, &ehdr);
if (result < 0) {
free_elf_info(&ehdr);
return result;
}
if (target_is_gamecube) {
gamecube_hack_addresses(&ehdr);
}
/* Load the Elf data */
result = elf_exec_load(&ehdr, info);
if (result < 0) {
free_elf_info(&ehdr);
return result;
}
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) + ((command_line_len + 3) & ~3);
arg_bytes = note_bytes + ((setup_size + 3) & ~3);
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 + ((setup_size + 3) & ~3));
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;
return 0;
}

View File

@@ -0,0 +1,64 @@
#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 *ehdr, 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_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;
}

151
kexec/arch/ppc/kexec-ppc.c Normal file
View File

@@ -0,0 +1,151 @@
/*
* 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/utsname.h>
#include "../../kexec.h"
#include "../../kexec-syscall.h"
#include "kexec-ppc.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)
{
int memory_ranges = 0;
#ifdef CONFIG_GAMECUBE
/* 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++;
#else
#error Please, fix this for your platform
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, "%Lx-%Lx : %n", &start, &end, &consumed);
if (count != 2)
continue;
str = line + consumed;
end = end + 1;
#if 0
printf("%016Lx-%016Lx : %s\n", start, end, str);
#endif
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, "ACPI Tables\n", 12) == 0) {
type = RANGE_ACPI;
} else if (memcmp(str, "ACPI Non-volatile Storage\n", 26) == 0) {
type = RANGE_ACPI_NVS;
} else {
continue;
}
memory_range[memory_ranges].start = start;
memory_range[memory_ranges].end = end;
memory_range[memory_ranges].type = type;
#if 0
printf("%016Lx-%016Lx : %x\n", start, end, type);
#endif
memory_ranges++;
}
fclose(fp);
#endif
*range = memory_range;
*ranges = memory_ranges;
return 0;
}
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},
};
int file_types = sizeof(file_type) / sizeof(file_type[0]);
void arch_usage(void)
{
}
static struct {
} arch_options = {
};
int arch_process_options(int argc, char **argv)
{
static const struct option options[] = {
KEXEC_ARCH_OPTIONS
{ 0, 0, NULL, 0 },
};
static const char short_options[] = KEXEC_ARCH_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;
}
}
/* Reset getopt for the next pass; called in other source modules */
opterr = 1;
optind = 1;
return 0;
}
int arch_compat_trampoline(struct kexec_info *info, unsigned long *flags)
{
int result;
struct utsname utsname;
result = uname(&utsname);
if (result < 0) {
fprintf(stderr, "uname failed: %s\n",
strerror(errno));
return -1;
}
if (strcmp(utsname.machine, "ppc") == 0)
{
/* For compatibility with older patches
* use KEXEC_ARCH_DEFAULT instead of KEXEC_ARCH_PPC here.
*/
*flags |= KEXEC_ARCH_DEFAULT;
}
else {
fprintf(stderr, "Unsupported machine type: %s\n",
utsname.machine);
return -1;
}
return 0;
}
void arch_update_purgatory(struct kexec_info *info)
{
}

View File

@@ -0,0 +1,28 @@
#ifndef KEXEC_PPC_H
#define KEXEC_PPC_H
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 struct {
uint32_t spr8;
} setup_dol_regs;
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 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);
#endif /* KEXEC_PPC_H */

View File

@@ -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

View File

@@ -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

506
kexec/arch/ppc/ppc_asm.h Normal file
View File

@@ -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

View File

@@ -0,0 +1,7 @@
#
# kexec ppc64 (linux booting linux)
#
KEXEC_C_SRCS+= kexec/arch/ppc64/kexec-elf-rel-ppc64.c
KEXEC_C_SRCS+= kexec/arch/ppc64/kexec-zImage-ppc64.c
KEXEC_S_SRCS+=

View File

@@ -0,0 +1,11 @@
#ifndef KEXEC_ARCH_PPC64_OPTIONS_H
#define KEXEC_ARCH_PPC64_OPTIONS_H
#define OPT_ARCH_MAX (OPT_MAX+0)
#define KEXEC_ARCH_OPTIONS \
KEXEC_OPTIONS \
#define KEXEC_ARCH_OPT_STR KEXEC_OPT_STR ""
#endif /* KEXEC_ARCH_PPC64_OPTIONS_H */

View File

@@ -0,0 +1,123 @@
/*
* 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 <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <getopt.h>
#include <linux/elf.h>
#include "../../kexec.h"
#include "../../kexec-elf.h"
#include "kexec-ppc64.h"
#define BOOTLOADER "kexec"
#define BOOTLOADER_VERSION VERSION
#define MAX_COMMAND_LINE 256
#define UPSZ(X) ((sizeof(X) + 3) & ~3)
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,
},
};
int elf_ppc64_probe(const char *buf, off_t len)
{
struct mem_ehdr ehdr;
int result;
result = build_elf_exec_info(buf, len, &ehdr);
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;
}
int elf_ppc64_load(int argc, char **argv, const char *buf, off_t len,
struct kexec_info *info)
{
struct mem_ehdr ehdr;
/* Parse command line arguments */
/* Parse the Elf file */
result = build_elf_exec_info(buf, len, &ehdr);
if (result < 0) {
free_elf_info(&ehdr);
return result;
}
/* Load the Elf data */
result = elf_exec_load(&ehdr, info);
if (result < 0) {
free_elf_info(&ehdr);
return result;
}
return 1;
}
void elf_ppc64_usage(void)
{
fprintf(stderr, "elf support is still broken\n");
}

View File

@@ -0,0 +1,95 @@
#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_PPC64) {
return 0;
}
return 1;
}
static struct mem_shdr *toc_section(const struct mem_ehdr *ehdr)
{
struct mem_shdr *shdr, *shdr_end;
unsigned char *strtab;
strtab = ehdr->e_shdr[ehdr->e_shstrndx].sh_data;
shdr_end = &ehdr->e_shdr[ehdr->shnum];
for(shdr = ehdr->e_shdr; shdr != shdr_end; shdr++) {
if (strcmp(shdr->sh_name, ".toc") == 0) {
return shdr;
}
}
return NULL;
}
/* 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) */
static 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;
}
void machine_apply_elf_rel(struct mem_ehdr *ehdr, 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:
/* Simply set it */
*(uint64_t *)location = value;
break;
case R_PPC64_TOC:
*(uint64_t *)location = my_r2(ehdr);
break;
case R_PPC64_TOC16_DS:
/* Subtact TOC pointer */
value -= my_r2(ehdr);
if ((value & 3) != 0 || value + 0x8000 > 0xffff) {
die("bad TOC16_DS relocation (%lu)\n", value);
}
*((uint16_t *) location)
= (*((uint16_t *) location) & ~0xfffc)
| (value & 0xfffc);
break;
case R_PPC64_REL24:
/* 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;
default:
die("Unknown rela relocation: %lu\n", r_type);
break;
}
return;
}

View File

@@ -0,0 +1,9 @@
#ifndef KEXEC_PPC64_H
#define KEXEC_PPC64_H
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);
#endif /* KEXEC_PPC_H */

View File

@@ -0,0 +1,173 @@
/*
* 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 <sys/mman.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;
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;
}
return (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);
}
int zImage_ppc64_load(FILE *file, int argc, char **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;
/* 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: ");
return -1;
}
if (fread(ph, sizeof(Elf32_Phdr) * elf.e_phnum, 1, file) != 1) {
perror("read error: ");
return -1;
}
*ret_segments = malloc(elf.e_phnum * sizeof(struct kexec_segment));
if (*ret_segments == 0) {
fprintf(stderr, "malloc failed: %s\n",
strerror(errno));
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");
return -1;
}
/* Load program segments */
p = ph;
segment->buf = malloc(filesize);
if (segment->buf == 0) {
perror("malloc failed: ");
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: ");
return -1;
}
mem_offset = p->p_vaddr - load_loc;
if (fread(segment->buf+mem_offset, p->p_filesz, 1, file) != 1) {
perror("read error: ");
return -1;
}
}
segment->mem = (void *) load_loc;
segment->memsz = memsize;
segment->bufsz = filesize;
*ret_entry = 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");
}

View File

@@ -0,0 +1,12 @@
#
# kexec x86_64 (linux booting linux)
#
KEXEC_C_SRCS+= kexec/arch/i386/kexec-elf-x86.c
KEXEC_C_SRCS+= kexec/arch/i386/kexec-bzImage.c
KEXEC_C_SRCS+= kexec/arch/i386/kexec-multiboot-x86.c
KEXEC_C_SRCS+= kexec/arch/i386/kexec-beoboot-x86.c
KEXEC_C_SRCS+= kexec/arch/i386/kexec-nbi.c
KEXEC_C_SRCS+= kexec/arch/i386/x86-linux-setup.c
KEXEC_C_SRCS+= kexec/arch/x86_64/kexec-x86_64.c
KEXEC_C_SRCS+= kexec/arch/x86_64/kexec-elf-x86_64.c
KEXEC_C_SRCS+= kexec/arch/x86_64/kexec-elf-rel-x86_64.c

View File

@@ -0,0 +1,22 @@
#ifndef KEXEC_ARCH_X86_64_OPTIONS_H
#define KEXEC_ARCH_X86_64_OPTIONS_H
#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_ARCH_MAX (OPT_MAX+5)
#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 }, \
#define KEXEC_ARCH_OPT_STR KEXEC_OPT_STR ""
#endif /* KEXEC_ARCH_X86_64_OPTIONS_H */

View File

@@ -0,0 +1,93 @@
#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) {
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(reloc_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 *ehdr, unsigned long r_type,
void *location, unsigned long address, unsigned long value)
{
#if 0
fprintf(stderr, "%s\n", reloc_name(r_type));
#endif
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:
*(uint32_t *)location = value - address;
break;
default:
die("Unhandled rela relocation: %lu\n", reloc_name(r_type));
break;
}
return;
overflow:
die("overflow in relocation type %s val %Lx\n",
reloc_name(r_type), value);
}

View File

@@ -0,0 +1,239 @@
/*
* 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 <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <getopt.h>
#include <elf.h>
#include <x86/x86-linux.h>
#include "../../kexec.h"
#include "../../kexec-elf.h"
#include "../../kexec-elf-boot.h"
#include "../i386/x86-linux-setup.h"
#include "kexec-x86_64.h"
#include <arch/options.h>
static const int probe_debug = 0;
int elf_x86_64_probe(const char *buf, off_t len)
{
struct mem_ehdr ehdr;
int result;
result = build_elf_exec_info(buf, len, &ehdr);
if (result < 0) {
if (probe_debug) {
fprintf(stderr, "Not an ELF executable\n");
}
goto out;
}
/* Verify the architecuture specific bits */
if (ehdr.e_machine != EM_X86_64) {
/* for a different architecture */
if (probe_debug) {
fprintf(stderr, "Not x86_64 ELF executable\n");
}
result = -1;
goto out;
}
result = 0;
out:
free_elf_info(&ehdr);
return result;
}
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"
" --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 *command_line;
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;
#define OPT_APPEND (OPT_ARCH_MAX+0)
#define OPT_RAMDISK (OPT_ARCH_MAX+1)
#define OPT_ARGS_ELF (OPT_ARCH_MAX+2)
#define OPT_ARGS_LINUX (OPT_ARCH_MAX+3)
#define OPT_ARGS_NONE (OPT_ARCH_MAX+4)
static const struct option options[] = {
KEXEC_ARCH_OPTIONS
{ "command-line", 1, NULL, OPT_APPEND },
{ "append", 1, NULL, OPT_APPEND },
{ "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;
command_line = 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 '?':
usage();
return -1;
case OPT_APPEND:
command_line = optarg;
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_len = 0;
if (command_line) {
command_line_len = strlen(command_line) +1;
}
/* Load the ELF executable */
elf_exec_build_load(info, &ehdr, buf, len);
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);
}
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", &regs, 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", &regs, sizeof(regs));
if (ramdisk) {
die("Ramdisks not supported with generic elf arguments");
}
}
else if (arg_style == ARG_STYLE_LINUX) {
struct x86_linux_faked_param_header *hdr;
unsigned long param_base;
const unsigned char *ramdisk_buf;
off_t ramdisk_length;
struct entry64_regs regs;
/* 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) {
unsigned char *ramdisk_buf;
ramdisk_buf = slurp_file(ramdisk, &ramdisk_length);
}
/* 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(&hdr->hdr);
/* Initialize the registers */
elf_rel_get_symbol(&info->rhdr, "entry64_regs", &regs, 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", &regs, sizeof(regs));
}
else {
die("Unknown argument style\n");
}
return 0;
}

View File

@@ -0,0 +1,247 @@
/*
* 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 <sys/utsname.h>
#include "../../kexec.h"
#include "../../kexec-elf.h"
#include "../../kexec-syscall.h"
#include "kexec-x86_64.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)
{
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, "%Lx-%Lx : %n",
&start, &end, &consumed);
if (count != 2)
continue;
str = line + consumed;
end = end + 1;
#if 0
printf("%016Lx-%016Lx : %s",
start, end, str);
#endif
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, "ACPI Tables\n", 12) == 0) {
type = RANGE_ACPI;
}
else if (memcmp(str, "ACPI Non-volatile Storage\n", 26) == 0) {
type = RANGE_ACPI_NVS;
}
else {
continue;
}
/* Don't report the interrupt table as ram */
if (type == RANGE_RAM && (start < 0x100)) {
start = 0x100;
}
memory_range[memory_ranges].start = start;
memory_range[memory_ranges].end = end;
memory_range[memory_ranges].type = type;
#if 0
printf("%016Lx-%016Lx : %x\n",
start, end, type);
#endif
memory_ranges++;
}
fclose(fp);
*range = memory_range;
*ranges = memory_ranges;
return 0;
}
struct file_type file_type[] = {
{ "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 },
{ "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=<buad_rate> Specify the serial port baud rate\n"
" --console-vga Enable the vga console\n"
" --console-serial Enable the serial console\n"
);
}
static struct {
uint8_t reset_vga;
uint16_t serial_base;
uint32_t serial_baud;
uint8_t console_vga;
uint8_t console_serial;
} arch_options = {
.reset_vga = 0,
.serial_base = 0x3f8,
.serial_baud = 0,
.console_vga = 0,
.console_serial = 0,
};
int arch_process_options(int argc, char **argv)
{
static const struct option options[] = {
KEXEC_ARCH_OPTIONS
{ 0, 0, NULL, 0 },
};
static const char short_options[] = KEXEC_ARCH_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;
}
}
/* Reset getopt for the next pass; called in other source modules */
opterr = 1;
optind = 1;
return 0;
}
int arch_compat_trampoline(struct kexec_info *info, unsigned long *flags)
{
int result;
struct utsname utsname;
result = uname(&utsname);
if (result < 0) {
fprintf(stderr, "uname failed: %s\n",
strerror(errno));
return -1;
}
if (strcmp(utsname.machine, "x86_64") == 0)
{
/* For compatibility with older patches
* use KEXEC_ARCH_DEFAULT instead of KEXEC_ARCH_X86_64 here.
*/
*flags |= KEXEC_ARCH_DEFAULT;
}
else {
fprintf(stderr, "Unsupported machine type: %s\n",
utsname.machine);
return -1;
}
return 0;
}
void arch_update_purgatory(struct kexec_info *info)
{
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));
}

View File

@@ -0,0 +1,31 @@
#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);
#endif /* KEXEC_X86_64_H */

74
kexec/ifdown.c Normal file
View File

@@ -0,0 +1,74 @@
/*
* 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 <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/errno.h>
#include <net/if.h>
#include <netinet/in.h>
#define MAX_IFS 64
/*
* 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 ifreq ifr[MAX_IFS];
struct ifconf ifc;
int i, fd;
int numif;
int shaper;
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
fprintf(stderr, "ifdown: ");
perror("socket");
return -1;
}
ifc.ifc_len = sizeof(ifr);
ifc.ifc_req = ifr;
if (ioctl(fd, SIOCGIFCONF, &ifc) < 0) {
fprintf(stderr, "ifdown: ");
perror("SIOCGIFCONF");
close(fd);
return -1;
}
numif = ifc.ifc_len / sizeof(struct ifreq);
for (shaper = 1; shaper >= 0; shaper--) {
for (i = 0; i < numif; i++) {
if ((strncmp(ifr[i].ifr_name, "shaper", 6) == 0)
!= shaper) continue;
if (strcmp(ifr[i].ifr_name, "lo") == 0)
continue;
if (strchr(ifr[i].ifr_name, ':') != NULL)
continue;
ifr[i].ifr_flags &= ~(IFF_UP);
if (ioctl(fd, SIOCSIFFLAGS, &ifr[i]) < 0) {
fprintf(stderr, "ifdown: shutdown ");
perror(ifr[i].ifr_name);
}
}
}
close(fd);
return 0;
}

95
kexec/kexec-elf-boot.c Normal file
View File

@@ -0,0 +1,95 @@
/*
* 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) ((sizeof(X) + 3) &~3)
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,
},
};
static inline unsigned long align(unsigned long val, unsigned long align)
{
return (val + align - 1) & ~(align - 1);
}
unsigned long elf_boot_notes(
struct kexec_info *info, unsigned long max_addr,
const unsigned 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;
}

8
kexec/kexec-elf-boot.h Normal file
View File

@@ -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 unsigned char *cmdline, int cmdline_len);
#endif /* KEXEC_ELF_BOOT_H */

149
kexec/kexec-elf-exec.c Normal file
View File

@@ -0,0 +1,149 @@
#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;
int build_elf_exec_info(const char *buf, off_t len, struct mem_ehdr *ehdr)
{
struct mem_phdr *phdr, *end_phdr;
int result;
result = build_elf_info(buf, len, ehdr);
if (result < 0) {
return result;
}
if ((ehdr->e_type != ET_EXEC) && (ehdr->e_type != ET_DYN)) {
/* 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(const struct mem_ehdr *ehdr, struct kexec_info *info)
{
unsigned long base;
int result;
int i;
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) {
unsigned long first, last, align;
first = ULONG_MAX;
last = 0;
align = 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 (start > first) {
start = first;
}
if (last < stop) {
last = stop;
}
if (align < phdr->p_align) {
align = phdr->p_align;
}
}
/* If I can't use the default paddr find a new
* hole for the dynamic executable.
*/
if (!valid_memory_range(first, last)) {
unsigned long hole;
hole = locate_hole(info,
last - first + 1, align,
0, elf_max_addr(ehdr), 1);
if (hole == ULONG_MAX) {
result = -1;
goto out;
}
/* Base is the value that when added
* to any virtual address in the file
* yields it's load virtual address.
*/
base = hole - first;
}
}
/* 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);
}
result = 0;
out:
return result;
}
void elf_exec_build_load(struct kexec_info *info, struct mem_ehdr *ehdr,
const char *buf, off_t len)
{
int result;
/* Parse the Elf file */
result = build_elf_exec_info(buf, len, ehdr);
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");
}
}

531
kexec/kexec-elf-rel.c Normal file
View File

@@ -0,0 +1,531 @@
#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;
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;
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;
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)
{
int result;
result = build_elf_info(buf, len, ehdr);
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;
}
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 = shdr->sh_addralign;
/* See if I need more alignment */
if (buf_align < align) {
buf_align = align;
}
/* Now align bufsz */
bufsz = (bufsz + (align - 1)) & ~(align - 1);
/* And now add our buffer */
bufsz += shdr->sh_size;
}
else {
unsigned long align;
align = shdr->sh_addralign;
/* See if I need more alignment */
if (bss_align < align) {
bss_align = align;
}
/* Now align bsssz */
bsssz = (bsssz + (align - 1)) & ~(align -1);
/* 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 = shdr->sh_addralign;
if (shdr->sh_type != SHT_NOBITS) {
unsigned long off;
/* Adjust the address */
data_addr = (data_addr + (align - 1)) & ~(align -1);
/* 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 = (bss_addr + (align - 1)) & ~(align -1);
/* 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;
struct mem_sym sym;
const void *location;
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 0
fprintf(stderr, "sym: %10s info: %02x other: %02x shndx: %lx value: %lx size: %lx\n",
strtab + sym.st_name,
sym.st_info,
sym.st_other,
sym.st_shndx,
sym.st_value,
sym.st_size);
#endif
if (sym.st_shndx == STN_UNDEF) {
die("Undefined symbol: %s\n",
strtab + sym.st_name);
}
sec_base = 0;
if (sym.st_shndx == SHN_COMMON) {
die("symbol: '%s' in common section\n",
strtab + sym.st_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,
strtab + sym.st_name);
}
else {
sec_base = ehdr->e_shdr[sym.st_shndx].sh_addr;
}
#if 0
fprintf(stderr, "sym: %s value: %lx addr: %lx\n",
strtab + sym.st_name, value, address);
#endif
value = sym.st_value;
value += sec_base;
value += rel.r_addend;
machine_apply_elf_rel(ehdr, 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)
{
int result;
/* Parse the Elf file */
result = build_elf_rel_info(purgatory, purgatory_size, ehdr);
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 = 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: %ld not %ld\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");
}
if (sym.st_size != size) {
die("Symbol: %s has size: %ld not %ld\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);
}

760
kexec/kexec-elf.c Normal file
View File

@@ -0,0 +1,760 @@
#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;
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 (len < 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) > ULONG_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) > ULONG_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) > ULONG_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 (len < 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) > ULONG_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) > ULONG_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) > ULONG_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 (len < 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, off_t len,
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) > ULONG_MAX) ||
(elf32_to_cpu(ehdr, lphdr.p_memsz) > ULONG_MAX) ||
(elf32_to_cpu(ehdr, lphdr.p_offset) > ULONG_MAX) ||
(elf32_to_cpu(ehdr, lphdr.p_paddr) > ULONG_MAX) ||
(elf32_to_cpu(ehdr, lphdr.p_vaddr) > ULONG_MAX) ||
(elf32_to_cpu(ehdr, lphdr.p_align) > ULONG_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, off_t len,
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) > ULONG_MAX) ||
(elf64_to_cpu(ehdr, lphdr.p_memsz) > ULONG_MAX) ||
(elf64_to_cpu(ehdr, lphdr.p_offset) > ULONG_MAX) ||
(elf64_to_cpu(ehdr, lphdr.p_paddr) > ULONG_MAX) ||
(elf64_to_cpu(ehdr, lphdr.p_vaddr) > ULONG_MAX) ||
(elf64_to_cpu(ehdr, lphdr.p_align) > ULONG_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)
{
size_t phdr_size, mem_phdr_size;
int 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 (ehdr->e_phoff + phdr_size > len) {
/* The program header did not fit in the file buffer */
if (probe_debug) {
fprintf(stderr, "ELF program segment truncated\n");
}
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, len, ehdr, i);
}
else if (ehdr->ei_class == ELFCLASS64) {
result = build_mem_elf64_phdr(buf, len, ehdr, i);
}
if (result < 0) {
return result;
}
/* Check the program headers to be certain
* they are safe to use.
*/
phdr = &ehdr->e_phdr[i];
if ((phdr->p_offset + phdr->p_filesz) > 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 + 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, off_t len,
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) > ULONG_MAX) ||
(elf32_to_cpu(ehdr, lshdr.sh_addr) > ULONG_MAX) ||
(elf32_to_cpu(ehdr, lshdr.sh_offset) > ULONG_MAX) ||
(elf32_to_cpu(ehdr, lshdr.sh_size) > ULONG_MAX) ||
(elf32_to_cpu(ehdr, lshdr.sh_addralign) > ULONG_MAX) ||
(elf32_to_cpu(ehdr, lshdr.sh_entsize) > ULONG_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: %ld\n",
shdr->sh_type, shdr->sh_entsize);
return -1;
}
return 0;
}
static int build_mem_elf64_shdr(const char *buf, off_t len,
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) > ULONG_MAX) ||
(elf64_to_cpu(ehdr, lshdr.sh_addr) > ULONG_MAX) ||
(elf64_to_cpu(ehdr, lshdr.sh_offset) > ULONG_MAX) ||
(elf64_to_cpu(ehdr, lshdr.sh_size) > ULONG_MAX) ||
(elf64_to_cpu(ehdr, lshdr.sh_addralign) > ULONG_MAX) ||
(elf64_to_cpu(ehdr, lshdr.sh_entsize) > ULONG_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: %ld\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)
{
size_t shdr_size, mem_shdr_size;
int 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 (ehdr->e_shoff + shdr_size > 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, len, ehdr, i);
}
else if (ehdr->ei_class == ELFCLASS64) {
result = build_mem_elf64_shdr(buf, len, ehdr, i);
}
if (result < 0) {
return result;
}
/* Check the section headers to be certain
* they are safe to use.
*/
shdr = &ehdr->e_shdr[i];
if ((shdr->sh_type != SHT_NOBITS) &&
((shdr->sh_offset + shdr->sh_size) > len))
{
/* The section does not fit in the buffer */
if (probe_debug) {
fprintf(stderr, "ELF section %d 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 = 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(const char *buf, off_t len, struct mem_ehdr *ehdr)
{
const unsigned char *note_start, *note_end, *note;
size_t note_size;
int 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];
if (phdr->p_type == PT_NOTE) {
note_start = 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 += (hdr.n_namesz + 3) & ~3;
note_size += (hdr.n_descsz + 3) & ~3;
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 += (hdr.n_namesz + 3) & ~3;
desc = note + note_size;
note_size += (hdr.n_descsz + 3) & ~3;
if ((hdr.n_namesz != 0) && (name[hdr.n_namesz -1] != '\0')) {
die("Note name is not null termiated");
}
ehdr->e_note[i].n_type = hdr.n_type;
ehdr->e_note[i].n_name = 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)
{
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);
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);
if (result < 0) {
free_elf_info(ehdr);
return result;
}
}
result = build_mem_notes(buf, len, ehdr);
if (result < 0) {
free_elf_info(ehdr);
return result;
}
return 0;
}

125
kexec/kexec-elf.h Normal file
View File

@@ -0,0 +1,125 @@
#ifndef KEXEC_ELF_H
#define KEXEC_ELF_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 e_entry;
unsigned long e_phoff;
unsigned 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 p_paddr;
unsigned long p_vaddr;
unsigned long p_filesz;
unsigned long p_memsz;
unsigned long p_offset;
const char *p_data;
unsigned p_type;
unsigned p_flags;
unsigned p_align;
};
struct mem_shdr {
unsigned sh_name;
unsigned sh_type;
unsigned long sh_flags;
unsigned long sh_addr;
unsigned long sh_offset;
unsigned long sh_size;
unsigned sh_link;
unsigned sh_info;
unsigned long sh_addralign;
unsigned 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 long st_shndx; /* Section index */
unsigned long st_value; /* Symbol value */
unsigned long st_size; /* Symbol size */
};
struct mem_rela {
unsigned long r_offset;
unsigned long r_sym;
unsigned long r_type;
unsigned 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;
extern void free_elf_info(struct mem_ehdr *ehdr);
extern int build_elf_info(const char *buf, off_t len, struct mem_ehdr *ehdr);
extern int build_elf_exec_info(const char *buf, off_t len, struct mem_ehdr *ehdr);
extern int build_elf_rel_info(const char *buf, off_t len, struct mem_ehdr *ehdr);
extern int elf_exec_load(const struct mem_ehdr *ehdr, struct kexec_info *info);
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);
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);
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, unsigned long r_type,
void *location, unsigned long address, unsigned long value);
#endif /* KEXEC_ELF_H */

11
kexec/kexec-sha256.h Normal file
View File

@@ -0,0 +1,11 @@
#ifndef KEXEC_SHA256_H
#define KEXEC_SHA256_H
struct sha256_region {
const void *start;
unsigned long len;
};
#define SHA256_REGIONS 8
#endif /* KEXEC_SHA256_H */

73
kexec/kexec-syscall.h Normal file
View File

@@ -0,0 +1,73 @@
#ifndef KEXEC_SYSCALL_H
#define KEXEC_SYSCALL_H
#define __LIBRARY__
#include <syscall.h>
#include <sys/syscall.h>
#include <unistd.h>
#define LINUX_REBOOT_MAGIC1 0xfee1dead
#define LINUX_REBOOT_MAGIC2 672274793
#define LINUX_REBOOT_MAGIC2A 85072278
#define LINUX_REBOOT_MAGIC2B 369367448
#define LINUX_REBOOT_CMD_RESTART 0x01234567
#define LINUX_REBOOT_CMD_HALT 0xCDEF0123
#define LINUX_REBOOT_CMD_CAD_ON 0x89ABCDEF
#define LINUX_REBOOT_CMD_CAD_OFF 0x00000000
#define LINUX_REBOOT_CMD_POWER_OFF 0x4321FEDC
#define LINUX_REBOOT_CMD_RESTART2 0xA1B2C3D4
#define LINUX_REBOOT_CMD_EXEC_KERNEL 0x18273645
#define LINUX_REBOOT_CMD_KEXEC_OLD 0x81726354
#define LINUX_REBOOT_CMD_KEXEC_OLD2 0x18263645
#define LINUX_REBOOT_CMD_KEXEC 0x45584543
#ifdef __i386__
#define __NR_kexec_load 283
#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
#ifndef __NR_kexec_load
#error Unknown processor architecture. Needs a kexec_load syscall number.
#endif
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 long kexec_reboot(void)
{
return (long) syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_KEXEC, 0);
}
#define KEXEC_ON_CRASH 0x00000001
#define KEXEC_ARCH_MASK 0xffff0000
/* 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_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_MAX_SEGMENTS 8
#endif /* KEXEC_SYSCALL_H */

45
kexec/kexec.8 Normal file
View File

@@ -0,0 +1,45 @@
.\" 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 KEXEC-TOOLS 8 "October 13, 2004"
.\" 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
kexec-tools \- Tool to load a kernel for warm reboot and initiate a warm reboot
.SH SYNOPSIS
.B kexec-tools
.RI [ options ] " files" ...
.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.
\fBkexec-tools\fP does not have a man page yet. Please use "kexec -h" for help.
.SH OPTIONS
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.
.TP
.B \-h, \-\-help
Show summary of options.
.TP
.B \-v, \-\-version
Show version of program.
.SH SEE ALSO
.SH AUTHOR
kexec-tools was written by Eric Biederman.
.PP
This manual page was written by Khalid Aziz <khalid_aziz@hp.com>,
for the Debian project (but may be used by others).

810
kexec/kexec.c Normal file
View File

@@ -0,0 +1,810 @@
/*
* 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 <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 <getopt.h>
#ifdef HAVE_ZLIB_H
#include <zlib.h>
#endif
#include <sha256.h>
#include "kexec.h"
#include "kexec-syscall.h"
#include "kexec-elf.h"
#include "kexec-sha256.h"
static unsigned long long mem_min = 0;
static unsigned long long mem_max = ULONG_MAX;
void die(char *fmt, ...)
{
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
fflush(stdout);
fflush(stderr);
exit(1);
}
void *xmalloc(size_t size)
{
void *buf;
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;
}
/* local variables */
static struct memory_range *memory_range;
static int memory_ranges;
int valid_memory_range(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 < memory_ranges; i++) {
unsigned long mstart, mend;
/* Only consider memory ranges */
if (memory_range[i].type != RANGE_RAM)
continue;
mstart = memory_range[i].start;
mend = memory_range[i].end;
/* Check to see if we are fully contained */
if ((mstart <= sstart) && (mend >= send)) {
return 1;
}
}
return 0;
}
int valid_memory_segment(struct kexec_segment *segment)
{
unsigned long sstart, send;
sstart = (unsigned long)segment->mem;
send = sstart + segment->memsz - 1;
return valid_memory_range(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 = %zx\n", i, info->segment[i].bufsz);
fprintf(f, "segment[%d].mem = %p\n", i, info->segment[i].mem);
fprintf(f, "segment[%d].memsz = %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 intial invalid value for the hole base */
hole_base = ULONG_MAX;
/* Ensure I have a sane alignment value */
if (hole_align == 0) {
hole_align = 1;
}
/* Align everything to at least a page size boundary */
if (hole_align < getpagesize()) {
hole_align = getpagesize();
}
/* Compute the free memory ranges */
max_mem_ranges = memory_ranges + (info->nr_segments -1);
mem_range = malloc(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 < memory_ranges; i++) {
unsigned long sstart, send;
unsigned long mstart, mend;
mstart = memory_range[i].start;
mend = memory_range[i].end;
if (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 = (start + hole_align - 1) & ~(hole_align - 1);
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 (size >= hole_size) {
if (hole_end > 0) {
hole_base = start;
break;
} else {
hole_base = (end - hole_size) & ~(hole_align - 1);
}
}
}
if (hole_base == ULONG_MAX) {
fprintf(stderr, "Could not find a free area of memory of %lx bytes...\n",
hole_size);
return ULONG_MAX;
}
if ((hole_base + hole_size) > hole_max) {
fprintf(stderr, "Could not find a free area of memory below: %lx...\n",
hole_max);
return ULONG_MAX;
}
return hole_base;
}
void add_segment(struct kexec_info *info,
const void *buf, size_t bufsz,
unsigned long base, size_t memsz)
{
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 = (memsz + (pagesize - 1)) & ~(pagesize - 1);
/* 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: %x is not page aligned\n", base);
}
last = base + memsz -1;
if (!valid_memory_range(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(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)
{
unsigned long base;
int result;
result = sort_segments(info);
if (result < 0) {
die("sort_segments failed\n");
}
base = locate_hole(info, memsz, buf_align, buf_min, buf_max, buf_end);
if (base == ULONG_MAX) {
die("locate_hole failed\n");
}
add_segment(info, buf, bufsz, base, memsz);
return base;
}
char *slurp_file(const char *filename, off_t *r_size)
{
int fd;
char *buf;
off_t size, progress;
ssize_t result;
struct stat stats;
if (!filename) {
*r_size = 0;
return 0;
}
fd = open(filename, O_RDONLY);
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));
}
size = stats.st_size;
*r_size = size;
buf = xmalloc(size);
progress = 0;
while(progress < size) {
result = read(fd, buf + progress, size - progress);
if (result < 0) {
if ((errno == EINTR) || (errno == EAGAIN))
continue;
die("read on %s of %ld bytes failed: %s\n",
filename, (size - progress)+ 0UL, strerror(errno));
}
progress += result;
}
result = close(fd);
if (result < 0) {
die("Close of %s failed: %s\n",
filename, strerror(errno));
}
return buf;
}
#if HAVE_ZLIB_H
char *slurp_decompress_file(const char *filename, off_t *r_size)
{
gzFile fp;
int errnum;
const char *msg;
char *buf;
off_t size, allocated;
ssize_t result;
if (!filename) {
*r_size = 0;
return 0;
}
fp = gzopen(filename, "rb");
if (fp == 0) {
msg = gzerror(fp, &errnum);
if (errnum == Z_ERRNO) {
msg = strerror(errno);
}
die("Cannot open `%s': %s\n", filename, msg);
}
size = 0;
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;
msg = gzerror(fp, &errnum);
if (errnum == Z_ERRNO) {
msg = strerror(errno);
}
die ("read on %s of %ld bytes failed: %s\n",
filename, (allocated - size) + 0UL, msg);
}
size += result;
} while(result > 0);
result = gzclose(fp);
if (result != Z_OK) {
msg = gzerror(fp, &errnum);
if (errnum == Z_ERRNO) {
msg = strerror(errno);
}
die ("Close of %s failed: %s\n", filename, msg);
}
*r_size = size;
return buf;
}
#else
char *slurp_decompress_file(const char *filename, off_t *r_size)
{
return slurp_file(filename, r_size);
}
#endif
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);
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 = info->segment[i].mem;
region[j].len = info->segment[i].memsz;
j++;
}
sha256_finish(&ctx, digest);
elf_rel_set_symbol(&info->rhdr, "sha256_regions", &region, 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)
{
char *kernel;
char *kernel_buf;
off_t kernel_size;
int i = 0;
int result;
struct kexec_info info;
int guess_only = 0;
memset(&info, 0, sizeof(info));
info.segment = NULL;
info.nr_segments = 0;
info.entry = NULL;
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);
#if 0
fprintf(stderr, "kernel: %p kernel_size: %lx\n",
kernel_buf, kernel_size);
#endif
if (get_memory_ranges(&memory_range, &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;
}
}
}
if (file_type[i].load(argc, argv, kernel_buf, kernel_size, &info) < 0) {
fprintf(stderr, "Cannot load %s\n", kernel);
return -1;
}
/* If we are not in native mode setup an appropriate trampoline */
if (arch_compat_trampoline(&info, &kexec_flags) < 0) {
return -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.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 0
fprintf(stderr, "kexec_load: entry = %p flags = %lx\n",
info.entry, kexec_flags);
print_segments(stderr, &info);
#endif
result = kexec_load(
info.entry, info.nr_segments, info.segment, 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 = %lx\n",
info.entry, kexec_flags);
print_segments(stderr, &info);
}
return result;
}
int k_unload (unsigned long kexec_flags)
{
int result;
result = kexec_load(NULL, 0, NULL, kexec_flags);
if (result != 0) {
/* The unload failed, print some debugging information */
fprintf(stderr, "kexec_load (0 segments) failed: %s\n",
strerror(errno));
}
return result;
}
/*
* Start a reboot.
*/
static int my_shutdown(void)
{
char *args[8];
int i = 0;
args[i++] = "shutdown";
args[i++] = "-r";
args[i++] = "now";
args[i++] = NULL;
execv("/sbin/shutdown", args);
execv("/etc/shutdown", args);
execv("/bin/shutdown", args);
perror("shutdown");
return -1;
}
/*
* Exec the new kernel (reboot)
*/
static int my_exec(void)
{
int result;
result = kexec_reboot();
/* I have failed if I make it here */
fprintf(stderr, "kexec failed: %s\n",
strerror(errno));
return -1;
}
static void version(void)
{
printf("kexec " VERSION " released " RELEASE_DATE "\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, don't call shutdown.\n"
" -x, --no-ifdown Don't bring down network interfaces.\n"
" (if used, must be last option specified)\n"
" -l, --load Load the new kernel into the current kernel.\n"
" -p, --load-panic Load the new kernel for use on panic.\n"
" -u, --unload Unload the current kexec target kernel.\n"
" -e, --exec Execute a currently loaded kernel.\n"
" -t, --type=TYPE Specify the new kernel is of this type.\n"
" --mem-min=<addr> Specify the lowest memory addres to load code into.\n"
" --mem-max=<addr> Specify the highest memory addres to load code into.\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");
}
int main(int argc, char *argv[])
{
int do_load = 1;
int do_exec = 0;
int do_shutdown = 1;
int do_sync = 1;
int do_ifdown = 0;
int do_unload = 0;
unsigned long kexec_flags = 0;
char *type = 0;
char *endptr;
int opt;
int result = 0;
int fileind;
static const struct option options[] = {
KEXEC_OPTIONS
{ 0, 0, 0, 0},
};
static const char short_options[] = KEXEC_OPT_STR;
opterr = 0; /* Don't complain about unrecognized options here */
while ((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) {
switch(opt) {
case OPT_HELP:
usage();
return 0;
case OPT_VERSION:
version();
return 0;
case OPT_NOIFDOWN:
do_ifdown = 0;
break;
case OPT_FORCE:
do_load = 1;
do_shutdown = 0;
do_sync = 1;
do_ifdown = 1;
do_exec = 1;
break;
case OPT_LOAD:
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;
break;
case OPT_EXEC:
do_load = 0;
do_shutdown = 0;
do_sync = 1;
do_ifdown = 1;
do_exec = 1;
break;
case OPT_TYPE:
type = optarg;
break;
case OPT_PANIC:
do_load = 1;
do_exec = 0;
do_shutdown = 0;
do_sync = 0;
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;
default:
break;
}
}
fileind = optind;
/* Reset getopt for the next pass; called in other source modules */
opterr = 1;
optind = 1;
result = arch_process_options(argc, argv);
if (do_unload) {
result = k_unload(kexec_flags);
}
if (do_load && (result == 0)) {
result = my_load(type, fileind, argc, argv, kexec_flags);
}
if ((result == 0) && do_shutdown) {
result = my_shutdown();
}
if ((result == 0) && do_sync) {
sync();
}
if ((result == 0) && do_ifdown) {
extern int ifdown(void);
(void)ifdown();
}
if ((result == 0) && do_exec) {
result = my_exec();
}
fflush(stdout);
fflush(stderr);
return result;
}

194
kexec/kexec.h Normal file
View File

@@ -0,0 +1,194 @@
#ifndef KEXEC_H
#define KEXEC_H
#include <sys/types.h>
#include <stdint.h>
#define USE_BSD
#include <byteswap.h>
#include <endian.h>
#define _GNU_SOURCE
#include "kexec-elf.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
#if 0
/*
* 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)) value; \
size_t size = sizeof(*(loc)); \
switch(size) { \
case 1: case 2: case 4: case 8: \
memcpy(&value, (loc), size); \
break; \
default: \
value = bad_unaligned_access_length(); \
break; \
} \
value; \
})
#define put_unaligned(value, loc) \
do { \
size_t size = sizeof(*(loc)); \
__typeof__(*(loc)) val = value; \
switch(size) { \
case 1: case 2: case 4: case 8: \
memcpy((loc), &val, size); \
break; \
default: \
bad_unaligned_access_length(); \
break; \
} \
} while(0)
#endif
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
};
struct kexec_info {
struct kexec_segment *segment;
int nr_segments;
void *entry;
struct mem_ehdr rhdr;
};
void usage(void);
int get_memory_ranges(struct memory_range **range, int *ranges);
int valid_memory_range(unsigned long sstart, unsigned long send);
int valid_memory_segment(struct kexec_segment *segment);
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_NOIFDOWN 'x'
#define OPT_EXEC 'e'
#define OPT_LOAD 'l'
#define OPT_UNLOAD 'u'
#define OPT_TYPE 't'
#define OPT_PANIC 'p'
#define OPT_MEM_MIN 256
#define OPT_MEM_MAX 257
#define OPT_MAX 258
#define KEXEC_OPTIONS \
{ "help", 0, 0, OPT_HELP }, \
{ "version", 0, 0, OPT_VERSION }, \
{ "force", 0, 0, OPT_FORCE }, \
{ "no-ifdown", 0, 0, OPT_NOIFDOWN }, \
{ "load", 0, 0, OPT_LOAD }, \
{ "unload", 0, 0, OPT_UNLOAD }, \
{ "exec", 0, 0, OPT_EXEC }, \
{ "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 }, \
#define KEXEC_OPT_STR "hvdfxluet:p"
extern void die(char *fmt, ...);
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_decompress_file(const char *filename, off_t *r_size);
extern void add_segment(struct kexec_info *info,
const void *buf, size_t bufsz, unsigned long base, size_t memsz);
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 char purgatory[];
extern size_t purgatory_size;
#define BOOTLOADER "kexec"
#define BOOTLOADER_VERSION VERSION
void arch_usage(void);
int arch_process_options(int argc, char **argv);
int arch_compat_trampoline(struct kexec_info *info, unsigned long *flags);
void arch_update_purgatory(struct kexec_info *info);
#endif /* KEXEC_H */

30
kexec_test/Makefile Normal file
View File

@@ -0,0 +1,30 @@
#
# kexec_test Debugging payload to be certain the infrastructure works
#
RELOC:=0x10000
KEXEC_TEST_S_SRCS:= kexec_test/kexec_test16.S kexec_test/kexec_test.S
KEXEC_TEST_S_TEMPS:=$(patsubst %.S, $(OBJDIR)/%.s, $(KEXEC_TEST_S_SRCS))
KEXEC_TEST_S_OBJS:=$(patsubst $(OBJDIR)/%.s, $(OBJDIR)/%.o, $(KEXEC_TEST_S_TEMPS))
KEXEC_TEST_S_DEPS:=$(patsubst %.S, $(OBJDIR)/%.d, $(KEXEC_TEST_S_SRCS))
KEXEC_TEST_SRCS:= $(KEXEC_TEST_S_SRCS)
KEXEC_TEST_OBJS:= $(KEXEC_TEST_S_OBJS)
KEXEC_TEST_DEPS:= $(KEXEC_TEST_S_DEPS)
KEXEC_TEST:=$(PKGLIBDIR)/kexec_test
include $(KEXEC_TEST_DEPS)
$(KEXEC_TEST_S_DEPS): $(OBJDIR)/%.d: %.S
mkdir -p $(@D)
$(CC) -m32 $(CFLAGS) -M $< | sed -e 's|$(patsubst %.d,%.o,$(@F))|$(patsubst %.d,%.o,$(@))|' > $@
$(KEXEC_TEST_S_TEMPS): $(OBJDIR)/%.s: %.S
mkdir -p $(@D)
$(CPP) $(CPPFLAGS) -DRELOC=$(RELOC) $< > $@
$(KEXEC_TEST_S_OBJS): $(OBJDIR)/%.o: $(OBJDIR)/%.s
mkdir -p $(@D)
$(AS) --32 -o $@ $<
$(KEXEC_TEST): $(KEXEC_TEST_OBJS)
mkdir -p $(@D)
$(LD) -m elf_i386 -e _start -Ttext $(RELOC) $(KEXEC_TEST_OBJS) -o $@

475
kexec_test/kexec_test.S Normal file
View File

@@ -0,0 +1,475 @@
/*
* 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.
*/
.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 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

1004
kexec_test/kexec_test16.S Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -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

68
purgatory/Makefile Normal file
View File

@@ -0,0 +1,68 @@
#
# 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.
PCFLAGS:=-Wall -Os \
-I$(shell $(CC) -print-file-name=include) \
-Ipurgatory/include -Ipurgatory/arch/$(ARCH)/include \
$(CPPFLAGS)
PCFLAGS += $(call cc-option, -ffreestanding)
PCFLAGS += $(call cc-option, -fnobuiltin)
PCFLAGS += $(call cc-option, -fnostdinc)
PCFLAGS += $(call cc-option, -fno-zero-initialized-in-bss)
PURGATORY_C_SRCS:=
PURGATORY_C_SRCS += purgatory/purgatory.c
PURGATORY_C_SRCS += purgatory/printf.c
PURGATORY_C_SRCS += purgatory/string.c
PURGATORY_S_OBJS:=
include purgatory/arch/$(ARCH)/Makefile
PURGATORY_C_OBJS:= $(patsubst %.c, $(OBJDIR)/%.o, $(PURGATORY_C_SRCS))
PURGATORY_C_DEPS:= $(patsubst %.c, $(OBJDIR)/%.d, $(PURGATORY_C_SRCS))
PURGATORY_S_OBJS:= $(patsubst %.S, $(OBJDIR)/%.o, $(PURGATORY_S_SRCS))
PURGATORY_S_DEPS:= $(patsubst %.S, $(OBJDIR)/%.d, $(PURGATORY_S_SRCS))
PURGATORY_SRCS:= $(PURGATORY_S_SRCS) $(PURGATORY_C_SRCS)
PURGATORY_OBJS:= $(PURGATORY_S_OBJS) $(PURGATORY_C_OBJS)
PURGATORY_DEPS:= $(PURGATORY_S_DEPS) $(PURGATORY_C_DEPS)
PURGATORY:= $(OBJDIR)/purgatory/purgatory.ro
include $(PURGATORY_DEPS)
$(PURGATORY_C_DEPS): $(OBJDIR)/%.d: %.c
$(MKDIR) -p $(@D)
$(CC) $(PCFLAGS) -M $< | sed -e 's|$(patsubst %.d,%.o,$(@F))|$(patsubst %.d,%.o,$(@))|' > $@
$(PURGATORY_S_DEPS): $(OBJDIR)/%.d: %.S
$(MKDIR) -p $(@D)
$(CC) $(PCFLAGS) -M $< | sed -e 's|$(patsubst %.d,%.o,$(@F))|$(patsubst %.d,%.o,$(@))|' > $@
$(PURGATORY_C_OBJS): $(OBJDIR)/%.o: %.c $(OBJDIR)/%.d
$(MKDIR) -p $(@D)
$(CC) $(PCFLAGS) -o $@ -c $<
$(PURGATORY_S_OBJS): $(OBJDIR)/%.o: %.S $(OBJDIR)/%.d
$(MKDIR) -p $(@D)
$(CC) $(PCFLAGS) -o $@ -c $<
$(PURGATORY): $(PURGATORY_OBJS) $(UTIL_LIB)
$(MKDIR) -p $(@D)
$(LD) $(LDFLAGS) --no-undefined -e purgatory_start -r -o $@ $(PURGATORY_OBJS) $(UTIL_LIB)
echo::
@echo "PURGATORY_C_SRCS $(PURGATORY_C_SRCS)"
@echo "PURGATORY_C_DEPS $(PURGATORY_C_DEPS)"
@echo "PURGATORY_C_OBJS $(PURGATORY_C_OBJS)"
@echo "PURGATORY_S_SRCS $(PURGATORY_S_SRCS)"
@echo "PURGATORY_S_DEPS $(PURGATORY_S_DEPS)"
@echo "PURGATORY_S_OBJS $(PURGATORY_S_OBJS)"
@echo "PURGATORY_SRCS $(PURGATORY_SRCS)"
@echo "PURGATORY_DEPS $(PURGATORY_DEPS)"
@echo "PURGATORY_OBJS $(PURGATORY_OBJS)"

View File

@@ -0,0 +1,7 @@
#
# Purgatory alpha
#
PURGATORY_C_SRCS+=
PURGATORY_S_SRCS+=

View File

@@ -0,0 +1,57 @@
#ifndef LIMITS_H
#define LIMITS_H 1
/* Number of bits in a `char' */
#define CHAR_BIT 8
/* Minimum and maximum values a `signed char' can hold */
#define SCHAR_MIN (-128)
#define SCHAR_MAX 127
/* Maximum value an `unsigned char' can hold. (Minimum is 0.) */
#define UCHAR_MAX 255
/* Minimum and maximum values a `char' can hold */
#define CHAR_MIN SCHAR_MIN
#define CHAR_MAX SCHAR_MAX
/* Minimum and maximum values a `signed short int' can hold */
#define SHRT_MIN (-32768)
#define SHRT_MAX 32767
/* Maximum value an `unsigned short' can hold. (Minimum is 0.) */
#define USHRT_MAX 65535
/* Minimum and maximum values a `signed int' can hold */
#define INT_MIN (-INT_MAX - 1)
#define INT_MAX 2147483647
/* Maximum value an `unsigned int' can hold. (Minimum is 0.) */
#define UINT_MAX 4294967295U
/* Minimum and maximum values a `signed int' can hold */
#define INT_MIN (-INT_MAX - 1)
#define INT_MAX 2147483647
/* Maximum value an `unsigned int' can hold. (Minimum is 0.) */
#define UINT_MAX 4294967295U
/* Minimum and maximum values a `signed long' can hold */
#define LONG_MAX 9223372036854775807L
#define LONG_MIN (-LONG_MAX - 1L)
/* Maximum value an `unsigned long' can hold. (Minimum is 0.) */
#define ULONG_MAX 18446744073709551615UL
/* Minimum and maximum values a `signed long long' can hold */
#define LLONG_MAX 9223372036854775807LL
#define LLONG_MIN (-LONG_MAX - 1LL)
/* Maximum value an `unsigned long long' can hold. (Minimum is 0.) */
#define ULLONG_MAX 18446744073709551615ULL
#endif /* LIMITS_H */

View File

@@ -0,0 +1,16 @@
#ifndef STDINT_H
#define STDINT_H
typedef unsigned long size_t;
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
typedef unsigned long uint64_t;
typedef signed char int8_t;
typedef signed short int16_t;
typedef signed int int32_t;
typedef signed long int64_t;
#endif /* STDINT_H */

View File

@@ -0,0 +1,14 @@
#
# Purgatory i386
#
PURGATORY_S_SRCS+= purgatory/arch/i386/entry32-16.S
PURGATORY_S_SRCS+= purgatory/arch/i386/entry32-16-debug.S
PURGATORY_S_SRCS+= purgatory/arch/i386/entry32.S
PURGATORY_S_SRCS+= purgatory/arch/i386/setup-x86.S
PURGATORY_S_SRCS+= purgatory/arch/i386/stack.S
PURGATORY_S_SRCS+= purgatory/arch/i386/compat_x86_64.S
PURGATORY_C_SRCS+= purgatory/arch/i386/purgatory-x86.c
PURGATORY_C_SRCS+= purgatory/arch/i386/console-x86.c
PURGATORY_C_SRCS+= purgatory/arch/i386/vga.c
PURGATORY_C_SRCS+= purgatory/arch/i386/pic.c

View File

@@ -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

View File

@@ -0,0 +1,134 @@
#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);
}

View File

@@ -0,0 +1,160 @@
/*
* 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 */
movl $entry16, %ebx
/* Fixup my real mode segment */
movl %ebx, %eax
shrl $4, %eax
movw %ax, 2 + realptr
/* Fixup the gdt */
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
orl %ecx, 0x0c + gdt
orl %eax, 0x10 + gdt
orl %ecx, 0x14 + gdt
/* Setup the classic BIOS interrupt table at 0x0 */
lidt idtptr
/* Provide us with 16bit segments that we can use */
lgdt gdt
/* 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 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:

View File

@@ -0,0 +1,195 @@
/*
* 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 */
movl $entry16_debug, %ebx
/* Fixup my real mode segment */
movl %ebx, %eax
shrl $4, %eax
movw %ax, 2 + realptr
/* Fixup the gdt */
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
orl %ecx, 0x0c + gdt
orl %eax, 0x10 + gdt
orl %ecx, 0x14 + gdt
DEBUG('a')
/* Setup the classic BIOS interrupt table at 0x0 */
lidt idtptr
DEBUG('b')
/* Provide us with 16bit segments that we can use */
lgdt gdt
DEBUG('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('d')
/* switch to 16bit mode */
ljmp $0x08, $1f - entry16_debug
1:
.code16
DEBUG('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('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('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('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 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('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('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

View File

@@ -0,0 +1,160 @@
/*
* 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 */
movl $entry16_debug, %ebx
/* Fixup my real mode segment */
movl %ebx, %eax
shrl $4, %eax
movw %ax, 2 + realptr
/* Fixup the gdt */
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
orl %ecx, 0x0c + gdt
orl %eax, 0x10 + gdt
orl %ecx, 0x14 + gdt
/* Setup the classic BIOS interrupt table at 0x0 */
lidt idtptr
/* Provide us with 16bit segments that we can use */
lgdt gdt
/* 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 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:

View File

@@ -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

View File

@@ -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(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')

View File

@@ -0,0 +1,98 @@
#ifndef ARCH_IO_H
#define ARCH_IO_H
#include <stdint.h>
/* Helper functions for directly doing I/O */
extern inline uint8_t inb(uint16_t port)
{
uint8_t result;
__asm__ __volatile__ (
"inb %w1,%0"
:"=a" (result)
:"Nd" (port));
return result;
}
extern inline uint16_t inw(uint16_t port)
{
uint16_t result;
__asm__ __volatile__ (
"inw %w1,%0"
:"=a" (result)
:"Nd" (port));
return result;
}
extern inline uint32_t inl(uint32_t port)
{
uint32_t result;
__asm__ __volatile__ (
"inl %w1,%0"
:"=a" (result)
:"Nd" (port));
return result;
}
extern inline void outb (uint8_t value, uint16_t port)
{
__asm__ __volatile__ (
"outb %b0,%w1"
:
:"a" (value), "Nd" (port));
}
extern inline void outw (uint16_t value, uint16_t port)
{
__asm__ __volatile__ (
"outw %w0,%w1"
:
:"a" (value), "Nd" (port));
}
extern 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 unsigned char readb(const volatile void *addr)
{
return *(volatile unsigned char *) addr;
}
static inline unsigned short readw(const volatile void *addr)
{
return *(volatile unsigned short *) addr;
}
static inline unsigned int readl(const volatile void *addr)
{
return *(volatile unsigned int *) addr;
}
static inline void writeb(unsigned char b, volatile void *addr)
{
*(volatile unsigned char *) addr = b;
}
static inline void writew(unsigned short b, volatile void *addr)
{
*(volatile unsigned short *) addr = b;
}
static inline void writel(unsigned int b, volatile void *addr)
{
*(volatile unsigned int *) addr = b;
}
#endif /* ARCH_IO_H */

View File

@@ -0,0 +1,58 @@
#ifndef LIMITS_H
#define LIMITS_H 1
/* Number of bits in a `char' */
#define CHAR_BIT 8
/* Minimum and maximum values a `signed char' can hold */
#define SCHAR_MIN (-128)
#define SCHAR_MAX 127
/* Maximum value an `unsigned char' can hold. (Minimum is 0.) */
#define UCHAR_MAX 255
/* Minimum and maximum values a `char' can hold */
#define CHAR_MIN SCHAR_MIN
#define CHAR_MAX SCHAR_MAX
/* Minimum and maximum values a `signed short int' can hold */
#define SHRT_MIN (-32768)
#define SHRT_MAX 32767
/* Maximum value an `unsigned short' can hold. (Minimum is 0.) */
#define USHRT_MAX 65535
/* Minimum and maximum values a `signed int' can hold */
#define INT_MIN (-INT_MAX - 1)
#define INT_MAX 2147483647
/* Maximum value an `unsigned int' can hold. (Minimum is 0.) */
#define UINT_MAX 4294967295U
/* Minimum and maximum values a `signed int' can hold */
#define INT_MIN (-INT_MAX - 1)
#define INT_MAX 2147483647
/* Maximum value an `unsigned int' can hold. (Minimum is 0.) */
#define UINT_MAX 4294967295U
/* Minimum and maximum values a `signed long' can hold */
#define LONG_MAX 2147483647L
#define LONG_MIN (-LONG_MAX - 1L)
/* Maximum value an `unsigned long' can hold. (Minimum is 0.) */
#define ULONG_MAX 4294967295UL
/* Minimum and maximum values a `signed long long' can hold */
#define LLONG_MAX 9223372036854775807LL
#define LLONG_MIN (-LONG_MAX - 1LL)
/* Maximum value an `unsigned long long' can hold. (Minimum is 0.) */
#define ULLONG_MAX 18446744073709551615ULL
#endif /* LIMITS_H */

View File

@@ -0,0 +1,16 @@
#ifndef STDINT_H
#define STDINT_H
typedef unsigned long size_t;
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
typedef unsigned long long uint64_t;
typedef signed char int8_t;
typedef signed short int16_t;
typedef signed int int32_t;
typedef signed long long int64_t;
#endif /* STDINT_H */

View File

@@ -0,0 +1,623 @@
/*
* 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.
*/
#if 1
#define TTYS0_BASE 0x3f8
#define TTYS0_RBR (TTYS0_BASE+0x00)
#define TTYS0_TBR TTYS0_RBR
#define TTYS0_LSR (TTYS0_BASE+0x05)
/* 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 ; \
9: mov $TTYS0_LSR, %dx ; \
inb %dx, %al ; \
test $0x40, %al ; \
jz 9b
/* uses: ax, dx */
#define TTYS0_TX_CHAR(byte) \
mov byte, %al ; \
TTYS0_TX_AL
/* uses: ax, 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
#define DEBUG(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')
#else
#define DEBUG(x)
#define DEBUG_TX_HEX32(x)
#endif
#undef i386
.text
.globl entry16, entry16_regs
.arch i386
.balign 16
entry16:
.code32
DEBUG('a')
/* Setup the classic BIOS interrupt table at 0x0 */
lidt idtptr
DEBUG('b')
/* Provide us with 16bit segments that we can use */
lgdt gdt
DEBUG('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('d')
/* switch to 16bit mode */
ljmp $0x08, $1f - entry16
1:
.code16
DEBUG('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('f')
/* make intersegment jmp to flush the processor pipeline
* and reload %cs:%eip (to clear upper 16 bits of %eip).
*/
ljmp *(realptr - entry16)
3:
DEBUG('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('h')
/* 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
.size entry16_regs, . - entry16_regs
.balign 16
realptr:
.word 3b - entry16
.word 0x0000
.data
.balign 4
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 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:
/*
* 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.
*/
#if 1
#define TTYS0_BASE 0x3f8
#define TTYS0_RBR (TTYS0_BASE+0x00)
#define TTYS0_TBR TTYS0_RBR
#define TTYS0_LSR (TTYS0_BASE+0x05)
/* 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 ; \
9: mov $TTYS0_LSR, %dx ; \
inb %dx, %al ; \
test $0x40, %al ; \
jz 9b
/* uses: ax, dx */
#define TTYS0_TX_CHAR(byte) \
mov byte, %al ; \
TTYS0_TX_AL
/* uses: ax, 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
#define DEBUG(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')
#else
#define DEBUG(x)
#define DEBUG_TX_HEX32(x)
#endif
.data
.globl setup16_debug_start, setup16_debug_end, setup16_debug_size, setup16_debug_align
.globl setup16_debug_regs
.globl setup16_debug_kernel_pre_protected
.globl setup16_debug_first_code32
.globl setup16_debug_old_code32
setup16_debug_start:
_reloc = .
.balign 16
.code32
DEBUG('a')
/* Compute where I am running at */
call 1f
1: popl %ebx
subl $(1b - _reloc), %ebx
/* Remember where I am running at */
movl %ebx, location - _reloc(%ebx)
DEBUG('b')
/* Fixup my real mode segment */
movl %ebx, %eax
shrl $4, %eax
movw %ax, 2 + realptr - _reloc(%ebx)
DEBUG('c')
/* Fixup the gdt */
movl %ebx, %eax
shll $16, %eax
movl %ebx, %ecx
shrl $16, %ecx
andl $0xff, %ecx
movl %ebx, %edx
andl $0xff000000, %edx
orl %edx, %ecx
addl %ebx, gdtaddr - _reloc(%ebx)
addl %ebx, debug_gdtaddr - _reloc(%ebx)
orl %eax, 0x08 + gdt - _reloc(%ebx)
orl %ecx, 0x0c + gdt - _reloc(%ebx)
orl %eax, 0x10 + gdt - _reloc(%ebx)
orl %ecx, 0x14 + gdt - _reloc(%ebx)
DEBUG('d')
/* Setup the classic BIOS interrupt table at 0x0 */
lidt idtptr - _reloc(%ebx)
/* Provide us with 16bit segments that we can use */
lgdt gdtptr - _reloc(%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, $2f - _reloc
2:
.code16
DEBUG('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('f')
/* make intersegment jmp to flush the processor pipeline
* and reload %cs:%eip (to clear upper 16 bits of %eip).
*/
ljmp *(realptr - _reloc)
3:
DEBUG('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('h')
/* Load the registers */
movl eax - _reloc, %eax
movl ebx - _reloc, %ebx
movl ecx - _reloc, %ecx
movl edx - _reloc, %edx
movl esi - _reloc, %esi
movl edi - _reloc, %esi
movl esp - _reloc, %esp
movl ebp - _reloc, %ebp
movw es - _reloc, %es
movw ss - _reloc, %ss
movw fs - _reloc, %fs
movw gs - _reloc, %gs
movw ds - _reloc, %ds
/* Jump to the kernel entrypoint */
ljmp %cs:*(realdest - _reloc)
setup16_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
.balign 16
realptr:
.word 3b - _reloc
.word 0x0000
idtptr:
/* 256 entry idt at 0 */
.word 0x400 - 1
.word 0, 0
gdtptr:
.word gdt_end - gdt - 1
gdtaddr:
.long gdt - _reloc
gdt:
/* dummy */
.word 0, 0, 0, 0
/* 16 bit real mode code segment */
.word 0xffff, 0x0000
.byte 0x00, 0x9b, 0x00, 0x00
/* 16 bit real mode data segment */
.word 0xffff, 0x0000
.byte 0x00, 0x93, 0x00, 0x00
gdt_end:
debug_gdt:
/* 0x00 */
.word debug_gdt_end - debug_gdt - 1
debug_gdtaddr:
.long debug_gdt - _reloc
.word 0
/* 0x08 */
.word 0, 0, 0, 0 /* Nothing in the first gdt entry */
/* 0x10 4Gb - (0x100000*0x1000 = 4Gb), base address = 0, code read/exec, granularity = 4096, 386 */
.word 0xFFFF, 0x00, 0x9A00, 0x00CF
/* 0x18 4Gb - (0x100000*0x1000 = 4Gb*), base address = 0, data read/write, granularity = 4096, 386 */
.word 0xFFFF, 0x0000, 0x9200, 0x00CF
/* 0x20 4Gb - (0x100000*0x1000 = 4Gb), base address = 0, code read/exec, granularity = 4096, 386 */
.word 0xFFFF, 0x00, 0x9A00, 0x00CF
/* 0x28 4Gb - (0x100000*0x1000 = 4Gb*), base address = 0, data read/write, granularity = 4096, 386 */
.word 0xFFFF, 0x0000, 0x9200, 0x00CF
/* 0x30 4Gb - (0x100000*0x1000 = 4Gb), base address = 0, code read/exec, granularity = 4096, 386 */
.word 0xFFFF, 0x00, 0x9A00, 0x00CF
/* 0x38 4Gb - (0x100000*0x1000 = 4Gb*), base address = 0, data read/write, granularity = 4096, 386 */
.word 0xFFFF, 0x0000, 0x9200, 0x00CF
/* 0x40 4Gb - (0x100000*0x1000 = 4Gb), base address = 0, code read/exec, granularity = 4096, 386 */
.word 0xFFFF, 0x00, 0x9A00, 0x00CF
/* 0x48 4Gb - (0x100000*0x1000 = 4Gb*), base address = 0, data read/write, granularity = 4096, 386 */
.word 0xFFFF, 0x0000, 0x9200, 0x00CF
/* 0x50 4Gb - (0x100000*0x1000 = 4Gb), base address = 0, code read/exec, granularity = 4096, 386 */
.word 0xFFFF, 0x00, 0x9A00, 0x00CF
/* 0x58 4Gb - (0x100000*0x1000 = 4Gb*), base address = 0, data read/write, granularity = 4096, 386 */
.word 0xFFFF, 0x0000, 0x9200, 0x00CF
/* 0x60 4Gb - (0x100000*0x1000 = 4Gb), base address = 0, code read/exec, granularity = 4096, 386 */
.word 0xFFFF, 0x00, 0x9A00, 0x00CF
/* 0x68 4Gb - (0x100000*0x1000 = 4Gb*), base address = 0, data read/write, granularity = 4096, 386 */
.word 0xFFFF, 0x0000, 0x9200, 0x00CF
debug_gdt_end:
setup16_debug_kernel_pre_protected:
.code16
DEBUG('i')
cli # no interrupts allowed !
movb $0x80, %al # disable NMI for bootup
# sequence
outb %al, $0x70
lret
setup16_debug_first_code32:
.code32
.byte 0xbf /* movl $0x12345678, %edi */
location:
.long 0x12345678
DEBUG('j')
.byte 0xb8 /* movl $0x10000, %eax */
setup16_debug_old_code32:
.long 0x10000
jmp %eax
setup16_debug_end:
setup16_debug_size:
.long setup16_debug_end - setup16_debug_start
setup16_debug_align:
.long 16

Some files were not shown because too many files have changed in this diff Show More