mirror of
https://github.com/meizu-m86/kexec-tools-arm64
synced 2025-11-04 05:46:10 +08:00
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:
6
AUTHORS
Normal file
6
AUTHORS
Normal 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
341
COPYING
Normal 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
249
Makefile
Normal 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
52
Makefile.conf.in
Normal 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
73
News
Normal 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
11
TODO
Normal 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
1441
config/config.guess
vendored
Executable file
File diff suppressed because it is too large
Load Diff
1552
config/config.sub
vendored
Executable file
1552
config/config.sub
vendored
Executable file
File diff suppressed because it is too large
Load Diff
251
config/install-sh
Executable file
251
config/install-sh
Executable 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
|
||||
129
configure.ac
Normal file
129
configure.ac
Normal 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
438
doc/linux-i386-boot.txt
Normal 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.
|
||||
79
doc/linux-i386-zero-page.txt
Normal file
79
doc/linux-i386-zero-page.txt
Normal 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
664
doc/multiboot.html
Normal 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
660
doc/nbi-spec.txt
Normal 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
39
include/boot/beoboot.h
Normal 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
104
include/boot/elf_boot.h
Normal 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 */
|
||||
82
include/boot/linuxbios_tables.h
Normal file
82
include/boot/linuxbios_tables.h
Normal 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
2367
include/elf.h
Normal file
File diff suppressed because it is too large
Load Diff
90
include/x86/mb_header.h
Normal file
90
include/x86/mb_header.h
Normal 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
219
include/x86/mb_info.h
Normal 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
212
include/x86/x86-linux.h
Normal 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
35
kdump/Makefile
Normal 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
311
kdump/kdump.c
Normal 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, ¬e_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
40
kexec-tools.spec.in
Normal 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
63
kexec/Makefile
Normal 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)"
|
||||
|
||||
11
kexec/arch/alpha/include/arch/options.h
Normal file
11
kexec/arch/alpha/include/arch/options.h
Normal 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
11
kexec/arch/i386/Makefile
Normal 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
|
||||
131
kexec/arch/i386/compat_x86_64.S
Normal file
131
kexec/arch/i386/compat_x86_64.S
Normal 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
|
||||
22
kexec/arch/i386/include/arch/options.h
Normal file
22
kexec/arch/i386/include/arch/options.h
Normal 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 */
|
||||
|
||||
140
kexec/arch/i386/kexec-beoboot-x86.c
Normal file
140
kexec/arch/i386/kexec-beoboot-x86.c
Normal 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;
|
||||
}
|
||||
|
||||
296
kexec/arch/i386/kexec-bzImage.c
Normal file
296
kexec/arch/i386/kexec-bzImage.c
Normal 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", ®s16, sizeof(regs16));
|
||||
elf_rel_get_symbol(&info->rhdr, "entry32_regs", ®s32, 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", ®s16, sizeof(regs16));
|
||||
elf_rel_set_symbol(&info->rhdr, "entry16_debug_regs", ®s16, sizeof(regs16));
|
||||
elf_rel_set_symbol(&info->rhdr, "entry32_regs", ®s32, sizeof(regs32));
|
||||
|
||||
/* 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;
|
||||
}
|
||||
35
kexec/arch/i386/kexec-elf-rel-x86.c
Normal file
35
kexec/arch/i386/kexec-elf-rel-x86.c
Normal 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;
|
||||
}
|
||||
}
|
||||
245
kexec/arch/i386/kexec-elf-x86.c
Normal file
245
kexec/arch/i386/kexec-elf-x86.c
Normal 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", ®s, sizeof(regs));
|
||||
regs.eip = entry; /* The entry point */
|
||||
regs.esp = elf_rel_get_addr(&info->rhdr, "stack_arg32_2");
|
||||
elf_rel_set_symbol(&info->rhdr, "entry32_regs", ®s, sizeof(regs));
|
||||
|
||||
if (ramdisk) {
|
||||
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", ®s, sizeof(regs));
|
||||
regs.ebx = 0; /* Bootstrap processor */
|
||||
regs.esi = param_base; /* Pointer to the parameters */
|
||||
regs.eip = entry; /* The entry point */
|
||||
regs.esp = elf_rel_get_addr(&info->rhdr, "stack_end"); /* Stack, unused */
|
||||
elf_rel_set_symbol(&info->rhdr, "entry32_regs", ®s, sizeof(regs));
|
||||
}
|
||||
else {
|
||||
die("Unknown argument style\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
393
kexec/arch/i386/kexec-multiboot-x86.c
Normal file
393
kexec/arch/i386/kexec-multiboot-x86.c
Normal 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", ®s, sizeof(regs));
|
||||
regs.eax = 0x2BADB002;
|
||||
regs.ebx = mbi_offset;
|
||||
regs.eip = ehdr.e_entry;
|
||||
elf_rel_set_symbol(&info->rhdr, "entry32_regs", ®s, sizeof(regs));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* EOF (kexec-multiboot-x86.c)
|
||||
*/
|
||||
|
||||
249
kexec/arch/i386/kexec-nbi.c
Normal file
249
kexec/arch/i386/kexec-nbi.c
Normal 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", ®s32, sizeof(regs32));
|
||||
regs32.eip = hdr.execaddr.linear;
|
||||
elf_rel_set_symbol(&info->rhdr, "entry32_regs32", ®s32, sizeof(regs32));
|
||||
}
|
||||
else {
|
||||
struct entry32_regs regs32;
|
||||
struct entry16_regs regs16;
|
||||
|
||||
/* Initialize the 16 bit registers */
|
||||
elf_rel_get_symbol(&info->rhdr, "entry16_regs", ®s16, sizeof(regs16));
|
||||
regs16.cs = hdr.execaddr.segoff.cs;
|
||||
regs16.ip = hdr.execaddr.segoff.ip;
|
||||
elf_rel_set_symbol(&info->rhdr, "entry16_regs", ®s16, sizeof(regs16));
|
||||
|
||||
/* Initialize the 32 bit registers */
|
||||
elf_rel_get_symbol(&info->rhdr, "entry32_regs", ®s32, sizeof(regs32));
|
||||
regs32.eip = elf_rel_get_addr(&info->rhdr, "entry16");
|
||||
elf_rel_set_symbol(&info->rhdr, "entry32_regs", ®s32, sizeof(regs32));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
262
kexec/arch/i386/kexec-x86.c
Normal file
262
kexec/arch/i386/kexec-x86.c
Normal 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));
|
||||
}
|
||||
67
kexec/arch/i386/kexec-x86.h
Normal file
67
kexec/arch/i386/kexec-x86.h
Normal 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 */
|
||||
178
kexec/arch/i386/x86-linux-setup.c
Normal file
178
kexec/arch/i386/x86-linux-setup.c
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
17
kexec/arch/i386/x86-linux-setup.h
Normal file
17
kexec/arch/i386/x86-linux-setup.h
Normal 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
6
kexec/arch/ia64/Makefile
Normal 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
|
||||
11
kexec/arch/ia64/include/arch/options.h
Normal file
11
kexec/arch/ia64/include/arch/options.h
Normal 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 */
|
||||
141
kexec/arch/ia64/kexec-elf-ia64.c
Normal file
141
kexec/arch/ia64/kexec-elf-ia64.c
Normal 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;
|
||||
}
|
||||
45
kexec/arch/ia64/kexec-elf-rel-ia64.c
Normal file
45
kexec/arch/ia64/kexec-elf-rel-ia64.c
Normal 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);
|
||||
}
|
||||
156
kexec/arch/ia64/kexec-ia64.c
Normal file
156
kexec/arch/ia64/kexec-ia64.c
Normal 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)
|
||||
{
|
||||
}
|
||||
|
||||
9
kexec/arch/ia64/kexec-ia64.h
Normal file
9
kexec/arch/ia64/kexec-ia64.h
Normal 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
9
kexec/arch/ppc/Makefile
Normal 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
|
||||
11
kexec/arch/ppc/include/arch/options.h
Normal file
11
kexec/arch/ppc/include/arch/options.h
Normal 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 */
|
||||
479
kexec/arch/ppc/kexec-dol-ppc.c
Normal file
479
kexec/arch/ppc/kexec-dol-ppc.c
Normal 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;
|
||||
}
|
||||
225
kexec/arch/ppc/kexec-elf-ppc.c
Normal file
225
kexec/arch/ppc/kexec-elf-ppc.c
Normal 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;
|
||||
}
|
||||
64
kexec/arch/ppc/kexec-elf-rel-ppc.c
Normal file
64
kexec/arch/ppc/kexec-elf-rel-ppc.c
Normal 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
151
kexec/arch/ppc/kexec-ppc.c
Normal 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)
|
||||
{
|
||||
}
|
||||
|
||||
28
kexec/arch/ppc/kexec-ppc.h
Normal file
28
kexec/arch/ppc/kexec-ppc.h
Normal 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 */
|
||||
174
kexec/arch/ppc/ppc-setup-dol.S
Normal file
174
kexec/arch/ppc/ppc-setup-dol.S
Normal 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
|
||||
|
||||
39
kexec/arch/ppc/ppc-setup-simple.S
Normal file
39
kexec/arch/ppc/ppc-setup-simple.S
Normal 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
506
kexec/arch/ppc/ppc_asm.h
Normal 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
|
||||
|
||||
7
kexec/arch/ppc64/Makefile
Normal file
7
kexec/arch/ppc64/Makefile
Normal 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+=
|
||||
11
kexec/arch/ppc64/include/arch/options.h
Normal file
11
kexec/arch/ppc64/include/arch/options.h
Normal 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 */
|
||||
123
kexec/arch/ppc64/kexec-elf-ppc64.c
Normal file
123
kexec/arch/ppc64/kexec-elf-ppc64.c
Normal 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");
|
||||
}
|
||||
95
kexec/arch/ppc64/kexec-elf-rel-ppc64.c
Normal file
95
kexec/arch/ppc64/kexec-elf-rel-ppc64.c
Normal 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;
|
||||
}
|
||||
9
kexec/arch/ppc64/kexec-ppc64.h
Normal file
9
kexec/arch/ppc64/kexec-ppc64.h
Normal 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 */
|
||||
173
kexec/arch/ppc64/kexec-zImage-ppc64.c
Normal file
173
kexec/arch/ppc64/kexec-zImage-ppc64.c
Normal 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");
|
||||
}
|
||||
12
kexec/arch/x86_64/Makefile
Normal file
12
kexec/arch/x86_64/Makefile
Normal 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
|
||||
22
kexec/arch/x86_64/include/arch/options.h
Normal file
22
kexec/arch/x86_64/include/arch/options.h
Normal 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 */
|
||||
|
||||
93
kexec/arch/x86_64/kexec-elf-rel-x86_64.c
Normal file
93
kexec/arch/x86_64/kexec-elf-rel-x86_64.c
Normal 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);
|
||||
}
|
||||
239
kexec/arch/x86_64/kexec-elf-x86_64.c
Normal file
239
kexec/arch/x86_64/kexec-elf-x86_64.c
Normal 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", ®s, sizeof(regs));
|
||||
regs.rdi = note_base; /* The notes (arg1) */
|
||||
regs.rip = entry; /* The entry point */
|
||||
regs.rsp = elf_rel_get_addr(&info->rhdr, "stack_end"); /* Stack, unused */
|
||||
elf_rel_set_symbol(&info->rhdr, "entry64_regs", ®s, sizeof(regs));
|
||||
|
||||
if (ramdisk) {
|
||||
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", ®s, sizeof(regs));
|
||||
regs.rbx = 0; /* Bootstrap processor */
|
||||
regs.rsi = param_base; /* Pointer to the parameters */
|
||||
regs.rip = entry; /* the entry point */
|
||||
regs.rsp = elf_rel_get_addr(&info->rhdr, "stack_end"); /* Stack, unused */
|
||||
elf_rel_set_symbol(&info->rhdr, "entry64_regs", ®s, sizeof(regs));
|
||||
}
|
||||
else {
|
||||
die("Unknown argument style\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
247
kexec/arch/x86_64/kexec-x86_64.c
Normal file
247
kexec/arch/x86_64/kexec-x86_64.c
Normal 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));
|
||||
}
|
||||
31
kexec/arch/x86_64/kexec-x86_64.h
Normal file
31
kexec/arch/x86_64/kexec-x86_64.h
Normal 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
74
kexec/ifdown.c
Normal 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
95
kexec/kexec-elf-boot.c
Normal 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
8
kexec/kexec-elf-boot.h
Normal 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
149
kexec/kexec-elf-exec.c
Normal 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
531
kexec/kexec-elf-rel.c
Normal 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
760
kexec/kexec-elf.c
Normal 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
125
kexec/kexec-elf.h
Normal 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
11
kexec/kexec-sha256.h
Normal 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
73
kexec/kexec-syscall.h
Normal 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
45
kexec/kexec.8
Normal 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
810
kexec/kexec.c
Normal 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", ®ion, sizeof(region));
|
||||
elf_rel_set_symbol(&info->rhdr, "sha256_digest", &digest, sizeof(digest));
|
||||
}
|
||||
|
||||
/*
|
||||
* Load the new kernel
|
||||
*/
|
||||
static int my_load(const char *type, int fileind, int argc, char **argv,
|
||||
unsigned long kexec_flags)
|
||||
{
|
||||
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
194
kexec/kexec.h
Normal 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
30
kexec_test/Makefile
Normal 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
475
kexec_test/kexec_test.S
Normal 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
1004
kexec_test/kexec_test16.S
Normal file
File diff suppressed because it is too large
Load Diff
53
kexec_test/x86-setup-legacy-pic.S
Normal file
53
kexec_test/x86-setup-legacy-pic.S
Normal 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
68
purgatory/Makefile
Normal 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)"
|
||||
7
purgatory/arch/alpha/Makefile
Normal file
7
purgatory/arch/alpha/Makefile
Normal file
@@ -0,0 +1,7 @@
|
||||
#
|
||||
# Purgatory alpha
|
||||
#
|
||||
|
||||
PURGATORY_C_SRCS+=
|
||||
PURGATORY_S_SRCS+=
|
||||
|
||||
57
purgatory/arch/alpha/include/limits.h
Normal file
57
purgatory/arch/alpha/include/limits.h
Normal 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 */
|
||||
16
purgatory/arch/alpha/include/stdint.h
Normal file
16
purgatory/arch/alpha/include/stdint.h
Normal 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 */
|
||||
14
purgatory/arch/i386/Makefile
Normal file
14
purgatory/arch/i386/Makefile
Normal 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
|
||||
100
purgatory/arch/i386/compat_x86_64.S
Normal file
100
purgatory/arch/i386/compat_x86_64.S
Normal 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
|
||||
134
purgatory/arch/i386/console-x86.c
Normal file
134
purgatory/arch/i386/console-x86.c
Normal 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);
|
||||
}
|
||||
|
||||
|
||||
160
purgatory/arch/i386/entry16.S
Normal file
160
purgatory/arch/i386/entry16.S
Normal 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:
|
||||
195
purgatory/arch/i386/entry32-16-debug.S
Normal file
195
purgatory/arch/i386/entry32-16-debug.S
Normal 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
|
||||
160
purgatory/arch/i386/entry32-16.S
Normal file
160
purgatory/arch/i386/entry32-16.S
Normal 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:
|
||||
110
purgatory/arch/i386/entry32.S
Normal file
110
purgatory/arch/i386/entry32.S
Normal 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
|
||||
|
||||
316
purgatory/arch/i386/include/arch/debug.h
Normal file
316
purgatory/arch/i386/include/arch/debug.h
Normal 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')
|
||||
98
purgatory/arch/i386/include/arch/io.h
Normal file
98
purgatory/arch/i386/include/arch/io.h
Normal 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 */
|
||||
58
purgatory/arch/i386/include/limits.h
Normal file
58
purgatory/arch/i386/include/limits.h
Normal 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 */
|
||||
16
purgatory/arch/i386/include/stdint.h
Normal file
16
purgatory/arch/i386/include/stdint.h
Normal 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 */
|
||||
623
purgatory/arch/i386/linux-entry16.S
Normal file
623
purgatory/arch/i386/linux-entry16.S
Normal 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
Reference in New Issue
Block a user