First cut at 1.4.0.
authorKen Yap <ken_yap@users.sourceforge.net>
Thu, 2 Jan 2003 12:44:36 +0000 (12:44 +0000)
committerKen Yap <ken_yap@users.sourceforge.net>
Thu, 2 Jan 2003 12:44:36 +0000 (12:44 +0000)
52 files changed:
COPYING [new file with mode: 0644]
Elf.pm [new file with mode: 0644]
LOG [new file with mode: 0644]
Makefile [new file with mode: 0644]
Nbi.pm [new file with mode: 0644]
README [new file with mode: 0644]
TruncFD.pm [new file with mode: 0755]
altboot.S [new file with mode: 0644]
ansiesc.c [new file with mode: 0644]
ansiesc.h [new file with mode: 0644]
boot.inc [new file with mode: 0644]
bootmenu.c [new file with mode: 0644]
bootmenu.h [new file with mode: 0644]
disdosbb.pl [new file with mode: 0755]
dismbr.pl [new file with mode: 0755]
disnbi.pl [new file with mode: 0755]
elf_boot.h [new file with mode: 0644]
etherboot.h [new file with mode: 0644]
first-dos.S [new file with mode: 0644]
first-dos.h [new file with mode: 0644]
first32.c [new file with mode: 0644]
linux-asm-io.h [new file with mode: 0644]
linux-asm-string.h [new file with mode: 0644]
md5.c [new file with mode: 0644]
md5.h [new file with mode: 0644]
memsizes.c [new file with mode: 0644]
menu-nfl.eg [new file with mode: 0644]
menu-simple.c [new file with mode: 0644]
menu-simple.eg [new file with mode: 0644]
menu-simple.h [new file with mode: 0644]
menu.c [new file with mode: 0644]
menuc.pl [new file with mode: 0755]
misc.c [new file with mode: 0644]
misc.h [new file with mode: 0644]
mklnim [new file with mode: 0755]
mklnim.1 [new file with mode: 0644]
mknbi.pl [new file with mode: 0644]
nfl.c [new file with mode: 0644]
nfl.h [new file with mode: 0644]
printf.c [new file with mode: 0644]
printf.h [new file with mode: 0644]
rmrd.S [new file with mode: 0644]
serial.c [new file with mode: 0644]
setjmp.h [new file with mode: 0644]
spec.txt [new file with mode: 0644]
start32.S [new file with mode: 0644]
startmenu.S [new file with mode: 0644]
stddef.h [new file with mode: 0644]
string.c [new file with mode: 0644]
string.h [new file with mode: 0644]
version-dos.h [new file with mode: 0644]
version-linux.h [new file with mode: 0644]

diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..a43ea21
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,339 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                          675 Mass Ave, Cambridge, MA 02139, 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.
+\f
+                   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.)
+\f
+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.
+\f
+  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.
+\f
+  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
+\f
+       Appendix: 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., 675 Mass Ave, Cambridge, MA 02139, 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.
diff --git a/Elf.pm b/Elf.pm
new file mode 100644 (file)
index 0000000..6d45896
--- /dev/null
+++ b/Elf.pm
@@ -0,0 +1,348 @@
+# Class to handle Elf images
+# Placed under GNU Public License by Ken Yap, December 2000
+
+package Elf;
+
+use strict;
+use IO::Seekable;
+
+use constant;
+use constant TFTPBLOCKSIZE => 512;
+# ELF magic header in first 4 bytes
+use constant MAGIC => "\x7FELF";
+# This is defined by the bootrom layout
+use constant HEADERSIZE => 512;
+# Size of ELF header
+use constant ELF_HDR_LEN => 52;
+# Type code
+use constant ELFCLASS32 => 1;
+# Byte order
+use constant ELFDATA2LSB => 1;
+# ELF version
+use constant EV_CURRENT => 1;
+# File type
+use constant ET_EXEC => 2;
+# Machine type
+use constant EM_386 => 3;
+# Size of each program header
+use constant PROG_HDR_LEN => 32;
+# Type of header
+use constant PT_LOAD => 1;
+use constant PT_NOTE => 4;
+# Size of each section header (there is just one)
+use constant SECT_HDR_LEN => 40;
+# Note types
+use constant EIN_PROGRAM_NAME     => 0x00000001;
+use constant EIN_PROGRAM_VERSION  => 0x00000002;
+use constant EIN_PROGRAM_CHECKSUM => 0x00000003;
+
+sub new {
+       my $class = shift;
+       my $self = { };
+       $self->{libdir} = shift;
+       $self->{segdescs} = [];
+       $self->{offset} = 0;    # cumulative offset from beginning of file
+       $self->{checksum} = 0;  # cumulative checksum of the file
+       $self->{summed} = 0;    # number of bytes checksummed
+       $self->{data} = "";     # string buffer containing the output file
+       bless $self, $class;
+#      $self->_initialize();
+       return $self;
+}
+
+sub add_pm_header ($$$$$)
+{
+       my ($self, $vendorinfo, $headerseg, $bootaddr, $progreturns) = @_;
+
+       push(@{$self->{segdescs}}, pack('A4C4@16v2V5v6',
+               MAGIC, ELFCLASS32, ELFDATA2LSB, EV_CURRENT,
+               255,            # embedded ABI
+               ET_EXEC,        # e_type
+               EM_386,         # e_machine
+               EV_CURRENT,     # e_version
+               $bootaddr,      # e_entry
+               ELF_HDR_LEN,    # e_phoff
+               0,              # e_shoff (come back and patch this)
+               ($progreturns ? 0x8000000 : 0),         # e_flags
+               ELF_HDR_LEN,    # e_ehsize
+               PROG_HDR_LEN,   # e_phentsize
+               0,              # e_phnum (come back and patch this)
+               SECT_HDR_LEN,   # e_shentsize
+               1,              # e_shnum, one mandatory entry 0
+               0));            # e_shstrndx
+       $self->{offset} = HEADERSIZE;
+}
+
+sub compute_ip_checksum
+{
+       my ($str) = @_;
+       my ($checksum, $i, $size, $shorts);
+       $checksum = 0;
+       $size = length($$str);
+       $shorts = $size >> 1;
+       # Perl has a fairly large loop overhead so a straight forward
+       # implementation of the ip checksum is intolerably slow.
+       # Instead we use the unpack checksum computation function,
+       # and sum 16bit little endian words into a 32bit number, on at
+       # most 64K of data at a time.  This ensures we do not overflow
+       # the 32bit sum allowing carry wrap around to be implemented by
+       # hand.
+       for($i = 0; $i < $shorts; $i += 32768) {
+               $checksum += unpack("%32v32768", substr($$str, $i <<1, 65536));
+               while($checksum > 0xffff) {
+                       $checksum = ($checksum & 0xffff) + ($checksum >> 16);
+               }
+       }
+       if ($size & 1) {
+               $checksum += unpack('C', substr($$str, -1, 1));
+               while($checksum > 0xffff) {
+                       $checksum = ($checksum & 0xffff) + ($checksum >> 16);
+               }
+       }
+       $checksum = (~$checksum) & 0xFFFF;
+       return $checksum;
+}
+
+sub add_summed_data
+{
+       my ($self, $str) = @_;
+       my $new_sum = compute_ip_checksum($str);
+       my $new = $new_sum;
+       my $sum = $self->{checksum};
+       my $checksum;
+       $sum = ~$sum & 0xFFFF;
+       $new = ~$new & 0xFFFF;
+       if ($self->{summed} & 1) {
+               $new = (($new >> 8) & 0xff) | (($new << 8) & 0xff00);
+       }
+       $checksum = $sum + $new;
+       if ($checksum > 0xFFFF) {
+               $checksum -= 0xFFFF;
+       }
+       $self->{checksum} = (~$checksum) & 0xFFFF;
+       $self->{summed} += length($$str);
+       print "$$str";
+#      $self->{data} .= $$str;
+#      print STDERR sprintf("sum: %02x %02x sz: %08x summed: %08x\n",
+#              $new_sum, $self->{checksum}, length($$str), $self->{summed});
+}
+
+
+# This should not get called as we don't cater for real mode calls but
+# is here just in case
+sub add_header ($$$$$)
+{
+       my ($self, $vendorinfo, $headerseg, $bootseg, $bootoff) = @_;
+
+       $self->add_pm_header($vendorinfo, $headerseg, ($bootseg << 4) + $bootoff, 0);
+}
+
+sub roundup ($$)
+{
+# Round up to next multiple of $blocksize, assumes that it's a power of 2
+       my ($size, $blocksize) = @_;
+
+       # Default to TFTPBLOCKSIZE if not specified
+       $blocksize = TFTPBLOCKSIZE if (!defined($blocksize));
+       return ($size + $blocksize - 1) & ~($blocksize - 1);
+}
+
+# Grab N bytes from a file
+sub peek_file ($$$$)
+{
+       my ($self, $descriptor, $dataptr, $datalen) = @_;
+       my ($file, $fromoff, $status);
+
+       $file = $$descriptor{'file'} if exists $$descriptor{'file'};
+       $fromoff = $$descriptor{'fromoff'} if exists $$descriptor{'fromoff'};
+       return 0 if !defined($file) or !open(R, "$file");
+       binmode(R);
+       if (defined($fromoff)) {
+               return 0 if !seek(R, $fromoff, SEEK_SET);
+       }
+       # Read up to $datalen bytes
+       $status = read(R, $$dataptr, $datalen);
+       close(R);
+       return ($status);
+}
+
+# Add a segment descriptor from a file or a string
+sub add_segment ($$$)
+{
+       my ($self, $descriptor, $vendorinfo) = @_;
+       my ($file, $string, $segment, $len, $maxlen, $fromoff, $align,
+               $id, $end, $vilen);
+
+       $end = 0;
+       $file = $$descriptor{'file'} if exists $$descriptor{'file'};
+       $string = $$descriptor{'string'} if exists $$descriptor{'string'};
+       $segment = $$descriptor{'segment'} if exists $$descriptor{'segment'};
+       $len = $$descriptor{'len'} if exists $$descriptor{'len'};
+       $maxlen = $$descriptor{'maxlen'} if exists $$descriptor{'maxlen'};
+       $fromoff = $$descriptor{'fromoff'} if exists $$descriptor{'fromoff'};
+       $align = $$descriptor{'align'} if exists $$descriptor{'align'};
+       $id = $$descriptor{'id'} if exists $$descriptor{'id'};
+       $end = $$descriptor{'end'} if exists $$descriptor{'end'};
+       if (!defined($len)) {
+               if (defined($string)) {
+                       $len = length($string);
+               } else {
+                       if (defined($fromoff)) {
+                               $len = (-s $file) - $fromoff;
+                       } else {
+                               $len = -s $file;
+                       }
+                       return 0 if !defined($len);             # no such file
+               }
+       }
+       if (defined($align)) {
+               $len = &roundup($len, $align);
+       } else {
+               $len = &roundup($len);
+       }
+       $maxlen = $len if (!defined($maxlen));
+       push(@{$self->{segdescs}}, pack('V8',
+               PT_LOAD,
+               $self->{offset},        # p_offset
+               $segment << 4,          # p_vaddr
+               $segment << 4,          # p_paddr
+               $len,                   # p_filesz
+               $len,                   # p_memsz == p_filesz
+               7,                      # p_flags == rwx
+               TFTPBLOCKSIZE));        # p_align
+       $self->{offset} += $len;
+       return ($len);                  # assumes always > 0
+}
+
+sub pad_with_nulls ($$$)
+{
+       my ($self, $i, $blocksize) = @_;
+
+       $blocksize = TFTPBLOCKSIZE if (!defined($blocksize));
+       # Pad with nulls to next block boundary
+       $i %= $blocksize;
+       if ($i != 0) {
+               # Nulls do not change the checksum
+               print "\0" x ($blocksize - $i);
+#              $self->{data} .= "\0" x ($blocksize - $i);
+               $self->{summed} += ($blocksize - $i);
+       }
+}
+
+# Copy data from file to stdout
+sub copy_file ($$)
+{
+       my ($self, $descriptor) = @_;
+       my ($i, $file, $fromoff, $align, $len, $seglen, $nread, $data, $status);
+
+       $file = $$descriptor{'file'} if exists $$descriptor{'file'};
+       $fromoff = $$descriptor{'fromoff'} if exists $$descriptor{'fromoff'};
+       $align = $$descriptor{'align'} if exists $$descriptor{'align'};
+       $len = $$descriptor{'len'} if exists $$descriptor{'len'};
+       return 0 if !open(R, "$file");
+       if (defined($fromoff)) {
+               return 0 if !seek(R, $fromoff, SEEK_SET);
+               $len = (-s $file) - $fromoff if !defined($len);
+       } else {
+               $len = -s $file if !defined($len);
+       }
+       binmode(R);
+       # Copy file in TFTPBLOCKSIZE chunks
+       $nread = 0;
+       while ($nread != $len) {
+               $status = read(R, $data, TFTPBLOCKSIZE);
+               last if (!defined($status) or $status == 0);
+               $self->add_summed_data(\$data);
+               $nread += $status;
+       }
+       close(R);
+       if (defined($align)) {
+               $self->pad_with_nulls($nread, $align);
+       } else {
+               $self->pad_with_nulls($nread);
+       }
+       return ($nread);
+}
+
+# Copy data from string to stdout
+sub copy_string ($$)
+{
+       my ($self, $descriptor) = @_;
+       my ($i, $string, $len, $align, $data);
+
+       $string = $$descriptor{'string'} if exists $$descriptor{'string'};
+       $len = $$descriptor{'len'} if exists $$descriptor{'len'};
+       $align = $$descriptor{'align'} if exists $$descriptor{'align'};
+       return 0 if !defined($string);
+       $len = length($string) if !defined($len);
+       $data = substr($string, 0, $len);
+       $self->add_summed_data(\$data);
+       defined($align) ? $self->pad_with_nulls($len, $align) : $self->pad_with_nulls($len);
+       return ($len);
+}
+
+sub dump_segments
+{
+       my ($self) = @_;
+       my ($s, $nsegs, @segdescs);
+
+       # generate the note header
+       my $notes = pack('V3Z8S2',
+               8,                      # n_namesz
+               2,                      # n_descsz
+               EIN_PROGRAM_CHECKSUM,   # n_type
+               "ELFBoot",              # n_name
+               0,                      # n_desc (Initial checksum value)
+               0);                     # padding to a 4byte boundary
+       my $note_len = length($notes);
+
+       # Add the note header
+       push(@{$self->{segdescs}}, pack('V8',
+               PT_NOTE,                # p_type
+               HEADERSIZE - $note_len, # p_offset
+               0,                      # p_vaddr
+               0,                      # p_paddr
+               $note_len,              # p_filesz
+               0,                      # p_memsz == p_filesz
+               0,                      # p_flags
+               0));                    # p_align
+                                       
+       @segdescs = @{$self->{segdescs}};
+       $nsegs = $#segdescs;    # number of program header entries
+       # fill in e_phnum
+       substr($segdescs[0], 44, 2) = pack('v', $nsegs);
+       # fill in e_shoff to point to a record after program headers
+       substr($segdescs[0], 32, 4) = pack('V',
+               ELF_HDR_LEN + PROG_HDR_LEN * $nsegs);
+       $self->{checksum} = 0;
+       $self->{summed} = 0;
+       while ($s = shift(@segdescs)) {
+               $self->add_summed_data(\$s);
+       }
+       # insert section header 0
+       # we just need to account for the length, the null fill
+       # will create the record we want
+       # warn if we have overflowed allocated header area
+       print STDERR "Warning, too many segments in file\n"
+               if ($self->{summed} > HEADERSIZE - SECT_HDR_LEN - $note_len);
+       print "\0" x (HEADERSIZE - $self->{summed});
+#      $self->{data} .= "\0" x (HEADERSIZE - $self->{summed});
+
+       # Write the note header;
+       seek(STDOUT, HEADERSIZE - $note_len, SEEK_SET) or die "Cannot seek to note header\n";
+       print "$notes";
+#      substr($self->{data}, HEADERSIZE - $note_len, $note_len) = $notes
+}
+
+sub finalise_image 
+{
+       my ($self) = @_;
+       # Fill in the checksum
+       seek(STDOUT, HEADERSIZE - 4, SEEK_SET) or die "Cannot seek to checksum\n";
+       print pack('S', $self->{checksum});
+#      substr($self->{data}, (HEADERSIZE - 4), 2) = pack('S', $self->{checksum});
+#      print $self->{data};
+}
+
+
+1;
diff --git a/LOG b/LOG
new file mode 100644 (file)
index 0000000..b154b1f
--- /dev/null
+++ b/LOG
@@ -0,0 +1,170 @@
+Split off from Etherboot as mknbi-1.1-0 on 2001-02-18
+
++ Change Z5 unpack formats to a5 because Z introduced on in Perl 5.005.
+
++ Set dl to drvid just before calling boot block, in case some boot
+blocks expect this.
+
++ Updated comments in first32.c to reflect Etherboot-5.0 memory map.
+
+Released as mknbi-1.1-1 (development)
+
+Released as mknbi-1.2-0 (production)
+
++ A debugging statement left behind in disnbi.pl, tsk.
+
++ Support setup.S header version 0x0202 where parameter string pointer
+is 32-bit pointer.
+
++ Put mknbi version into vendor string so that it will be in image.
+
++ Derive version number of mknbi.pl and first32.c from VERSION in Makefile.
+
++ Replace 0x9000 by $relocseg, and add support for placement of Linux
+segments at 0x90000 and 0x80000 upwards.
+
++ Makefile now makes 2 more versions of first32{,pm,elf}.linux for
+placement at 0x92200 and 0x82200.
+
+Released as mknbi-1.2-1 (production)
+
++ Vendor magic string changed due to addition of version number, this
+caused the self consistency check to fail in first-*dos.
+
++ Use --oformat binary to cater for newest ld's.
+
++ Moved setup segment of mknbi-fdos to 0x93000. 0x97000 is used by
+Etherboot now.
+
+Released as mknbi-1.2-2 (production)
+
++ Added the --rdbase option to mknbi-linux, for specifying the memory
+location of the ramdisk.
+
++ Added some tips by Phil Davey on creating bootable MSDOS images.
+
++ Henk van de Kamer pointed out that 2.88 MB floppies actually have 80
+tracks and 36 sectors instead of 160/18. It doesn't matter for network
+booting, but I changed it to reduce confusion.
+
++ Merged in changes by Peter Lister to support --rootmode=rw|ro.
+
++ Round down top of memory as returned by memory sizing routine to next
+lower 4kB boundary to make sure that ramdisk will be aligned properly.
+
++ Implement missing functionality per spec: appending additional kernel
+options from the vendor selection. RFC1533_VENDOR_SEL option holds value
+of tag that holds the options string. If the 7th argument exists, append
+it to kernel options after the global options, but before keyword=value
+substitutions.
+
+Released as mknbi-1.2-3 (production)
+
++ Hyun-Joon Cha discovered a trivial bug in 1.2-3.  The kernel
+parameters are inserted twice, which may lead to overflow of the
+parameter space.
+
++ A new (s)printf which does not limit the length of the output is now
+used.
+
+Released as mknbi-1.2-4 (production)
+
++ Renamed do_printf to vsprintf because that's the standard function it
+has the same signature as.
+
++ Moved include of linux-asm-string.h into string.h in anticipation of
+using string.h for other standalone binaries. Added signatures for more
+string functions. Introduced stddef.h which contains minimal definitions
+to port hosted programs. Current target: lua.
+
++ Daniel Wagner sent in patches to the Makefile so that distinct
+intermediate files are used for the various first* images, otherwise
+there will be problems with parallel makes (-j).
+
++ Implemented --noharddisk option to disable hard disk access. No, make
+that --disableharddisk. In Perl Getopt --noharddisk is automatically the
+inverse of --harddisk.
+
++ first-linux.S had the right parenthesis in the wrong place for the
+initialisation of vmagic.
+
++ Print warning if zImage kernels are large enough to extend into
+Etherboot area.
+
+Released as mknbi-1.2-5 (production)
+
++ Implement --rdbase for mknbi-fdos and mknbi-dos to specify starting
+address of ramdisk (floppy image).
+
++ Make first.*dos depend on Makefile so that the version signature gets
+updated when the version is updated.
+
++ Print some friendly advice if --ip is not specified and there is no
+ramdisk argument (i.e. probably needs NFSroot).
+
++ Shuffle segments to make more space for parameters and first32. This
+version and future versions may not work with Etherboot images that have
+been compiled with BOOTP_DATA_AT_0x93Cxx. (There is some reprieve due to
+first32.c not using all 6kB of the allocated room yet, but that space
+may be used some day.)
+
++ Implement interception and decoding of the mem= kernel parameter to
+use as top of memory for the purpose of relocating the ramdisk.
+
+Released as mknbi-1.2-6 (production)
+
++ Add E820 BIOS memory sizing routines from Etherboot written by Eric
+Biederman. May help people with recent BIOSes.
+
++ Limit the top of ramdisk to 896MB for setup version 0x202 and earlier,
+or the greater of the setup variable ramdisk_max or 896MB for setup
+version 0x203 and later. If the user overrides it with mem=, then it's
+their responsibility.
+
+Released as mknbi-1.2-7 (production)
+
++ Add an EXTRAVERSION to Makefile, to be printed out by first32.c.
+
++ Modified call to int15h/e801 in start32.S to check for return values
+in CX, DX in case BIOS doesn't return them in AX, BX.
+
++ Ported internal menu program from Etherboot to be external menu
+program for mknbi-menu.
+
++ Updated URL for FreeDOS kernel sources.
+
+Released as mknbi-1.2-8 (production)
+
++ Robb Main contributed an enhanced menu implementation, mknbi-nfl.
+
+Released as mknbi-1.2-9 (production)
+
++ Add finalise_image hook in Elf.pm and Nbi.pm for later use.
+
++ Fixed start32.S to not rely on where it's called from, essential for
+booting from Etherboot 5.1+. For this we have to switch GDT like
+startmenu.S does.
+
+Released as mknbi-1.2-10 (production)
+
++ Patch from Doug Ambrisko to handle BSD.
+
++ Put in serial port console.
+
++ Forgot to include binary menu in RPM version, spotted by Masaru
+Kawashima.  Also make symlinks to man page while at it.
+
+Released as mknbi-1.2-11 (production)
+
++ Changes by Eric Biederman for checksumming. Propagate
+parse_elf_boot_notes to other menu programs.
+
++ printf ignores width specification now.
+
++ mknbi-rom loads ROM image at 0x60000 now.
+
++ Make printf return value of sprintf, as per C99 spec.
+
+Released as mknbi-1.2-12 (production)
+
+Released as mknbi-1.4.0 (production)
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..462db56
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,235 @@
+VERSION=1.4.0
+EXTRAVERSION=
+TARVERSION=1.4.0
+# spec file can override
+RPMVERSION=$(VERSION)
+
+# If you don't have either nasm or the ELKS as86, then just do make
+# and make install because the assembler sources are supplied preassembled
+# If you do make realclean you will have to unpack the mknbi archive again
+
+# Use nasm or as86
+ASM=nasm
+# This must be the as86 from the ELKS project, the default Linux one won't do
+#ASM=as86
+
+# for first32*.linux
+# This one makes multiple versions
+FIRSTRELOCS=   0x92800 0x82800
+OLDGAS:=       $(shell $(AS) --version | grep -q '2\.9\.1' && echo -DGAS291)
+CFLAGS=                -Os -ffreestanding -fstrength-reduce -fomit-frame-pointer \
+               -mcpu=i386 \
+               -Wall -W -Wno-format -Wno-unused -DVERSION=\"$(VERSION)$(EXTRAVERSION)\"
+LDBINARYFLAG=  --oformat binary
+FIRST32SIZE=   6144
+
+# for FreeDOS
+FDKSEG=        0x60
+
+# for menu
+MENURELOC=     0x60000
+# Options for auxiliary menu program
+# Available: -DANSIESC -DMOTD -DUSRPARMS -DPASSWD
+# -DCONSOLE_CRT/CONSOLE_SERIAL -DSHOW_NUMERIC -DPOWERSAVE
+# Might as well take the lot since code size is not a concern
+BOOTMENU_FLAGS=        -DCONSOLE_DUAL -DANSIESC -DMOTD -DUSRPARMS -DPASSWD -DPOWERSAVE -DCOMCONSOLE=0x3f8 -DCOMPRESERVE
+
+PREFIX=                /usr/local
+INSTPREFIX=    $(BUILD_ROOT)$(PREFIX)
+LIBDIR=                $(PREFIX)/lib/mknbi
+INSTLIBDIR=    $(INSTPREFIX)/lib/mknbi
+BINDIR=                $(INSTPREFIX)/bin
+MANDIR=                $(INSTPREFIX)/share/man/man1
+DOCDIR=                $(INSTPREFIX)/share/doc/mknbi-$(RPMVERSION)
+
+INSTALL=       install
+
+PROG=  mknbi disnbi dismbr disdosbb
+MODULES=Nbi.pm Elf.pm TruncFD.pm
+FIRSTS=        $(foreach i,$(FIRSTRELOCS),first32@$(i).linux first32elf@$(i).linux) \
+       first.dos first.fdos menu nfl
+ALTBOOT=altboot.bin
+RMRD=  rmrd.com
+MANS=  mknbi.1 disnbi.1 menuc.1
+HTMLS= mknbi.html disnbi.html menuc.html
+DOCS=  $(MANS) $(HTMLS)
+
+
+all:   $(PROG) $(FIRSTS) $(RMRD) $(DOCS)
+
+# Tagged image builder
+mknbi: mknbi.pl Nbi.pm TruncFD.pm
+       sed -e 's:@@VERSION@@:$(VERSION):' -e 's:@@LIBDIR@@:$(LIBDIR):' -e 's/@@FDKSEG@@/$(FDKSEG)/' mknbi.pl > $@
+       chmod 755 $@
+
+# Tagged image display and disassemble
+disnbi:        disnbi.pl
+       cp -p disnbi.pl disnbi
+       chmod 755 disnbi
+
+# Master boot record display
+dismbr:        dismbr.pl
+       cp -p dismbr.pl dismbr
+       chmod 755 dismbr
+
+# DOS boot block display
+disdosbb:      disdosbb.pl
+       cp -p disdosbb.pl disdosbb
+       chmod 755 disdosbb
+
+# 32-bit first stage protected mode call setup program
+first32@%.linux:       start32@%.o first32.o memsizes.o printf.o
+       $(LD) -N -Ttext $* -e _start $(LDBINARYFLAG) -o $@ start32@$*.o first32.o memsizes.o printf.o
+       @if [ `wc -c < $@` -gt $(FIRST32SIZE) ]; then echo Binary too large; fi
+
+# 32-bit first stage ELF setup program
+first32elf@%.linux:    start32@%.o first32elf.o memsizes.o printf.o
+       $(LD) -N -Ttext $* -e _start $(LDBINARYFLAG) -o $@ start32@$*.o first32elf.o memsizes.o printf.o
+       @if [ `wc -c < $@` -gt $(FIRST32SIZE) ]; then echo Binary too large; fi
+
+start32@%.o:   start32.S
+       gcc -E -DRELOC=$* $(OLDGAS) start32.S | $(AS) -o start32@$*.o
+
+first32.o:     first32.c etherboot.h
+       gcc $(CFLAGS) -o first32.o -c first32.c
+
+first32elf.o:  first32.c etherboot.h
+       gcc $(CFLAGS) -DFIRST32ELF -o first32elf.o -c first32.c
+
+memsizes.o:            memsizes.c
+       gcc $(CFLAGS) -c $*.c
+
+printf.o:              printf.c
+       gcc $(CFLAGS) -c $*.c
+
+# DOS first stage setup program, depend on Makefile for version number
+first.dos:     first-dos.S first-dos.h version-dos.h Makefile
+ifeq ($(ASM),as86)
+       gcc $(ASMCFLAGS) -DUSE_AS86 -DVENDOR_MAGIC=\"mknbi-dos-$(VERSION)\" -E -traditional -o first-dos.s first-dos.S
+       as86 -0 -b first.dos first-dos.s
+else
+       gcc $(ASMCFLAGS) -DUSE_NASM -DVENDOR_MAGIC=\"mknbi-dos-$(VERSION)\" -E -traditional -o first-dos.s first-dos.S
+       nasm -f bin first-dos.s -o first.dos
+endif
+
+# FreeDOS first stage setup program, depend on Makefile for version number
+first.fdos:    first-dos.S first-dos.h version-dos.h Makefile
+ifeq ($(ASM),as86)
+       gcc $(ASMCFLAGS) -DFREEDOS -DFDKSEG=$(FDKSEG) -DUSE_AS86 -DVENDOR_MAGIC=\"mknbi-fdos-$(VERSION)\" -E -traditional -o first-fdos.s first-dos.S
+       as86 -0 -b first.fdos first-fdos.s
+else
+       gcc $(ASMCFLAGS) -DFREEDOS -DFDKSEG=$(FDKSEG) -DUSE_NASM -DVENDOR_MAGIC=\"mknbi-fdos-$(VERSION)\" -E -traditional -o first-fdos.s first-dos.S
+       nasm -f bin first-fdos.s -o first.fdos
+endif
+
+# Menu first stage program
+menu:  startmenu.o menu.o bootmenu.o string.o printf.o ansiesc.o md5.o misc.o serial.o
+       $(LD) -N -Ttext $(MENURELOC) -e _start $(LDBINARYFLAG) -o $@ startmenu.o menu.o bootmenu.o string.o printf.o ansiesc.o md5.o misc.o serial.o
+
+# Another menu program, this one simpler
+menu-simple:   startmenu.o menu-simple.o string.o printf.o ansiesc.o
+       $(LD) -N -Ttext $(MENURELOC) -e _start $(LDBINARYFLAG) -o $@ startmenu.o menu-simple.o string.o printf.o ansiesc.o
+
+# Network FreeLoader (light-bar menu program)
+nfl:   startmenu.o nfl.o string.o printf.o ansiesc.o
+       $(LD) -N -Ttext $(MENURELOC) -e _start $(LDBINARYFLAG) -o $@ startmenu.o nfl.o string.o printf.o ansiesc.o
+
+# LUA interpreter
+lua/bin/lua:
+       cd lua; make
+
+startmenu.o:   startmenu.S
+       gcc -E -Ui386 -DRELOC=$(MENURELOC) $(OLDGAS) startmenu.S | $(AS) -o startmenu.o
+
+menu.o:                menu.c stddef.h string.h printf.h ansiesc.h \
+               misc.h linux-asm-io.h etherboot.h
+       gcc $(CFLAGS) $(BOOTMENU_FLAGS) -c $*.c
+
+bootmenu.o:    bootmenu.h bootmenu.c stddef.h string.h md5.h misc.h etherboot.h
+       gcc $(CFLAGS) $(BOOTMENU_FLAGS) -c $*.c
+
+string.o:      string.h string.c
+       gcc $(CFLAGS) -c $*.c
+
+ansiesc.o:     ansiesc.h ansiesc.c stddef.h string.h etherboot.h
+       gcc $(CFLAGS) -DGFX -c $*.c
+
+md5.o: md5.h md5.c etherboot.h
+       gcc $(CFLAGS) $(BOOTMENU_FLAGS) -c $*.c
+
+misc.o:        misc.h misc.c ansiesc.h etherboot.h
+       gcc $(CFLAGS) $(BOOTMENU_FLAGS) -c $*.c
+
+serial.o:      serial.c
+       gcc $(CFLAGS) $(BOOTMENU_FLAGS) -c $*.c
+
+menu-simple.o: menu-simple.h menu-simple.c string.h etherboot.h
+       gcc $(CFLAGS) -c $*.c
+
+# Remove ramdisk utility under DOS
+rmrd.com:      rmrd.S
+ifeq ($(ASM),as86)
+       gcc $(ASMCFLAGS) -DUSE_AS86 -E -traditional -o rmrd.s rmrd.S
+       # -s appears to be necessary to start binary at 0x100
+       as86 -0 -b rmrd.com -s rmrd.map rmrd.s
+else
+       gcc $(ASMCFLAGS) -DUSE_NASM -E -traditional -o rmrd.s rmrd.S
+       nasm -f bin rmrd.s -o rmrd.com
+endif
+
+# Alternate boot block, taken from netboot mknbi-dos
+# Not made by default because not everybody has as86 installed
+$(ALTBOOT):    altboot.S
+       gcc -E altboot.S > altboot.s
+       as86 -s /dev/null -b $(ALTBOOT) altboot.s
+       $(RM) altboot.s
+
+$(MANS):       mknbi.pl disnbi.pl Makefile
+       pod2man --date=`date +%Y-%m-%d` --release="Mknbi $(VERSION)$(EXTRAVERSION)" --center="Etherboot tools" mknbi.pl > mknbi.1
+       pod2man --date=`date +%Y-%m-%d` --release="Mknbi $(VERSION)$(EXTRAVERSION)" --center="Etherboot tools" disnbi.pl > disnbi.1
+       pod2man --date=`date +%Y-%m-%d` --release="Mknbi $(VERSION)$(EXTRAVERSION)" --center="Etherboot tools" menuc.pl > menuc.1
+
+$(HTMLS):      mknbi.pl disnbi.pl
+       pod2html mknbi.pl > mknbi.html
+       pod2html disnbi.pl > disnbi.html
+       pod2html menuc.pl > menuc.html
+
+install:       all $(ALTBOOT)
+       -mkdir -p $(INSTLIBDIR)
+       $(INSTALL) $(PROG) $(INSTLIBDIR)/
+       $(INSTALL) -m 644 $(MODULES) $(FIRSTS) $(ALTBOOT) $(RMRD) $(INSTLIBDIR)/
+       if [ -e lua/bin/lua ]; then $(INSTALL) -m 644 lua/bin/lua $(INSTLIBDIR)/; fi
+       -mkdir -p $(BINDIR)
+       cd $(BINDIR); \
+       for i in linux rom fdos dos menu nfl; \
+       do \
+               ln -sf ../lib/mknbi/mknbi mknbi-$$i; \
+       done; \
+       for i in linux menu nfl lua; \
+       do \
+               ln -sf ../lib/mknbi/mknbi mkelf-$$i; \
+       done
+       (cd $(BINDIR); ln -sf ../lib/mknbi/disnbi disnbi)
+       (cd $(BINDIR); ln -sf ../lib/mknbi/dismbr dismbr)
+       (cd $(BINDIR); ln -sf ../lib/mknbi/disdosbb disdosbb)
+       -mkdir -p $(MANDIR)
+       $(INSTALL) -m 644 mknbi.1 disnbi.1 $(MANDIR)/
+       cd $(MANDIR); \
+       for i in mknbi-linux mkelf-linux mknbi-rom mknbi-menu mkelf-menu mknbi-nfl mkelf-nfl mknbi-dos mknbi-fdos; \
+       do \
+               ln -sf mknbi.1 $$i.1; \
+       done
+       -mkdir -p $(DOCDIR)
+       $(INSTALL) -m 644 README LOG spec.txt $(DOCDIR)/
+
+tarball:
+       (cd ..; tar zcf /tmp/mknbi-$(TARVERSION).tar.gz --exclude CVS --exclude lua mknbi-$(TARVERSION))
+
+clean:
+       $(RM) -f $(PROG) start32*.o first32*.o first32elf*.o \
+       printf.o menu.o menu-simple.o nfl.o startmenu.o \
+       bootmenu.o md5.o misc.o serial.o ansiesc.o string.o memsizes.o \
+       first-dos.s first-fdos.s rmrd.s rmrd.map *.l pod2htm*
+
+realclean:     clean
+       $(RM) -f $(FIRSTS) $(RMRD) $(DOCS)
diff --git a/Nbi.pm b/Nbi.pm
new file mode 100644 (file)
index 0000000..808c6d2
--- /dev/null
+++ b/Nbi.pm
@@ -0,0 +1,226 @@
+# Class to handle tagged images
+# Placed under GNU Public License by Ken Yap, April 2000
+
+package Nbi;
+
+use strict;
+use IO::Seekable;
+
+use constant;
+use constant TFTPBLOCKSIZE => 512;
+# This is correct for the current version of the netboot specs
+# Note: reverse of the way it is in the specs because of Intel byte order
+use constant MAGIC => "\x36\x13\x03\x1B";
+# This is needed at the end of the boot block, again byte reversed
+use constant MAGIC2 => "\x55\xAA";
+# This is defined by the bootrom layout
+use constant HEADERSIZE => 512;
+
+use vars qw($libdir $bootseg $bootoff @segdescs);
+
+sub new {
+       my $class = shift;
+       $libdir = shift;
+       my $self = {};
+       bless $self, $class;
+#      $self->_initialize();
+       return $self;
+}
+
+sub add_header ($$$$$)
+{
+       my ($class, $vendorinfo, $headerseg, $bootseg, $bootoff) = @_;
+       my ($vilen);
+
+       $vilen = length($vendorinfo);
+       $vilen += 4;            # three plus one for null byte
+       $vilen &= ~0x3; # round to multiple of 4
+       push(@segdescs, pack("A4V3a$vilen",
+               MAGIC,
+               ($vilen << 2) + 4,
+               $headerseg << 16,
+               ($bootseg << 16) + $bootoff,
+               $vendorinfo));
+}
+
+sub add_pm_header ($$$$$)
+{
+       my ($class, $vendorinfo, $headerseg, $bootaddr, $progreturns) = @_;
+       my ($vilen);
+
+       $vilen = length($vendorinfo);
+       $vilen += 4;            # three plus one for null byte
+       $vilen &= ~0x3; # round to multiple of 4
+       push(@segdescs, pack("A4V3a$vilen",
+               MAGIC,
+               (($vilen << 2) + 4) | (1 << 31) | ($progreturns << 8),
+               $headerseg << 16,
+               $bootaddr,
+               $vendorinfo));
+}
+
+sub roundup ($$)
+{
+# Round up to next multiple of $blocksize, assumes that it's a power of 2
+       my ($size, $blocksize) = @_;
+
+       # Default to TFTPBLOCKSIZE if not specified
+       $blocksize = TFTPBLOCKSIZE if (!defined($blocksize));
+       return ($size + $blocksize - 1) & ~($blocksize - 1);
+}
+
+# Grab N bytes from a file
+sub peek_file ($$$$)
+{
+       my ($class, $descriptor, $dataptr, $datalen) = @_;
+       my ($file, $fromoff, $status);
+
+       $file = $$descriptor{'file'} if exists $$descriptor{'file'};
+       $fromoff = $$descriptor{'fromoff'} if exists $$descriptor{'fromoff'};
+       return 0 if !defined($file) or !open(R, "$file");
+       binmode(R);
+       if (defined($fromoff)) {
+               return 0 if !seek(R, $fromoff, SEEK_SET);
+       }
+       # Read up to $datalen bytes
+       $status = read(R, $$dataptr, $datalen);
+       close(R);
+       return ($status);
+}
+
+# Add a segment descriptor from a file or a string
+sub add_segment ($$$)
+{
+       my ($class, $descriptor, $vendorinfo) = @_;
+       my ($file, $string, $segment, $len, $maxlen, $fromoff, $align,
+               $id, $end, $vilen);
+
+       $end = 0;
+       $file = $$descriptor{'file'} if exists $$descriptor{'file'};
+       $string = $$descriptor{'string'} if exists $$descriptor{'string'};
+       $segment = $$descriptor{'segment'} if exists $$descriptor{'segment'};
+       $len = $$descriptor{'len'} if exists $$descriptor{'len'};
+       $maxlen = $$descriptor{'maxlen'} if exists $$descriptor{'maxlen'};
+       $fromoff = $$descriptor{'fromoff'} if exists $$descriptor{'fromoff'};
+       $align = $$descriptor{'align'} if exists $$descriptor{'align'};
+       $id = $$descriptor{'id'} if exists $$descriptor{'id'};
+       $end = $$descriptor{'end'} if exists $$descriptor{'end'};
+       if (!defined($len)) {
+               if (defined($string)) {
+                       $len = length($string);
+               } else {
+                       if (defined($fromoff)) {
+                               $len = (-s $file) - $fromoff;
+                       } else {
+                               $len = -s $file;
+                       }
+                       return 0 if !defined($len);             # no such file
+               }
+       }
+       if (defined($align)) {
+               $len = &roundup($len, $align);
+       } else {
+               $len = &roundup($len);
+       }
+       $maxlen = $len if (!defined($maxlen));
+       if (!defined($vendorinfo)) {
+               push(@segdescs, pack('V4',
+                       4 + ($id << 8) + ($end << 26),
+                       $segment << 4,
+                       $len,
+                       $maxlen));
+       } else {
+               $vilen = length($vendorinfo);
+               $vilen += 3;           # three plus one for null byte
+               $vilen &= ~0x3;        # round to multiple of 4
+               push(@segdescs, pack("V4a$vilen",
+                       ($vilen << 2) + 4 + ($id << 8) + ($end << 26),
+                       $segment << 4,
+                       $len,
+                       $maxlen,
+                       $vendorinfo));
+       }
+       return ($len);                  # assumes always > 0
+}
+
+sub pad_with_nulls ($$)
+{
+       my ($i, $blocksize) = @_;
+
+       $blocksize = TFTPBLOCKSIZE if (!defined($blocksize));
+       # Pad with nulls to next block boundary
+       $i %= $blocksize;
+       print "\0" x ($blocksize - $i) if ($i != 0);
+}
+
+# Copy data from file to stdout
+sub copy_file ($$)
+{
+       my ($class, $descriptor) = @_;
+       my ($i, $file, $fromoff, $align, $len, $seglen, $nread, $data, $status);
+
+       $file = $$descriptor{'file'} if exists $$descriptor{'file'};
+       $fromoff = $$descriptor{'fromoff'} if exists $$descriptor{'fromoff'};
+       $align = $$descriptor{'align'} if exists $$descriptor{'align'};
+       $len = $$descriptor{'len'} if exists $$descriptor{'len'};
+       return 0 if !open(R, "$file");
+       if (defined($fromoff)) {
+               return 0 if !seek(R, $fromoff, SEEK_SET);
+               $len = (-s $file) - $fromoff if !defined($len);
+       } else {
+               $len = -s $file if !defined($len);
+       }
+       binmode(R);
+       # Copy file in TFTPBLOCKSIZE chunks
+       $nread = 0;
+       while ($nread != $len) {
+               $status = read(R, $data, TFTPBLOCKSIZE);
+               last if (!defined($status) or $status == 0);
+               print $data;
+               $nread += $status;
+       }
+       close(R);
+       if (defined($align)) {
+               &pad_with_nulls($nread, $align);
+       } else {
+               &pad_with_nulls($nread);
+       }
+       return ($nread);
+}
+
+# Copy data from string to stdout
+sub copy_string ($$)
+{
+       my ($class, $descriptor) = @_;
+       my ($i, $string, $len, $align);
+
+       $string = $$descriptor{'string'} if exists $$descriptor{'string'};
+       $len = $$descriptor{'len'} if exists $$descriptor{'len'};
+       $align = $$descriptor{'align'} if exists $$descriptor{'align'};
+       return 0 if !defined($string);
+       $len = length($string) if !defined($len);
+       print substr($string, 0, $len);
+       defined($align) ? &pad_with_nulls($len, $align) : &pad_with_nulls($len);
+       return ($len);
+}
+
+sub dump_segments {
+       my ($s, $len);
+
+       $len = 0;
+       while ($s = shift(@segdescs)) {
+               $len += length($s);
+               print $s;
+       }
+       print "\0" x (HEADERSIZE - 2 - $len), MAGIC2;
+}
+
+# This empty for now, but is available as a hook to do any actions
+# before closing the image file
+
+sub finalise_image {
+}
+
+@segdescs = ();
+
+1;
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..5114379
--- /dev/null
+++ b/README
@@ -0,0 +1,26 @@
+mknbi.pl 1.2
+
+mknbi is a utility for building a tagged image for a kernel for various
+operating systems. It can also build a tagged images for an Etherboot ROM
+image and an external menu program, which aren't really operating systems.
+
+mknbi has several advantages over the mknbi tools in netboot-0.8.1
+and contrib:
+
+It is more portable, as it is written in Perl. It should be possible to
+execute the Perl script unchanged on various platforms to which Perl5
+has been ported. January 2001: I have since received word of one person
+uring mknbi on an WinNT system with only a couple of changes.
+
+It will be able to cope with any additional load formats that Etherboot
+might support in future, because the image format specific handling has
+been abstracted into a class, Nbi.pm. As of January 2001, there is a new
+class for handling ELF format, Elf.pm.
+
+It is more readable. While this is a matter of opinion, my personal
+perspective is that Perl is more concise than C, and this aids
+comprehension because one can keep more context with the limited number
+of lines on the screen. Perl's powerful builtin functions and useful
+data types handle situations such as structure extraction and packing,
+and byte ordering simply. It is relatively easy to add support for new
+features. The drawback is that the programmer must be fluent in Perl.
diff --git a/TruncFD.pm b/TruncFD.pm
new file mode 100755 (executable)
index 0000000..eacdfea
--- /dev/null
@@ -0,0 +1,88 @@
+# Perl subroutine to shorten a floppy image by omitting trailing sectors
+# that are not in use
+#
+# AFAIK works on FAT12 and FAT16. Won't work on FAT32
+#
+# Placed under GNU Public License by Ken Yap, April 2000
+
+package TruncFD;
+
+use strict;
+use IO::Seekable;
+use POSIX qw(ceil);
+
+use constant;
+use constant DEBUG => 0;
+
+sub truncfd ($)
+{
+       my ($file) = @_;
+       my ($nread, $buffer, $cylinders, $clusters, $fatsectors,
+               $rootdirsectors, @fatents, $i, $lastclus, $clusstart, $lastsector);
+       
+       return -1 if (!defined($file) or !open(F, "$file"));
+       binmode(F);
+       $nread = read(F, $buffer, 512);
+       return -1 if (!defined($nread) or $nread < 512);
+
+       my ($dummy1, $bytepersect, $sectperclus, $resvsectors, $fats, $rootdirents,
+               $sectors, $dummy2, $sectperfat, $sectpertrk, $heads, $hidsectors,
+               $dummy3, $fstype) =
+               unpack('a11vCvCvvavvvva24a5', $buffer);
+       $cylinders = $sectors / ($sectpertrk * $heads);
+       $clusters = $sectors / $sectperclus;
+       $fatsectors = $fats * $sectperfat;
+       $rootdirsectors = POSIX::ceil(($rootdirents * 32) / $bytepersect);
+       if (DEBUG) {
+               print STDERR <<EOF;
+$fstype filesystem:
+
+Bytes/sector:          $bytepersect
+Heads:                 $heads
+Cylinders:             $cylinders
+Sectors/cluster:       $sectperclus
+Clusters:              $clusters
+Reserved sectors:      $resvsectors
+Hidden sectors:                $hidsectors
+Total sectors:         $sectors
+Sectors/track:         $sectpertrk
+FATs:                  $fats
+Sectors/FAT:           $sectperfat
+FAT sectors:           $fatsectors
+Root dir entries:      $rootdirents
+Root dir sectors:      $rootdirsectors
+
+EOF
+       }
+       return -1 if (!seek(F, $resvsectors * 512, SEEK_SET));
+       $nread = read(F, $buffer, $sectperfat * 512);
+       close(F);
+       return -1 if (!defined($nread));
+       # Default assumption is FAT16
+       # For FAT12
+       if ($fstype eq 'FAT12') {
+               $buffer =~ s/(...)/$1\x00/sg;
+       }
+       @fatents = unpack("V$clusters", $buffer);
+       # For FAT12 shift bits 23..12 up 4 bits to make it just like FAT16
+       if ($fstype eq 'FAT12') {
+               foreach $i (0..$#fatents) {
+                       $fatents[$i] = ($fatents[$i] & 0xFFF) | ($fatents[$i] >> 12) << 16;
+               }
+       }
+       # Make a string of it
+       $buffer = pack("L*", @fatents);
+       # Then extract in little endian format
+       @fatents = unpack("v*", $buffer);
+       $#fatents = $clusters if ($clusters < $#fatents);
+       foreach $i (reverse(0..$#fatents)) {
+               $lastclus = $i, last if ($fatents[$i] != 0);
+       }
+       print STDERR "Last used cluster is $lastclus\n" if (DEBUG);
+       $clusstart = $resvsectors + $fatsectors + $rootdirsectors;
+       $lastsector = $clusstart + ($lastclus - 1) * $sectperclus;
+       print STDERR "Last used sector is $lastsector\n" if (DEBUG);
+       return ($lastsector * 512);
+}
+
+1;
diff --git a/altboot.S b/altboot.S
new file mode 100644 (file)
index 0000000..f0f71e8
--- /dev/null
+++ b/altboot.S
@@ -0,0 +1,170 @@
+! bootsect.S  -  boot sector for DOS ramdisk
+!
+! Copyright (C) 1996-1998 Gero Kuhlmann   <gero@gkminix.han.de>
+!
+!  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
+!  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.
+
+
+#ifndef ASM_DEBUG
+#undef ASM_DEBUG
+#endif
+#include "boot.inc"
+
+
+       .text
+
+       .org    BOOT_OFFSET
+
+
+_main: jmp     start
+       nop
+
+! Define boot record (defaults are for 1.44MB floppy disk)
+
+       .ascii  "MSDOS5.0"              ! OEM name
+bpsect:        .word   SECT_SIZE               ! bytes per sector
+spc:   .byte   BOOT_SPC                ! number of sectors per cluster
+resvd: .word   1                       ! number of reserved sectors
+fatnum:        .byte   2                       ! number of FATs
+dirnum:        .word   BOOT_DIRNUM             ! number of entries in root directory
+secnum:        .word   BOOT_SECNUM             ! total number of sectors on disk
+fmtid: .byte   BOOT_FORMAT             ! media ID
+spfat: .word   BOOT_SPFAT              ! sectors per fat
+sptrk: .word   BOOT_SPTRK              ! sectors per track
+       .word   2                       ! number of heads
+hidden:        .long   0                       ! number of hidden sectors
+       .long   0                       ! total number of sectors on disk
+bootid:        .byte   BOOT_ID                 ! BIOS number of boot device
+       .byte   0                       ! head number of boot sector
+       .byte   0x29                    ! disk attributes
+       .long   0                       ! volume number
+       .ascii  "NO NAME    "           ! volume name
+       .ascii  "FAT12   "              ! filesystem name
+iosys: .word   3                       ! no of sectors in IO.SYS
+
+
+! Start of main routine
+
+start: cli
+       xor     ax,ax
+       mov     ss,ax
+       mov     sp,#_main               ! set stack to beginning of code
+       mov     ds,ax                   ! set segment registers
+       mov     es,ax
+       sti
+       xor     dx,dx
+       int     0x13                    ! initialize disk system
+
+! Compute the logical number of the first sector of the data area. We
+! assume that IO.SYS is always located at the beginning. This might
+! not be true for a real DOS disk, but is always correct for a disk
+! image produced by mknbi.
+
+       mov     al,fatnum
+       mul     word ptr (spfat)        ! compute size of FAT
+       add     ax,word ptr (hidden+0)
+       adc     dx,word ptr (hidden+2)  ! add number of hidden sectors
+       add     ax,resvd
+       adc     dx,#0                   ! add number of reserved sectors
+       mov     bx,#DIR_SEG * 16
+       call    rdsect                  ! read first directory sector
+       jc      doerr1
+
+       push    dx
+       mov     cx,ax
+       mov     ax,#32
+       mul     word ptr (dirnum)       ! compute size of root directory
+       mov     bx,bpsect               ! area
+       add     ax,bx                   ! round up to sector size
+       dec     ax
+       div     bx
+       add     ax,cx                   ! add number of sectors used by
+       pop     dx                      ! root directory
+       adc     dx,#0
+
+! Now load the first 3 sectors of IO.SYS at 0x0070.
+
+       push    dx
+       push    ax
+       mov     bx,#IOSYS_SEG * 16      ! get offset to IO.SYS segment from
+       mov     cx,iosys                ! start of current data segment (0)
+start1:        call    rdsect
+       jc      doerr
+       add     ax,#1
+       adc     dx,#0                   ! continue with next sector
+       add     bx,bpsect
+       loop    start1
+       pop     bx
+       pop     ax
+
+       mov     si,#endata
+       mov     ch,fmtid                ! prepare registers for IO.SYS
+       mov     dl,bootid
+       jmpi    0,IOSYS_SEG             ! call IO.SYS
+
+! Print error message and terminate
+
+doerr: pop     ax
+       pop     ax
+doerr1:        mov     si,#errmsg
+doerr2:        lodsb
+       or      al,al
+       jz      doerr3
+       mov     ah,#0x0e
+       mov     bx,#7                   ! print using BIOS
+       int     0x10
+       jmp     doerr2
+doerr3:        xor     ax,ax
+       int     0x16                    ! wait for key press
+       int     0x19                    ! reboot the system
+
+
+! Read one logical sector into ES:BX
+
+rdsect:        push    ax
+       push    dx
+       push    cx
+       div     word ptr (sptrk)
+       inc     dl
+       mov     cl,dl                   ! convert logical sector number into
+       mov     dh,al                   ! cylinder/head/sector format
+       and     dh,#0x01
+       shr     al,#1
+       mov     ch,al
+       mov     ax,#0x0201
+       mov     dl,bootid               ! read one sector from ramdisk
+       int     0x13
+       pop     cx
+       pop     dx
+       pop     ax
+       ret
+
+
+! Data area
+
+errmsg:        .byte   0x0d, 0x0a
+       .ascii  "Ramdisk error - press any key to continue"
+       .byte   0x0d, 0x0a
+       .byte   0
+
+endata:
+
+
+! The end of the boot sectors has to contain the boot signature.
+
+       .org    BOOT_SIG_OFS
+       .word   BOOT_SIG                ! boot sector signature
+
diff --git a/ansiesc.c b/ansiesc.c
new file mode 100644 (file)
index 0000000..900e212
--- /dev/null
+++ b/ansiesc.c
@@ -0,0 +1,468 @@
+#include "stddef.h"
+#include "string.h"
+#include "etherboot.h"
+#include "ansiesc.h"
+
+/*
+ * 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, or (at
+ * your option) any later version.
+ */
+
+/*
+ *     Display attributes
+ *
+ *     Code          Effect
+ *     <esc>[0m      normal text
+ *     <esc>[1m      high-intensity on
+ *     <esc>[21m     high-intensity off
+ *     <esc>[5m      blinking on
+ *     <esc>[25m     blinking off
+ *     <esc>[7m      reverse video on
+ *     <esc>[27m     reverse video off
+ *     <esc>[3xm     set foreground color:
+ *     <esc>[4xm     set background color. x can be:
+ *                        0 - black                   4 - blue
+ *                        1 - red                     5 - magenta
+ *                        2 - green                   6 - cyan
+ *                        3 - yellow                  7 - white
+ *     <esc>[=xh     set video mode
+ *                        0 - 40x25 mono     (text)  13 - 40x25 16colors (gfx)
+ *                        1 - 40x25 16colors (text)  14 - 80x25 16colors (gfx)
+ *                        2 - 80x25 mono     (text)  15 - 80x25 mono     (gfx)
+ *                        3 - 80x25 16colors (text)  16 - 80x25 16colors (gfx)
+ *                        4 - 40x25 4colors  (gfx)   17 - 80x30 mono     (gfx)
+ *                        5 - 40x25 mono     (gfx)   18 - 80x30 16colors (gfx)
+ *                        6 - 80x25 mono     (gfx)   19 - 40x25 256colors(gfx)
+ *
+ *
+ *     Cursor control
+ *
+ *     Code          Effect
+ *     <esc>[r;cH    move cursor to row r col c (r and c are both numbers)
+ *     <esc>[r;cf    move cursor to row r col c (r and c are both numbers)
+ *     <esc>[rA      move cursor up r rows
+ *     <esc>[rB      move cursor down r rows
+ *     <esc>[cC      move cursor right c columns
+ *     <esc>[cD      move cursor left c columns
+ *     <esc>[?7l     turn off line wrap
+ *     <esc>[?7h     turn on line wrap
+ *     <esc>[J       clear screen and home cursor
+ *     <esc>[K       clear to end of line
+ *     <esc>[s       save the cursor position
+ *     <esc>[u       return cursor to saved position
+ *
+ *     Extended features
+ *
+ *     Code          Effect
+ *      <esc>[a;b;c;d+<data>
+ *                    draw pixel data; use one byte per pixel. Parameters
+ *                    differ depending on the number of parameters passed:
+ *                    cnt
+ *                      "cnt" bytes follow; they will be drawn to the
+ *                      right of the last graphics position
+ *                    rle;col
+ *                      the next "rle" pixels have the value "col"; they
+ *                      will be drawn to the right of the last graphics
+ *                      position
+ *                    x;y;cnt
+ *                      "cnt" bytes follow; they will be drawn relative to
+ *                      the text cursor position with an offset of (x/y)
+ *                    x;y;rle;col
+ *                      the next "rle" pixels have the value "col"; the
+ *                      will be drawn relative to the text cursor position
+ *                      with an offset of (x/y)
+ *      <esc>[a;b;c;d-<data>
+ *                      same as above, but pack pixels into three bits.
+ *
+ */
+
+#define MAXARGS                8
+#define MAXSTRARGS     1
+#define MAXSTRARGLEN   40
+
+extern union {
+  struct {
+    unsigned char al,ah,bl,bh,cl,ch,dl,dh;
+  } __attribute__ ((packed)) lh;
+  struct {
+    unsigned short ax,bx,cx,dx;
+  } __attribute__ ((packed)) x;
+  unsigned long axbx;
+} __attribute__ ((packed)) int10ret;
+
+union axbxstruct {
+  struct {
+    unsigned char al,ah,bl,bh;
+  } __attribute__ ((packed)) lh;
+  struct {
+    unsigned short ax,bx;
+  } __attribute__ ((packed)) x;
+  unsigned long axbx;
+} __attribute__ ((packed));
+
+#define int10(a,b,c,d) _int10((unsigned long)(a)+((unsigned long)(b)<<16), \
+                             (unsigned long)(c)+((unsigned long)(d)<<16))
+extern unsigned long _int10(unsigned long axbx,unsigned long cxdx);
+
+static enum { esc_init, esc_std, esc_esc, esc_bracket, esc_digit,
+             esc_semicolon, esc_str, esc_quote
+} esc_state = esc_init;
+
+/* Don't declare these static, so we can access them from code in other files */
+unsigned short rows,columns,attr = 7;
+
+static unsigned short scr,cx = 0,cy = 0, scx = 0,scy = 0;
+static int fg = 7, bg = 0, blink = 0,reverse = 0, bright = 0;
+static int wraparound = 1;
+static int argn = 0;
+static int argi[MAXARGS];
+static char args[MAXSTRARGS][MAXSTRARGLEN];
+static const unsigned char coltable[9] = "\000\004\002\006\001\005\003\007";
+#ifdef GFX
+static unsigned short gfx_rows,gfx_columns,char_width,char_height,gfx_x,gfx_y;
+static int in_gfx = 0, gfx_packed,gfx_data,gfx_nbits;
+static int defaultmode = -1, curmode = -1, curmodetype = 0;
+#endif
+
+void ansi_reset(void)
+{
+#ifdef GFX
+  /* this will not execute, when the serial console is used, because
+     both curmode and defaultmode will be -1 */
+  if (curmode != defaultmode) {
+    in_gfx = 0;
+    curmodetype = 0;
+    int10(curmode = defaultmode,0,0,0);
+    esc_state = esc_init;
+    ansi_putc('\010'); /* force reinitialization */ }
+  return;
+#endif
+}
+
+void enable_cursor(int enable)
+{
+#ifdef GFX
+  /* this will not execute, when the serial console is used, because
+     curmodetype will be 0 */
+  if (curmodetype == 'G' || curmodetype == 'H') {
+    if (enable)
+      int10(0x09DB,7,1,0);
+    else
+      int10(0x0920,(attr/16)&7,1,0); }
+  return;
+#endif
+}
+
+static void docommand(unsigned char ch)
+{
+#ifdef GFX
+  if (ch == '+' || ch == '-') { /* gfx bitmap */
+    int i;
+    gfx_packed = ((ch == '-') ? 3 : 8);
+    gfx_nbits  = 0;
+    if (argn > 2) {
+      gfx_x    = argi[0];
+      gfx_y    = argi[1];
+      if (curmodetype == 'T') {
+       gfx_x += cx;
+       gfx_y += cy; }
+      else {
+       gfx_x += cx*char_width;
+       gfx_y += cy*char_height; }
+      i        = 2; }
+    else
+      i        = 0;
+    in_gfx   = argi[i+0];
+    if (argn == i+2)
+      while (in_gfx)
+       ansi_putc(argi[i+1]); }
+  else
+#endif
+  if (ch == 'm') { /* set attribute */
+    int i,j;
+    for (j = 0; (i = argi[j]), (j++ < argn); )
+      if (i == 0 ||       /* reset attributes */
+         i == 20) {
+       blink = reverse = bright = bg = 0; fg = 7;
+       goto doattr; }
+      else if (i == 1) {  /* high intensity on */
+       bright = 1;
+       goto doattr; }
+      else if (i == 21) { /* high intensity off */
+       bright = 0;
+       goto doattr; }
+      else if (i == 5) {  /* blinking on */
+       blink = 1;
+       goto doattr; }
+      else if (i == 25) { /* blinking off */
+       blink = 0;
+       goto doattr; }
+      else if (i == 7) {  /* reverse video on */
+       reverse = 1;
+      isreverse:
+       attr =
+#ifdef GFX
+         curmodetype != 'T' ? (fg?128:0)+16*fg+(fg^(8*bright+bg)) :
+#endif
+         128*blink+16*fg+8*bright+bg; }
+      else if (i == 27) { /* reverse video off */
+       reverse = 0;
+      isnormal:
+       attr =
+#ifdef GFX
+         curmodetype != 'T' ? (bg?128:0)+16*bg+(bg^(8*bright+fg)) :
+#endif
+         128*blink+16*bg+8*bright+fg; }
+      else if (i >= 30 && i <= 37) {
+       fg = coltable[i-30];
+      doattr:
+       if (reverse) goto isreverse; else goto isnormal; }
+      else if (i >= 40 && i <= 47) {
+       bg = coltable[i-40];
+       goto doattr; } }
+  else if (ch == 'H' || /* set cursor position */
+          ch == 'f') { /* set cursor position */
+    cy = argi[0];
+    cx = argi[1];
+    updatecursor:
+    int10(0x0200,256*scr,0,256*(cy-1)+(cx-1)); }
+  else if (ch == 'A') { /* move cursor up */
+    if (cy < argi[0]) cy = 0;
+    else              cy -= argi[0];
+    goto updatecursor; }
+  else if (ch == 'B') { /* move cursor down */
+    if ((cy += argi[0]) >= rows) cy = rows-1;
+    goto updatecursor; }
+  else if (ch == 'C') { /* move cursor right */
+    if ((cx += argi[0]) >= columns) cx = columns-1;
+    goto updatecursor; }
+  else if (ch == 'D') { /* move cursor left */
+    if (cx < argi[0]) cx = 0;
+    else              cx -= argi[0];
+    goto updatecursor; }
+  else if (ch == 'l') { /* turn off line wrapping or set text mode */
+    if (argi[0] == 7)
+      wraparound = 0;
+#ifdef GFX
+    else {  /* text mode */
+      curmodetype = 0;
+      int10(curmode = defaultmode,0,0,0);
+      esc_state = esc_init; }
+#endif
+  }
+  else if (ch == 'h') { /* turn on line wrapping or set graphics mode */
+    if (argi[0] == 7)
+      wraparound = 1;
+#ifdef GFX
+    else {  /* graphics mode */
+      curmodetype = 0;
+      int10(curmode = argi[0],0,0,0);
+      esc_state = esc_init; }
+#endif
+  }
+  else if (ch == 'J') { /* clear screen and home cursor */
+#ifdef GFX
+    int10(0x0600,256*(curmodetype != 'T' ? reverse ? fg : bg : attr),0,
+         256*(rows-1)+columns-1);
+#else
+    int10(0x0600,256*attr,0,256*(rows-1)+columns-1);
+#endif
+    cx = cy = 0;
+    goto updatecursor; }
+  else if (ch == 'K')   /* clear to end of line */
+#ifdef GFX
+    int10(curmodetype == 'T' ? 0x0920 : 0x09DB,
+         curmodetype == 'T' ? 256*scr+attr : reverse ? fg : bg,
+         columns-cx,0);
+#else
+    int10(0x0920,256*scr+attr,columns-cx,0);
+#endif
+  else if (ch == 's') { /* save cursor position */
+    scx = cx; scy = cy; }
+  else if (ch == 'u') { /* restore cursor position */
+    cx = scx; cy = scy;
+    goto updatecursor; }
+  return;
+}
+
+void ansi_putc(unsigned int ch)
+{
+  union axbxstruct u;
+
+#ifdef GFX
+  if (in_gfx) {
+    gfx_data   = (gfx_data << 8) | ch;
+    gfx_nbits += 8;
+    while (gfx_nbits >= gfx_packed && in_gfx) {
+      in_gfx--;
+      ch = (gfx_data >> (gfx_nbits -= gfx_packed)) & ((1 << gfx_packed) - 1);
+      if (curmodetype == 'T') {
+       if (gfx_x < columns && gfx_y < rows*2) {
+         int pix[2];
+         int10(0x0200,256*scr,0,        /* move cursor position */
+               256*(gfx_y/2)+gfx_x++);
+         u.axbx = int10(0x0800,256*scr,0,0);     /* read character/attribute */
+         if (u.lh.al == 0xDB)
+           pix[0] = pix[1] =  u.lh.ah    &0xF;
+         else if (u.lh.al == 0xDC) {
+           pix[0] =          (u.lh.ah/16)&0xF;
+           pix[1] =           u.lh.ah    &0xF; }
+         else
+           pix[0] = pix[1] = (u.lh.ah/16)&0xF;
+         pix[gfx_y&1] = coltable[ch & 0x7];
+         int10(0x0900|                  /* output char */
+               (pix[0]==pix[1]?0xDB:0xDC),
+               256*scr+(0x7F&(pix[0]*16+pix[1])),1,0); }
+       if (!in_gfx)
+         int10(0x0200,256*scr,0,        /* restore cursor position */
+               256*(cy-1)+(cx-1)); }
+      else if (gfx_x < gfx_columns && gfx_y < gfx_rows)
+       int10(0x0C00|                    /* write pixel */
+             (ch <= 7 ? coltable[ch] : ch),256*scr,gfx_x++,gfx_y); }
+    return; }
+#endif
+  switch (esc_state) {
+  case esc_esc:
+    if (ch == '[') {
+      esc_state = esc_bracket;
+      argn = 0;
+      memset(&argi, 0, sizeof(argi));
+      memset(args, 0, MAXSTRARGS*MAXSTRARGLEN); }
+    else {
+      esc_state = esc_std;
+      goto doputchar; }
+    break;
+  case esc_bracket:
+  case esc_semicolon:
+    if (ch == '\'') {
+      esc_state = esc_str;
+      break; }
+    /* fall thru */
+    if (ch == '=' || ch == '?')
+      break;
+    /* fall thru */
+  case esc_digit:
+    if (ch >= '0' && ch <= '9') {
+      esc_state = esc_digit;
+      if (argn < MAXARGS)
+       argi[argn] = 10*argi[argn] + ch - '0'; }
+    else
+      quote:
+      if (ch == ';') {
+      esc_state = esc_semicolon;
+      argn++; }
+    else {
+      if (esc_state == esc_digit || esc_state == esc_quote)
+       argn++;
+      esc_state = esc_std;
+      docommand(ch); }
+    break;
+  case esc_str:
+    if (ch == '\'') {
+      esc_state = esc_quote;
+      break; }
+    { int i;
+    if (argn < MAXSTRARGS && (i = strlen(args[argn])) < MAXSTRARGLEN-2)
+      args[argn][i] = ch; }
+    break;
+  case esc_quote:
+    goto quote;
+  case esc_init: {
+    int i;
+    esc_state = esc_std;
+    u.axbx = int10(0x0F00,0,0,0);                 /* get graphics mode information */
+#ifdef GFX
+    if (defaultmode < 0)
+      defaultmode = curmode = u.lh.al ? u.lh.al : 3;
+#endif
+    columns = u.lh.ah;
+    scr     = u.lh.bh;
+    int10(0x0200,256*scr,0,0);           /* put cursor to home position */
+    for (i = 0; ++i < 100; ) {
+      int10(0x0E0A,256*scr,0,0);         /* output LF */
+      int10(0x0300,256*scr,0,0);         /* get cursor position */
+      if (int10ret.lh.dh != i)          /* row */
+       break; }
+    rows    = i;
+    int10(0x0200,256*scr,0,0);           /* put cursor to home position */
+#ifdef GFX
+    char_width  = 8;
+    char_height = *(unsigned char *)0x485;
+    if (char_height < 8 || char_height > 20)
+      char_height = 8;
+    gfx_columns = char_width*columns;
+    gfx_rows    = char_height*rows;
+    if (!curmodetype) {
+      int10(0x0941,256*scr+127,1,0);     /* 'A', bright, white on white */
+      u.axbx = int10(0x0800,256*scr,0,0);         /* read character/attribute */
+      if (u.lh.al == 0x41 &&
+         u.lh.ah == 127)
+       curmodetype = 'T';
+      else {
+       int10(0x0C40,256*scr,0,0);       /* write pixel */
+       u.axbx = int10(0x0D00,256*scr,0,0);       /* read pixel color */
+       if (u.lh.al == 0x40)
+         curmodetype = 'H';
+       else
+         curmodetype = 'G'; } }
+#endif
+    attr = fg = 7;
+    cx = cy = scx = scy = bg = blink = reverse = bright = 0;
+    wraparound = 1;
+#ifdef GFX
+    int10(0x0600,                        /* clear screen */
+         curmodetype != 'T' ? 0 : 7*256,0,256*(rows-1)+columns-1); }
+#else
+    int10(0x0600,                        /* clear screen */
+         7*256,0,256*(rows-1)+columns-1); }
+#endif
+
+    /* fall thru */
+  default:
+    if (ch == 0x1B)
+      esc_state = esc_esc;
+    else if (ch == '\t') {
+      int i;
+      for (i = 8-cx%8; i--; ) ansi_putc(' '); }
+    else if (ch == '\r') {
+      cx = 0; goto updatecursor; }
+    else if (ch == '\n') {
+      ansi_putc('\r'); goto newline; }
+    else if (ch == '\010') {
+      if (cx) {
+       cx--; goto updatecursor; } }
+    else {
+    doputchar:
+#ifdef GFX
+      if (curmodetype == 'G' && (reverse ? fg : bg))
+       int10(0x09DB,(attr/16)&0x07,1,0);/* set background in gfx mode */
+#endif
+#ifdef GFX
+      int10(0x0900|ch,                   /* output char */
+           curmodetype == 'H' ? reverse ? fg*256+bg : bg*256+fg :
+           256*scr+attr,1,0);
+#else
+      int10(0x0900|ch,256*scr+attr,1,0); /* output char */
+#endif
+      if (++cx == columns) {
+       if (!wraparound)
+         cx--;
+        else {
+         cx = 0;
+       newline:
+         if (++cy == rows)
+#ifdef GFX
+           int10(0x0601,                /* scroll screen */
+                 256*(curmodetype != 'T' ? reverse ? fg : bg : attr),0,
+                 256*--cy+columns-1); }
+#else
+           int10(0x0601,256*attr,0,     /* scroll screen */
+                 256*--cy+columns-1); }
+#endif
+      }
+    updatecursor:
+      int10(0x0200,256*scr,0,256*(cy-1)+(cx-1)); /* update cursor position */ } }
+  return;
+}
diff --git a/ansiesc.h b/ansiesc.h
new file mode 100644 (file)
index 0000000..a58e547
--- /dev/null
+++ b/ansiesc.h
@@ -0,0 +1,3 @@
+extern void ansi_reset(void);
+extern void ansi_putc(unsigned int ch);
+extern void enable_cursor(int);
diff --git a/boot.inc b/boot.inc
new file mode 100644 (file)
index 0000000..241d963
--- /dev/null
+++ b/boot.inc
@@ -0,0 +1,44 @@
+/*
+ * bootsect.inc  -  constants for assembler module for DOS boot sector
+ *
+ * Copyright (C) 1996-1998 Gero Kuhlmann   <gero@gkminix.han.de>
+ *
+ *  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
+ *  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.
+ */
+
+
+/*
+ *====================================================================
+ *
+ * Miscellaneous definitions
+ */
+
+#define SECT_SIZE      512             /* sector size must be 512 bytes */
+
+#define BOOT_SPC       1               /* sectors per cluster */
+#define BOOT_SPFAT     9               /* sectors per FAT */
+#define BOOT_SPTRK     18              /* sectors per track */
+#define BOOT_DIRNUM    224             /* number of root dir entries */
+#define BOOT_SECNUM    2880            /* total number of sectors */
+#define BOOT_FORMAT    0xf0            /* format ID for floppy disk */
+#define BOOT_ID                0x00            /* BIOS id of boot floppy disk */
+
+#define BOOT_OFFSET    0x7c00          /* offset for boot block in segment 0 */
+#define BOOT_SIG_OFS   0x7dfe          /* offset of boot signature in memory */
+#define BOOT_SIG       0xaa55          /* boot signature */
+
+#define DIR_SEG                0x0050          /* segment where DOS expects root dir */
+#define IOSYS_SEG      0x0070          /* load segment for IO.SYS */
+
diff --git a/bootmenu.c b/bootmenu.c
new file mode 100644 (file)
index 0000000..2c8a157
--- /dev/null
@@ -0,0 +1,405 @@
+#include "stddef.h"
+#include "string.h"
+#include "ansiesc.h"
+#include "printf.h"
+#include "md5.h"
+#include "misc.h"
+#include "etherboot.h"
+
+static int             menutmo = 10, menudefault  = 0;
+static unsigned char   *defparams = NULL;
+static int             defparams_max = 0;
+
+#ifdef MOTD
+/**************************************************************************
+SHOW_MOTD - display the message of the day
+**************************************************************************/
+void show_motd(unsigned char *motd[])
+{
+       unsigned char   *ptr;
+       int             i, j, k = 0;
+
+       for (i = 0; i < RFC1533_VENDOR_NUMOFMOTD; i++) if (motd[i]) {
+               for (j = TAG_LEN(motd[i]), ptr = motd[i]+2; j-- && *ptr; )
+                       putchar(*ptr++);
+               putchar('\n');
+       }
+}
+#endif
+
+#if    !defined(ANSIESC) || defined(CONSOLE_SERIAL)
+static const char *const clrline = "                                                                               ";
+#endif
+
+static int getoptvalue(unsigned char **ptr, int *len, int *rc)
+{
+       unsigned char *tmp,*old;
+       int  i,l;
+
+       for (tmp = *ptr, l = *len; *tmp != '='; tmp++, l--);
+       old = ++tmp; l--;
+       if (!*tmp || *tmp == ':' || l <= 0)
+               return (0);
+       i = getdec(&tmp);
+       if (i < 0 || (l -= tmp - old) < 0)
+               return (0);
+       *rc = i;
+       *len = l;
+       *ptr = tmp;
+       return (1);
+}
+
+/**************************************************************************
+PARSE_MENUOPTS - parse menu options and set control variables
+**************************************************************************/
+void parse_menuopts(unsigned char *opt, int len)
+{
+       /* This code assumes that "bootpd" terminates the control string
+          with a '\000' character */
+       while (len > 0 && *opt) {
+               if (!memcmp(opt,"timeout=",8)) {
+                       if (!getoptvalue(&opt, &len, &menutmo))
+                               return; }
+               else if (!memcmp(opt,"default=",8)) {
+                       if (!getoptvalue(&opt, &len, &menudefault))
+                               return; }
+               while (len > 0 && *opt != ':') { opt++; len--; }
+               while (len > 0 && *opt == ':') { opt++; len--; }
+       }
+}
+
+/**************************************************************************
+GETPARMS - get user provided parameters
+**************************************************************************/
+
+#ifdef USRPARMS
+static int getparms(struct bootpd_t *bootp, unsigned char *end_of_rfc1533)
+{
+       unsigned char *ptr = end_of_rfc1533+2;
+       int  ch,i = 0, max = &bootp->bootp_extension[MAX_BOOTP_EXTLEN] - ptr - 1;
+
+       if (!end_of_rfc1533 || max < 0)
+               return (0);
+       else if (max > 255)
+               max = 255;
+restart:
+#if    defined(ANSIESC) && !defined(CONSOLE_SERIAL)
+       printf("\rParams: \033[K");
+#else
+       printf("%s%s%s","\rParams: ",clrline+8,"\rParams: ");
+#endif
+       for (ch = i; ch; )
+               putchar(ptr[-(ch--)]);
+       for (;;) {
+#if    defined(ANSIESC) && defined(CONSOLE_CRT)
+               enable_cursor(1);
+               ch = getchar();
+               enable_cursor(0);
+#else
+               ch = getchar();
+#endif
+               if (ch == '\n')
+                       break;
+               if (ch == ('U'&0x1F)) {
+                       ptr -= i;
+                       i = 0;
+                       goto restart; }
+               if (ch == ('H'&0x1F)) {
+                       if (i) {
+                               i--; ptr--;
+                               printf("\010 \010"); }
+                       continue; }
+               if (ch == ('L'&0x1F))
+                       goto restart;
+               if (i == max || (signed char)ch < (signed char)' ')
+                       continue;
+               putchar(*ptr++ = ch);
+               i++; }
+       if (i) {
+               end_of_rfc1533[0] = RFC1533_VENDOR_ADDPARM;
+               end_of_rfc1533[1] = i;
+               *ptr++ = '\000';
+               *ptr   = RFC1533_END;
+               i += 3; }
+       putchar('\n');
+       return (i);
+}
+#endif
+
+/**************************************************************************
+GETHEX - compute one hex byte
+**************************************************************************/
+#ifdef PASSWD
+static int gethex(unsigned char *dat)
+{
+       int  i,j;
+
+       i = (j = *dat) > '9' ? (j+9) & 0xF : j - '0';
+       dat++;
+       return (16*i + ((j = *dat) > '9' ? (j+9) & 0xF : j - '0'));
+}
+#endif
+
+/**************************************************************************
+SELECTIMAGE - interactively ask the user for the boot image
+**************************************************************************/
+
+/* Warning! GCC 2.7.2 has difficulties with analyzing the data flow in
+   the following function; it will sometimes clobber some of the local
+   variables. If you encounter strange behavior, try to introduce more
+   temporary variables for storing intermediate results. This might
+   help GCC... */
+
+int selectImage(struct bootpd_t *bootp, unsigned char *imagelist[],
+       unsigned char *end_of_rfc1533)
+{
+#ifdef USRPARMS
+       int flag_parms = 1;
+       int modifier_keys = 0;
+#endif
+#ifdef PASSWD
+       int flag_image = 1;
+#endif
+       unsigned char   *e,*s,*d,*p;
+       int             i,j,k,m,len;
+       unsigned long   tmo;
+       in_addr         ip;
+
+       printf("List of available boot images:\n\n");
+       for (i = j = 0; i < RFC1533_VENDOR_NUMOFIMG; i++) {
+               if ((e = imagelist[i]) != NULL) {
+#if    defined(ANSIESC) && !defined(CONSOLE_SERIAL)
+#ifdef SHOW_NUMERIC
+                       printf("\033[4C%c) ",'1'+j++);
+#else
+                       printf("\033[4C%c) ",'A'+j++);
+#endif
+#else
+#ifdef SHOW_NUMERIC
+                       printf("    %c) ",'1'+j++);
+#else
+                       printf("    %c) ",'A'+j++);
+#endif
+#endif
+                       for (s = e+2, len = TAG_LEN(e)+2;
+                            (s - e) < len && *s != ':';
+                            putchar(*s++));
+                       putchar('\n');
+               }
+       }
+       m = j;
+       putchar('\n');
+reselect:
+#if    defined(ANSIESC) && !defined(CONSOLE_SERIAL)
+       printf("Select: \033[K");
+#else
+       printf("%s%s%s","\rSelect: ",clrline+8,"\rSelect: ");
+#endif
+       tmo = currticks()/TICKS_PER_SEC + menutmo;
+       for (;;) {
+               if (menutmo >= 0) { for (i = -1; !iskey(); ) {
+                       if ((k = tmo - currticks()/TICKS_PER_SEC) <= 0) {
+selectdefault:
+                               if (menudefault >= 0 && menudefault <
+                                   RFC1533_VENDOR_NUMOFIMG) {
+                                       i = menudefault;
+                                       goto findimg;
+                               } else if (menudefault >= RFC1533_VENDOR_IMG &&
+                                        menudefault < RFC1533_VENDOR_IMG +
+                                        RFC1533_VENDOR_NUMOFIMG &&
+                                        imagelist[menudefault -
+                                                 RFC1533_VENDOR_IMG]) {
+                                       j = menudefault-RFC1533_VENDOR_IMG;
+                                       goto img;
+                               }
+                               i = ESC;
+                               goto key;
+                       } else if (i != k) {
+#if    defined(ANSIESC) && !defined(CONSOLE_SERIAL)
+                               printf("\033[s(%d seconds)\033[K\033[u",i = k);
+#else
+                               printf("%s%s%s(%d seconds)",
+                                      "\rSelect: ",clrline+8,
+                                      "\rSelect: ",i = k);
+#endif
+                       }
+               } }
+#if    defined(ANSIESC) && defined(CONSOLE_CRT)
+               enable_cursor(1);
+               i = getchar();
+               enable_cursor(0);
+#else
+               i = getchar();
+#endif
+       key:
+#ifdef USRPARAMS
+               modifier_keys = 0;
+#endif
+#if    defined(USRPARMS) && defined(CONSOLE_CRT)
+               modifier_keys |= getshift();
+#endif
+               if (i == ESC)
+                       return (255);
+               if (i == '\n') {
+                       goto selectdefault;
+               }
+               if (i == '\t' || i == ' ') {
+                       menutmo = -1;
+                       printf("\r");
+                       goto reselect;
+               }
+               if ((i >= 'A') && (i < 'A'+m)) {
+                       i -= 'A';
+#ifdef USRPARMS
+                       modifier_keys |= 3; /* ShiftL + ShiftR */
+#endif
+                       break;
+               } else if ((i >= 'a') && (i < 'a'+m)) {
+                       i -= 'a';
+                       break;
+               } else if ((i >= '1') && (i < '1'+m)) {
+                       i -= '1';
+                       break;
+               }
+       }
+findimg:
+       j = 0;
+       while (i >= 0) {
+               if (imagelist[j] != NULL) i--;
+               j++;
+       }
+       j--;
+img:
+#if    defined(ANSIESC) && !defined(CONSOLE_SERIAL)
+       printf("\033[K");
+#else
+       printf("%s%s%s","\rSelect: ",clrline+8,"\rSelect: ");
+#endif
+       for (len = TAG_LEN(e = imagelist[j]) + 2, s = e + 2;
+            (s - e) < len && *s != ':';
+            putchar(*s++));
+       putchar('\n');
+#if    0       /* unimplemented for now */
+       for (i = ARP_SERVER; i <= ARP_GATEWAY; i++) {
+               if ((++s - e) >= len) goto local_disk;
+               if (inet_aton(s, &ip)) {
+                       arptable[i].ipaddr.s_addr = ip.s_addr;
+                       memset(arptable[i].node, 0, ETH_ALEN);
+                       while ((s - e) < len && *s != ':') s++;
+               }
+       }
+#else
+       /* Just skip the server and gateway components */
+       s++;
+       while ((s - e) < len && *s != ':') s++;
+       s++;
+       while ((s - e) < len && *s != ':') s++;
+#endif
+       if ((++s - e) >= len) goto local_disk;
+       for (d = s; (d - e) < len && *d != ':'; d++);
+       for (p = d + 1, j = 0; (p - e) < len && *p && *p != ':'; p++, j++);
+       for (p++, i = 0; (p - e) < len && *p != ':'; p++) {
+#if    defined(USRPARMS) || defined(PASSWD)
+               if (*p >= '0' && *p <= '9')
+                       i = 10*i + *p - '0';
+               else {
+                       k = *p & ~0x20;
+#ifdef USRPARMS
+                       if (k == 'P') {
+                               flag_parms = i;
+                               /* 0 - never interactively accept parameters
+                                * 1 - require password before accepting parms.
+                                * 2 - always ask for passwd and parameters
+                                * 3 - always ask for parameters
+                                */
+                       }
+#endif
+#ifdef PASSWD
+                       if (k == 'I') {
+                               flag_image = i;
+                               /* 0 - do not require a password for this image
+                                * 1 - require a password for this image
+                                */
+                       }
+#endif
+                       i = 0;
+               }
+#endif
+       }
+       defparams = p + 1;
+       defparams_max = len - (defparams - (unsigned char *)e);
+#ifdef USRPARMS
+       if (flag_parms == 1 && modifier_keys)
+               flag_parms++;
+#endif
+#ifdef PASSWD
+       if ((flag_image > 0
+#ifdef USRPARMS
+            || flag_parms == 2
+#endif
+           ) && j == 32) {
+               unsigned char md5[16];
+               printf("Passwd: ");
+#if    defined(ANSIESC) && defined(CONSOLE_CRT)
+               enable_cursor(1);
+#endif
+               while ((i = getchar()) != '\n') {
+                       if (i == ('U'&0x1F)) md5_done(md5);
+                       else                 md5_put(i);
+               }
+#if    defined(ANSIESC) && defined(CONSOLE_CRT)
+               enable_cursor(0);
+#endif
+               md5_done(md5);
+               for (i = 16, p = d+31; i--; p -= 2) {
+                       if (gethex(p) != md5[i]) {
+#if    defined(ANSIESC) && !defined(CONSOLE_SERIAL)
+                               printf("\r\033[K\033[1A");
+#else
+                               printf("\r");
+#endif
+                               goto reselect;
+                       }
+               }
+               putchar('\n');
+       }
+#endif
+#ifdef USRPARMS
+       if (flag_parms > 1)
+               i = getparms(bootp, end_of_rfc1533);
+       else
+               i = 0;
+#endif
+       if ((len = d - s) <= 0 || len > (int)sizeof(bootp->bootp_reply.bp_file)-1 || !*s) {
+       local_disk:
+               return (255);
+       }
+
+
+       if (end_of_rfc1533 != NULL &&
+           end_of_rfc1533 + 4 < &bootp->bootp_extension[MAX_BOOTP_EXTLEN]) {
+#ifdef USRPARMS
+               d    = end_of_rfc1533 + i;
+#else
+               d    = end_of_rfc1533;
+#endif
+               i    = e-d;
+               *d++ = RFC1533_VENDOR_SELECTION;
+               *d++ = 1;
+               *d++ = *e;
+               *d   = RFC1533_END;
+       }
+
+       /* if name is -, reuse previous filename */
+        if (!(s[0] == '-' && s[1] == ':')) {
+            memcpy(bootp->bootp_reply.bp_file, s, len);
+            bootp->bootp_reply.bp_file[len] = '\0';
+        }
+       return (1);
+}
+
+/*
+ * Local variables:
+ *  c-basic-offset: 8
+ * End:
+ */
diff --git a/bootmenu.h b/bootmenu.h
new file mode 100644 (file)
index 0000000..8c812a8
--- /dev/null
@@ -0,0 +1,3 @@
+extern void show_motd(unsigned char *[]);
+extern void parse_menuopts(unsigned char *, int);
+extern int selectImage(struct bootpd_t *, unsigned char *[], unsigned char *);
diff --git a/disdosbb.pl b/disdosbb.pl
new file mode 100755 (executable)
index 0000000..b01e876
--- /dev/null
@@ -0,0 +1,50 @@
+#!/usr/bin/perl -w
+if ($#ARGV >= 0) {
+       open(STDIN, "$ARGV[0]") or die "$ARGV[0]: $!\n";
+}
+binmode(STDIN);
+$nread = read(STDIN, $params, 0x3e);
+(defined($nread) and $nread == 0x3e) or die "Cannot read 0x3e bytes of boot block\n";
+(undef,
+       $oem_name,
+       $bytes_per_sector,
+       $sectors_per_cluster,
+       $reserved_sectors,
+       $fat_copies,
+       $root_dir_entries,
+       $total_disk_sectors,
+       $media_descriptor,
+       $sectors_per_fat,
+       $sectors_per_track,
+       $sides,
+       $hidden_sectors_low,
+       $hidden_sectors_high,
+       $total_num_sectors,
+       $phys_drive_number_1,
+       $phys_drive_number_2,
+       $boot_record_sig,
+       $vol_serial_num,
+       $volume_label,
+       $file_system_id) = unpack('A3a8vCvCvvCvvvvvVCCCVa11a8', $params);
+       print <<EOF;
+oem_name: $oem_name
+bytes_per_sector: $bytes_per_sector
+sectors_per_cluster: $sectors_per_cluster
+reserved_sectors: $reserved_sectors
+fat_copies: $fat_copies
+root_dir_entries: $root_dir_entries
+total_disk_sectors: $total_disk_sectors
+media_descriptor: $media_descriptor
+sectors_per_fat: $sectors_per_fat
+sectors_per_track: $sectors_per_track
+sides: $sides
+hidden_sectors_low: $hidden_sectors_low
+hidden_sectors_high: $hidden_sectors_high
+total_num_sectors: $total_num_sectors
+phys_drive_number_1: $phys_drive_number_1
+phys_drive_number_2: $phys_drive_number_2
+boot_record_sig: $boot_record_sig
+vol_serial_num: $vol_serial_num
+volume_label: $volume_label
+file_system_id: $file_system_id
+EOF
diff --git a/dismbr.pl b/dismbr.pl
new file mode 100755 (executable)
index 0000000..780f0f8
--- /dev/null
+++ b/dismbr.pl
@@ -0,0 +1,43 @@
+#!/usr/bin/perl -w
+#
+# Quick and dirty program to decode a partition table in MBR
+# GPL, July 2000, Ken Yap
+#
+
+sub decodechs ($)
+{
+       my ($chs) = @_;
+       my ($c, $h, $s);
+
+       ($h, $s, $c) = unpack('CCC', $chs);
+       $c += ($s & 0xC0) << 2;
+       $s &= 0x3F;
+       return ($c, $h, $s);
+}
+
+sub dismbr ($)
+{
+       my ($par) = @_;
+       my ($flags, $chs1, $type, $chs2, $bootseg, $numsegs);
+
+       ($flags, $chs1, $type, $chs2, $bootseg, $numsegs) =
+               unpack('Ca3Ca3VV', $par);
+       printf "%s type:%02x %d/%d/%d-%d/%d/%d boot:%04x sectors:%04x\n",
+               ($flags & 0x80) ? '*' : ' ', $type,
+               decodechs($chs1), decodechs($chs2),
+               $bootseg, $numsegs;
+}
+
+if ($#ARGV >= 0) {
+       open(STDIN, "$ARGV[0]") or die "$ARGV[0]: $!\n";
+}
+binmode(STDIN);
+$nread = read(STDIN, $mbr, 512);
+(defined($nread) and $nread == 512) or die "Cannot read 512 bytes of MBR\n";
+(undef, $par[0], $par[1], $par[2], $par[3], $sig) =
+       unpack('a446a16a16a16a16v', $mbr);
+$sig == 0xAA55 or die "Input is not a MBR\n";
+foreach $i (0..3) {
+       print "$i: ";
+       dismbr($par[$i]);
+}
diff --git a/disnbi.pl b/disnbi.pl
new file mode 100755 (executable)
index 0000000..5be98ba
--- /dev/null
+++ b/disnbi.pl
@@ -0,0 +1,211 @@
+#!/usr/bin/perl -w
+#
+# Quick Perl program to decode and display details about 
+# tagged images created by mknbi
+# -e extracts directory to `nbidir' and segments to `segmentN'
+# N = 0, 1, 2, ...
+#
+# Added code to dump vendor tag in hex (for DOS disk parameters)
+#
+# Ken Yap, August 1999
+#
+
+use strict;
+
+use vars qw($imagefile $data $curoffset $dirfile @seglengths $vendordata
+       $extract $segnum $status $i);
+
+sub getvendordata ($)
+{
+       my ($flags) = @_;
+
+       my $vendordata = '';
+       my $vendorlen = ($flags & 0xff) >> 4;
+       if ($vendorlen > 0) {
+               $vendorlen *= 4;
+               $vendordata = unpack("a$vendorlen", substr($data, $curoffset));
+               $curoffset += $vendorlen;
+       }
+       return ($vendordata);
+}
+
+sub decodesegmentflags ($)
+{
+       my ($flags) = @_;
+       my ($type);
+
+       $flags >>= 24;
+       $flags &= 0x3;
+       ($flags == 0) and $type = "Absolute";
+       ($flags == 1) and $type = "Follows last segment";
+       ($flags == 2) and $type = "Below end of memory";
+       ($flags == 3) and $type = "Below last segment loaded";
+       return ($type);
+}
+
+sub one_nbi_segment ($)
+{
+       my ($segnum) = @_;
+       my ($type, $vendordata, @vdata, $i);
+
+       my ($flags, $loadaddr, $imagelen, $memlength) = unpack('V4', substr($data, $curoffset));
+       $curoffset += 16;
+       print "Segment number $segnum\n";
+       printf "Load address:\t\t%08x\n", $loadaddr;
+       printf "Image length:\t\t%d\n", $imagelen;
+       printf "Memory length:\t\t%d\n", $memlength;
+       $type = &decodesegmentflags($flags);
+       print "Position:\t\t$type\n";
+       printf "Vendor tag:\t\t%d\n", ($flags >> 8) & 0xff;
+       if (($vendordata = &getvendordata($flags)) ne '') {
+               print "Vendor data:\t\t\"", $vendordata, "\"\n";
+               @vdata = unpack('C*', $vendordata);
+               print "Vendor data in hex:\t";
+               foreach $i (0..$#vdata) {
+                       printf "%02x ", $vdata[$i];
+               }
+               print "\n";
+       }
+       print "\n";
+       push (@seglengths, $imagelen);
+       return (($flags >> 26) & 1);
+}
+
+sub decode_nbi
+{
+       my ($magic, $flags, $bx, $ds, $ip, $cs) = unpack('a4Vv4', substr($data, 0, 16));
+
+       $curoffset = 16;
+       # Decode the header
+       printf "Type: NBI\nHeader location:\t%04x:%04x\n", $ds, $bx;
+       if (($flags >> 31) & 1) {
+               printf "Start address:\t\t%04x%04x (flat)\n", $cs, $ip;
+       } else {
+               printf "Start address:\t\t%04x:%04x\n", $cs, $ip;
+       }
+       print "Flags:\n";
+               print "\tReturn to loader after execution (extension)\n" if (($flags >> 8) &  1);
+       if (($vendordata = &getvendordata($flags)) ne '') {
+               print "Vendor data:\t\t", $vendordata, "\n";
+       }
+       print "\n";
+
+       # Now decode each segment record
+       $segnum = 1;
+       do {
+               $i = &one_nbi_segment($segnum);
+               ++$segnum;
+       } while (!$i);
+}
+
+sub one_elf_segment ($$)
+{
+       my ($segnum, $curoffset) = @_;
+
+       my ($offset, $vaddr, $paddr, $filesz, $memsz, $flags,
+               $align) = unpack('@4V6', substr($data, $curoffset));
+       print "Segment number $segnum\n";
+       printf "Load address:\t\t%08x\n", $vaddr;
+       printf "Image length:\t\t%d\n", $filesz;
+       printf "Memory length:\t\t%d\n", $memsz;
+       print "\n";
+       push (@seglengths, $filesz);
+}
+
+sub decode_elf
+{
+       my ($entry, $phoff, $shoff, $flags, $ehsize, $phentsize,
+               $phnum) = unpack('@24V4v3', $data);
+       printf "Type: ELF\nStart address:\t\t%08x\n", $entry;
+       print "Flags:\n";
+               print "\tReturn to loader after execution (extension)\n" if ($flags & 0x8000000);
+       print "\n";
+       $curoffset = $phoff;
+       foreach $i (1..$phnum) {
+               &one_elf_segment($i, $curoffset);
+               $curoffset += $phentsize;
+       }
+}
+
+$extract = 0;
+@seglengths = ();
+$#ARGV >= 0 or die "Usage: disnbi [-e] Etherboot-image-file\n";
+if ($ARGV[0] eq '-e') {
+       $extract = 1; shift
+}
+$#ARGV >= 0 or die "Usage: disnbi [-e] Etherboot-image-file\n";
+$imagefile= $ARGV[0];
+open(I, $ARGV[0]) or die "$imagefile: $!\n";
+binmode(I);
+(defined($status = sysread(I, $data, 512)) and $status == 512)
+       or die "$imagefile: Cannot read header\n";
+my ($magic) = unpack('a4', substr($data, 0, 4));
+if ($magic eq "\x36\x13\x03\x1B") {
+       &decode_nbi();
+       $dirfile = 'nbidir';
+} elsif ($magic eq "\x7FELF") {
+       &decode_elf();
+       $dirfile = 'elfdir';
+} else {
+       die "$imagefile: Not a tagged or ELF image file\n";
+}
+
+exit(0) if ($extract == 0);
+print "Dumping directory to `$dirfile'...\n";
+open(O, ">$dirfile") or die "$dirfile: $!\n";
+binmode(0);
+print O $data;
+close(O);
+$data = '';
+foreach $i (0..$#seglengths) {
+       print "Extracting segment $i to `segment$i'...\n";
+       open(O, ">segment$i") or die "segment$i: $!\n";
+       binmode(O);
+       (defined($status = sysread(I, $data, $seglengths[$i]))
+               and $status = $seglengths[$i])
+               or die "$imagefile: Cannot read data\n";
+       print O $data;
+       close(O);
+}
+print "Done\n";
+exit(0);
+
+__END__
+
+=head1 NAME
+
+disnbi - display Etherboot image
+
+=head1 SYNOPSIS
+
+B<disnbi> [C<-e>] I<Etherboot-file>
+
+=head1 DESCRIPTION
+
+B<disnbl> is a program that to display in symbolic form the contents
+of a Etherboot image created by mknbi or mkelf. Detection of image type
+is automatic.
+
+B<-e> Extract contents of image as well. The directory will be written
+to C<nbidir> or C<elfdir> and the segments to C<segment>I<n> where I<n>
+is 0, 1, 2, etc.
+
+=head1 BUGS
+
+Please report all bugs to the author.
+
+=head1 SEE ALSO
+
+Etherboot tutorial at C<http://etherboot.sourceforge.net/>
+
+=head1 COPYRIGHT
+
+B<disnbl> is under the GNU Public License
+
+=head1 AUTHOR
+
+Ken Yap (C<ken_yap@users.sourceforge.net>)
+
+=head1 DATE
+
+Version 1.4 December 2002
diff --git a/elf_boot.h b/elf_boot.h
new file mode 100644 (file)
index 0000000..45cb1e3
--- /dev/null
@@ -0,0 +1,83 @@
+#ifndef ELF_BOOT_H 
+#define ELF_BOOT_H 
+
+typedef unsigned char  uint8_t;
+typedef unsigned short uint16_t;
+typedef unsigned int   uint32_t;
+
+/* 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_BHDR_MAGIC         0x0E1FB007
+
+typedef uint16_t Elf_Half;
+typedef uint32_t Elf_Word;
+
+typedef struct Elf_Bhdr
+{
+       Elf_Word b_signature; /* "0x0E1FB007" */
+       Elf_Word b_size;
+       Elf_Half b_checksum;
+       Elf_Half b_records;
+} Elf_Bhdr;
+
+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;
+
+
+/* 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. */
+
+
+/* Notes that are passed to a loaded image */
+/* For standard notes n_namesz must be zero */
+#define EBN_FIRMWARE_TYPE      0x00000001
+/* ASCIZ name of the platform firmware. */
+#define EBN_BOOTLOADER_NAME    0x00000002
+/* This specifies just the ASCIZ name of the bootloader */
+#define EBN_BOOTLOADER_VERSION 0x00000003
+/* This specifies the version of the bootloader as an ASCIZ string */
+#define EBN_COMMAND_LINE       0x00000004
+/* This specifies a command line that can be set by user interaction,
+ * and is provided as a free form ASCIZ string to the loaded image.
+ */
+#define EBN_NOP                        0x00000005
+/* A note nop note has no meaning, useful for inserting explicit padding */
+#define EBN_LOADED_IMAGE       0x00000006
+/* An ASCIZ string naming the loaded image */
+
+
+/* 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
+
+
+#endif /* ELF_BOOT_H */
diff --git a/etherboot.h b/etherboot.h
new file mode 100644 (file)
index 0000000..b9c2d5d
--- /dev/null
@@ -0,0 +1,297 @@
+/*
+ *     Info we need to know from Etherboot
+ */
+
+#define        MKNBI_VERSION           "mknbi-" VERSION
+
+#define        ESC                     '\033'
+
+#define        TICKS_PER_SEC           18
+
+#define ETH_ALEN               6       /* Size of Ethernet address */
+#define ETH_HLEN               14      /* Size of ethernet header */
+#define        ETH_ZLEN                60      /* Minimum packet */
+#define        ETH_FRAME_LEN           1514    /* Maximum packet */
+#ifndef        ETH_MAX_MTU
+#define        ETH_MAX_MTU             (ETH_FRAME_LEN-ETH_HLEN)
+#endif
+
+/* ANSI prototyping macro */
+#ifdef __STDC__
+#define        P(x)    x
+#else
+#define        P(x)    ()
+#endif
+
+typedef struct {
+       unsigned long   s_addr;
+} in_addr;
+
+#define DHCP_OPT_LEN           312
+
+/* Vendor magic cookies in host order */
+#define        RFC_1048        0x63538263
+#define        VEND_CMU        0x00554D43
+#define        VEND_STAN       0x4E415453
+#define        VEND_EB         0x687445E4      /* äEth */
+
+#define TAG_LEN(p)             (*((p)+1))
+#define RFC1533_PAD            0       /* tag for NO-OP */
+#define RFC1533_NETMASK                1       /* tag for netmask */
+#define RFC1533_GATEWAY                3       /* tag for gateway */
+#define RFC1533_HOSTNAME       12      /* tag for host name */
+#define RFC1533_ROOTPATH       17      /* tag for root directory */
+
+#define RFC1533_VENDOR_MAJOR   0
+#define RFC1533_VENDOR_MINOR   0
+
+#define RFC1533_VENDOR_MAGIC   128     /* Etherboot identification */
+#define RFC1533_VENDOR_ADDPARM 129     /* Addition to kernel command line */
+#define RFC1533_VENDOR_ETHDEV  130     /* Device by which to find root FS */
+#define        RFC1533_VENDOR_ETHERBOOT_ENCAP  150
+#define        RFC1533_VENDOR_MENUOPTS 160
+#define RFC1533_VENDOR_SELECTION       176     /* index to user selected image */
+#define RFC1533_VENDOR_MOTD    184
+#define RFC1533_VENDOR_NUMOFMOTD       8
+#define RFC1533_VENDOR_IMG     192
+#define        RFC1533_VENDOR_NUMOFIMG 16
+#define RFC1533_END            255     /* tag for end of record */
+
+#define MAX_BOOTP_EXTLEN       (ETH_MAX_MTU-sizeof(struct bootpip_t))
+
+struct iphdr {
+       char verhdrlen;
+       char service;
+       unsigned short len;
+       unsigned short ident;
+       unsigned short frags;
+       char ttl;
+       char protocol;
+       unsigned short chksum;
+       in_addr src;
+       in_addr dest;
+};
+
+struct udphdr {
+       unsigned short src;
+       unsigned short dest;
+       unsigned short len;
+       unsigned short chksum;
+};
+
+#define        BOOTP_REPLY     2
+
+struct bootp_t {
+       char bp_op;
+       char bp_htype;
+       char bp_hlen;
+       char bp_hops;
+       unsigned long bp_xid;
+       unsigned short bp_secs;
+       unsigned short unused;
+       in_addr bp_ciaddr;
+       in_addr bp_yiaddr;
+       in_addr bp_siaddr;
+       in_addr bp_giaddr;
+       char bp_hwaddr[16];
+       char bp_sname[64];
+       char bp_file[128];
+       char bp_vend[DHCP_OPT_LEN];
+};
+
+/* Format of a bootp IP packet */
+struct bootpip_t
+{
+       struct iphdr ip;
+       struct udphdr udp;
+       struct bootp_t bp;
+};
+
+/* Format of bootp packet with extensions */
+struct bootpd_t {
+       struct bootp_t bootp_reply;
+       unsigned char  bootp_extension[MAX_BOOTP_EXTLEN];
+};
+
+struct segoff
+{
+       unsigned short  offset, segment;
+};
+
+struct imgheader
+{
+       unsigned long   magic;
+       unsigned long   length;
+       struct segoff   location;
+       struct segoff   execaddr;
+};
+
+#define        TAG_MAGIC       0x1B031336      /* Little endian */
+
+/* ELF */
+#define EI_NIDENT      16      /* Size of e_ident array. */
+
+/* Values for e_type. */
+#define ET_NONE                0       /* No file type */
+#define ET_REL         1       /* Relocatable file */
+#define ET_EXEC                2       /* Executable file */
+#define ET_DYN         3       /* Shared object file */
+#define ET_CORE                4       /* Core file */
+
+/* Values for e_machine (incomplete). */
+#define EM_386         3       /* Intel 80386 */
+#define EM_486         6       /* Intel i486 */
+
+/* Values for p_type. */
+#define PT_NULL                0       /* Unused entry. */
+#define PT_LOAD                1       /* Loadable segment. */
+#define PT_DYNAMIC     2       /* Dynamic linking information segment. */
+#define PT_INTERP      3       /* Pathname of interpreter. */
+#define PT_NOTE                4       /* Auxiliary information. */
+#define PT_SHLIB       5       /* Reserved (not used). */
+#define PT_PHDR                6       /* Location of program header itself. */
+
+/* Values for p_flags. */
+#define PF_X           0x1     /* Executable. */
+#define PF_W           0x2     /* Writable. */
+#define PF_R           0x4     /* Readable. */
+
+/*
+ * ELF definitions common to all 32-bit architectures.
+ */
+
+typedef unsigned int   Elf32_Addr;
+typedef unsigned short Elf32_Half;
+typedef unsigned int   Elf32_Off;
+typedef int            Elf32_Sword;
+typedef unsigned int   Elf32_Word;
+typedef unsigned int   Elf32_Size;
+
+/*
+ * ELF header.
+ */
+typedef struct {
+       unsigned char   e_ident[EI_NIDENT];     /* File identification. */
+       Elf32_Half      e_type;         /* File type. */
+       Elf32_Half      e_machine;      /* Machine architecture. */
+       Elf32_Word      e_version;      /* ELF format version. */
+       Elf32_Addr      e_entry;        /* Entry point. */
+       Elf32_Off       e_phoff;        /* Program header file offset. */
+       Elf32_Off       e_shoff;        /* Section header file offset. */
+       Elf32_Word      e_flags;        /* Architecture-specific flags. */
+       Elf32_Half      e_ehsize;       /* Size of ELF header in bytes. */
+       Elf32_Half      e_phentsize;    /* Size of program header entry. */
+       Elf32_Half      e_phnum;        /* Number of program header entries. */
+       Elf32_Half      e_shentsize;    /* Size of section header entry. */
+       Elf32_Half      e_shnum;        /* Number of section header entries. */
+       Elf32_Half      e_shstrndx;     /* Section name strings section. */
+} Elf32_Ehdr;
+
+#define        ELF_MAGIC       0x464C457FL     /* e_ident[0:3], little-endian */
+
+union infoblock
+{
+       unsigned char           c[512];
+       struct imgheader        img;
+       Elf32_Ehdr              ehdr;
+};
+
+/* We use ELF names for some members to reduce ifdefs in first32.c */
+struct segment
+{
+       unsigned char   lengths;
+       unsigned char   tagnum;
+       unsigned char   mbz;
+       unsigned char   flags;
+       unsigned long   p_paddr;
+       unsigned long   seglength;
+       unsigned long   p_filesz;
+};
+
+enum segflags { F_ABS = 0, F_INCR = 1, F_EOM = 2, F_DECR = 3, F_FINAL = 4 };
+
+/*
+ * ELF Program header.
+ */
+typedef struct {
+       Elf32_Word      p_type;         /* Entry type. */
+       Elf32_Off       p_offset;       /* File offset of contents. */
+       Elf32_Addr      p_vaddr;        /* Virtual address (not used). */
+       Elf32_Addr      p_paddr;        /* Physical address. */
+       Elf32_Size      p_filesz;       /* Size of contents in file. */
+       Elf32_Size      p_memsz;        /* Size of contents in memory. */
+       Elf32_Word      p_flags;        /* Access permission flags. */
+       Elf32_Size      p_align;        /* Alignment in memory and file. */
+} Elf32_Phdr;
+
+/* These are determined by mknbi.pl. If that changes, so must this. */
+enum linuxsegments { S_FIRST = 0, S_PARAMS, S_BOOT, S_SETUP, S_KERNEL,
+       S_RAMDISK, S_NOTE, S_END };
+
+struct bootblock
+{
+       unsigned char   fill1[0x20];
+       unsigned short  cl_magic;       /* command line magic number */
+       unsigned short  cl_offset;      /* command line offset */
+       unsigned char   fill2[0x1FA-0x24];
+       short           vgamode;
+};
+
+#define CL_MAGIC       0xA33F          /* very unusual command sequence */
+
+struct setupblock
+{
+       unsigned char   jump[2];        /* jump instruction */
+       unsigned char   su_magic[4];    /* HdrS */
+       unsigned short  su_version;     /* >= 0x0201 */
+       unsigned char   fill[8];        /* realmode_switch, start_sys_seg
+                                          kernel_version */
+       unsigned char   su_type;        /* type of loader */
+       unsigned char   su_load_flags;
+       unsigned short  su_move_size;
+       unsigned long   su_32_start;
+       unsigned long   su_ramdisk_start;
+       unsigned long   su_ramdisk_size;
+       unsigned short  su_bootsect_kludge[2];
+       unsigned short  su_heap_end_ptr;
+       unsigned short  su_pad;
+       unsigned char   *su_cmd_line_ptr;/* new cmd line protocol used when
+                                          su_version >= 0x202 */
+       unsigned long   ramdisk_max;    /* highest safe address for the
+                                          contents of an initrd
+                                          su_version >= 0x203 */
+};
+
+#define        SU_MY_LOADER_TYPE       0x41
+
+struct ebinfo {
+       unsigned char   major, minor;
+       unsigned short  flags;
+};
+
+struct e820entry {
+       unsigned long long addr;
+       unsigned long long size;
+       unsigned long type;
+#define E820_RAM       1
+#define E820_RESERVED  2
+#define E820_ACPI      3 /* usable as RAM once ACPI tables have been read */
+#define E820_NVS       4
+};
+#define E820MAX 32
+struct meminfo {
+       unsigned short basememsize;
+       unsigned int memsize;
+       int map_count;
+       struct e820entry map[E820MAX];
+};
+extern struct meminfo meminfo;
+extern void get_memsizes P((void));
+
+/* Assembler routines */
+extern unsigned long currticks P((void));
+extern int console_getc P((void));
+extern void console_putc P((int));
+extern int console_ischar P((void));
+extern int getshift P((void));
+extern void cpu_nap P((void));
diff --git a/first-dos.S b/first-dos.S
new file mode 100644 (file)
index 0000000..fe59077
--- /dev/null
@@ -0,0 +1,1985 @@
+; first.S  -  primary boot loader for DOS
+;
+; Copyright (C) 1996-1998 Gero Kuhlmann   <gero@gkminix.han.de>
+; Modifications for booting FreeDOS by Ken Yap <ken_yap@users.sourceforge.net>
+;
+;  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
+;  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.
+
+#if    !defined(USE_NASM) && !defined(USE_AS86)
+#define        USE_AS86
+#endif
+
+#ifdef USE_AS86
+#define        CON(x)          *x
+#define        BCON(x)         *x
+#define        LOC(x)          x
+#define        BLOC(x)         byte ptr x
+#define        WLOC(x)         word ptr x
+#define        JMP(x)          jmp x
+#define        STRDECL(s)      .ascii  s
+#define        SEGCS           seg     cs
+#define        SEGES           seg     es
+#define        ALIGN(x)        .align  x
+#define        SPACE(x)        .space  x
+#endif
+
+#ifdef USE_NASM
+#define        CON(x)          x
+#define        BCON(x)         byte x
+#define        LOC(x)          [x]
+#define        BLOC(x)         byte [x]
+#define        WLOC(x)         word [x]
+#define        JMP(x)          jmp short x
+#define        STRDECL(s)      db      s
+#define        SEGCS           cs
+#define        SEGES           es
+#define        ALIGN(x)        align x, db 0
+#define        SPACE(x)        times x db 0
+#endif
+
+#ifndef ASM_DEBUG
+#undef ASM_DEBUG
+#endif
+#include "first-dos.h"
+#include "version-dos.h"
+
+#ifdef USE_AS86
+       .text
+       .org    0
+#endif
+#ifdef USE_NASM
+       text
+#endif
+
+       mov     dx,ds
+       mov     ax,cs                   ; set DS
+       mov     ds,ax
+       mov     LOC(oldES),es
+       mov     LOC(oldDS),dx           ; save old register values in case
+       mov     LOC(oldBP),bp           ; we have to return to the boot rom
+       mov     LOC(oldSI),si
+       mov     LOC(oldDI),di
+       mov     bp,sp
+       mov     ax,[bp+4]
+       mov     LOC(header+0),ax                ; load the address of the boot header
+       mov     ax,[bp+6]
+       mov     LOC(header+2),ax
+       mov     ax,[bp+8]
+       mov     LOC(bootp+0),ax         ; load the address of the bootp block
+       mov     ax,[bp+10]
+       mov     LOC(bootp+2),ax
+
+; Tell the user who we are and that we started running
+
+       mov     si,CON(sigmsg)
+       call    prnstr
+
+; Check if the boot image header is correct.
+
+       les     bx,LOC(header)
+       mov     si,CON(bmerr)           ; prepare for correct error message
+       SEGES
+       mov     ax,[bx+BOOT_HD_MAGIC+0]
+       cmp     ax,LOC(bmagic+0)        ; compare boot rom magic numbers
+       jne     doerr1
+       SEGES
+       mov     ax,[bx+BOOT_HD_MAGIC+2]
+       cmp     ax,LOC(bmagic+2)
+       jne     doerr1
+
+       mov     si,CON(vmerr)           ; prepare for correct error message
+       SEGES
+       mov     al,[bx+BOOT_HD_LENGTH]
+       mov     cl,CON(4)
+       shr     al,cl
+       and     al,CON(0x0F)
+       cmp     al,CON(VENDOR_SIZE)     ; check vendor ID size
+       jne     doerr1
+       xor     di,di
+dovmag:        mov     al,[di+vmagic]          ; check vendor ID
+       or      al,al
+       jz      getrd                   ; vendor ID ok, continue
+       SEGES
+       cmp     al,[bx+di+BOOT_HD_VENDOR]
+       jne     doerr1
+       inc     di
+       JMP(dovmag)
+
+doerr1:        call    prnstr                  ; in case of error return to the
+       mov     si,LOC(oldSI)           ; boot rom with registers set
+       mov     di,LOC(oldDI)           ; correctly
+       mov     bp,LOC(oldBP)
+       mov     es,LOC(oldES)
+       mov     ds,LOC(oldDS)
+       retf
+
+; Next get the address of the ramdisk and its size.
+
+getrd: mov     si,CON(recerr)
+       mov     al,CON(VENDOR_RAMDISK)
+       call    fndldr                          ; find load record for ramdisk
+       mov     ax,es
+       or      ax,di
+       jz      doerr1
+       SEGES
+       mov     al,[di+BOOT_LD_FLAGS]           ; get load record flags
+       test    al,CON(BOOT_FLAG_B0 + BOOT_FLAG_B1)     ; check that it has a
+       jnz     doerr1                          ; correct flag
+
+       SEGES
+       mov     ax,[di+BOOT_LD_ADDR+0]          ; get base adress of ramdisk
+       SEGES
+       mov     bx,[di+BOOT_LD_ADDR+2]
+       mov     LOC(rdaddr+0),ax
+       mov     LOC(rdaddr+2),bx
+
+       SEGES
+       mov     ax,[di+BOOT_LD_MLENGTH+0]       ; get ramdisk size
+       SEGES
+       mov     bx,[di+BOOT_LD_MLENGTH+2]
+       add     ax,CON(0x03ff)                  ; round to nearest kb
+       adc     bx,BCON(0)
+       mov     cl,CON(10)
+       shr     ax,cl                           ; convert into kb
+       mov     cl,CON(6)
+       shl     bx,cl
+       or      ax,bx
+       mov     LOC(rdsize),ax
+
+; Get the disk geometry out of the vendor information block in the
+; load record
+
+       SEGES
+       mov     bl,[di+BOOT_LD_LENGTH]
+       and     bl,CON(0x0f)
+       xor     bh,bh                           ; compute pointer to
+       shl     bx,CON(1)                       ; vendor block
+       shl     bx,CON(1)
+       SEGES
+       mov     ax,[di+bx+BOOT_LD_SECNUM]       ; get number of sectors
+       mov     LOC(secnum),ax
+       SEGES
+       mov     ax,[di+bx+BOOT_LD_SPT]          ; get sectors per track
+       mov     LOC(secptk),al
+       SEGES
+       mov     ax,[di+bx+BOOT_LD_CYL]          ; get number of cylinders
+       mov     LOC(cylnum),ax
+       SEGES
+       mov     al,[di+bx+BOOT_LD_NOHD]         ; get no-hard-disk flag
+       mov     LOC(nohd),al
+       SEGES
+       mov     al,[di+bx+BOOT_LD_DRIVE]                ; get ram disk drive id
+       mov     LOC(drvid),al
+
+; Set the address of the BIOS disk status byte
+
+       mov     bx,CON(BIOS_FDSTAT)
+       cmp     al,CON(0x80)
+       jb      setsts
+       mov     bx,CON(BIOS_HDSTAT)
+setsts:        mov     LOC(statof),bx
+
+; Get system configuration from BIOS
+
+       push    ax
+       int     0x11
+       mov     LOC(syscnf),ax
+       pop     ax
+
+; Get the number of floppy or hard disk drives in the system and set
+; the DOS disk parameter block
+
+       cmp     al,BCON(0x80)
+       jae     getnm2
+
+       mov     ah,CON(0x08)
+       xor     dl,dl
+       int     0x13                    ; get the number of floppy disk
+       jc      getnm1                  ; drives from the BIOS
+       or      dl,dl
+       jnz     gotnum
+       inc     dl                      ; indicate at least one drive
+       JMP(gotnum)
+getnm1:        mov     dx,LOC(syscnf)          ; if int 13h didnt work try it with
+       test    dl,CON(0x01)            ; the mainboard dip switch config
+       jz      getnm3
+       mov     cl,CON(6)
+       shr     dl,cl
+       and     dl,CON(0x03)            ; determine number of floppy disk
+       inc     dl                      ; drives
+       JMP(gotnum)
+
+getnm2:        mov     ah,CON(0x08)
+       mov     dl,CON(0x80)
+       int     0x13                    ; get the number of hard disk
+       jc      getnm3                  ; drives from the BIOS
+       inc     dl
+       JMP(gotnum)
+; The next line was mov dl,1 in netboot-0.8.1. This was probably an error.
+getnm3:        mov     dl,CON(1)               ; we have at least one drive
+
+gotnum:        mov     LOC(drvnum),dl          ; save number of disk drives
+       call    setdpb                  ; set disk parameter block
+
+; Now get the boot sector of the ramdisk and check that its correct. If
+; we are simulating a hard disk the boot sector contains the partition
+; table, which we have to analyze. Then load the partitions boot sector.
+
+       mov     ax,CON(TEMP_SEGMENT)
+       mov     es,ax                   ; pointer to temporary buffer
+       xor     bx,bx
+       xor     al,al                   ; indicate read
+       xor     dx,dx                   ; first sector
+       call    rwsect                  ; read boot sector
+       mov     si,CON(rderr)
+       jc      doerr2
+       cmp     BLOC(drvid),CON(0x80)   ; if the ram disk is simulates a
+       jb      chkbot                  ; floppy, there is no partition table
+
+       mov     si,CON(dskerr)          ; prepare for correct error message
+       SEGES
+       cmp     BLOC(PART_STATUS),CON(PART_ACTIVE)
+       jne     doerr2
+       SEGES
+       cmp     BLOC(PART_TYPE),CON(PART_FAT16)
+       je      partok
+       SEGES
+       cmp     BLOC(PART_TYPE),CON(PART_FAT12)
+       jne     doerr2
+partok:        SEGES
+       mov     dx,[PART_ABS_SECT+0]    ; get number of first sector
+       SEGES
+       mov     ax,[PART_ABS_SECT+2]
+       or      ax,ax
+       jnz     doerr2
+       xor     al,al                   ; indicate read
+       call    rwsect                  ; read boot sector
+       mov     si,CON(rderr)
+       jc      doerr2
+#ifndef        HD_PARM_CHECK
+       JMP(dobotp)
+#endif
+
+chkbot:        mov     si,CON(dskerr)          ; prepare for correct error message
+       mov     al,LOC(drvid)
+       SEGES
+       cmp     BLOC(DISK_BOOT),al      ; check boot disk number
+       jne     doerr2
+       SEGES
+       cmp     WLOC(DISK_BPS),CON(SECT_SIZE)   ; check sector size
+       jne     doerr2
+       SEGES
+       cmp     WLOC(DISK_HEADS),BCON(16)       ; check number of heads
+       jg      doerr2
+       SEGES
+       mov     ax,LOC(DISK_SPT)        ; check number of sectors per track
+       cmp     al,LOC(secptk)
+       je      dobotp
+
+doerr2:        call    prnstr                  ; in case of error return to the
+       mov     si,LOC(oldSI)           ; boot rom with registers set
+       mov     di,LOC(oldDI)           ; correctly
+       mov     bp,LOC(oldBP)
+       mov     es,LOC(oldES)
+       mov     ds,LOC(oldDS)
+       retf
+
+; Save the BOOTP record for later retrieval by a DOS program.
+
+dobotp:        cld
+       xor     dx,dx
+       les     di,LOC(bootp)
+       mov     ax,es
+       or      ax,di
+       jz      dobot9
+       SEGES
+       mov     al,[di+BOOTP_OP]                ; op code must indicate reply
+       cmp     al,CON(BOOTP_REPLY)
+       jne     dobot9                  ; it isnt
+       add     di,CON(BOOTP_VEND)
+       mov     bx,di
+       mov     si,CON(pmagic)          ; compare vendor ID
+dobot1:        mov     di,bx
+       mov     cx,CON(BOOTP_MAGIC_LEN)
+       repe
+       cmpsb
+       jz      dobot2                  ; vendor ID is valid
+       add     si,cx
+#ifdef USE_AS86
+       cmp     byte ptr[si],CON(0)     ; check next vendor ID
+#endif
+#ifdef USE_NASM
+       cmp     byte [si],CON(0)        ; check next vendor ID
+#endif
+       jne     dobot1
+dobot9:        JMP(nobot2)                     ; vendor ID not found
+
+doerr6:        JMP(doerr2)
+
+dobot2:        sub     si,BCON(BOOTP_MAGIC_LEN)
+       sub     si,CON(pmagic)
+       mov     ax,si
+       push    ds
+       mov     bx,ds
+       mov     es,bx
+       mov     di,CON(btpnew)
+       lds     si,LOC(bootp)
+       mov     bx,si
+       mov     dx,CON(BOOTP_SIZE)
+       or      ax,ax                   ; if not RFC vendor ID the bootp
+       jnz     dobot7                  ; record has fixed length
+
+       xor     cx,cx
+       add     si,CON(BOOTP_VEND + BOOTP_MAGIC_LEN)
+dobot3:        lodsb
+       cmp     al,CON(BOOTP_RFC_NOP)   ; handle NOP tag
+       jnz     dobot4
+       inc     cx
+       cmp     cx,BCON(16)             ; more than 16 NOP tags is VERY unusual
+       jae     dobot7                  ; so the bootp record maybe broken
+       JMP(dobot3)                     ; loop to next tag
+
+nobot2:        JMP(nobotp)
+
+dobot4:        cmp     al,CON(BOOTP_RFC_END)   ; handle END tag
+       jnz     dobot6
+       mov     dx,si
+       sub     dx,bx                   ; compute length of bootp record
+       cmp     dx,CON(BOOTP_SIZE)
+       jae     dobot7
+       mov     dx,CON(BOOTP_SIZE)      ; use minimum size
+       JMP(dobot7)
+dobot6:        lodsb                           ; handle all other tags
+       mov     cl,al
+       xor     ch,ch
+       add     si,cx                   ; jump to next tag
+       xor     cx,cx                   ; reset NOP counter
+       JMP(dobot3)                     ; proceed with next tag
+
+dobot7:        mov     si,CON(btperr)
+       mov     ax,CON(btpnew)          ; bootp record cannot be larger
+       add     ax,dx                   ; than the current segment
+       jc      doerr6
+       mov     cx,dx
+       mov     si,bx                   ; restore source pointer
+       rep
+       movsb                           ; save the bootp record
+       pop     ds
+nobotp:        mov     LOC(btplen),dx          ; set length of bootp record
+
+; Everything is right, so we can now move the resident section to the
+; end of the conventional memory, thus overwriting the bootrom data
+; area. Therefore there is no chance of returning to the bootrom from
+; now on.
+; Note that the resident section doesnt start at offset 0, so we have
+; to set the segment address to somewhere lower.
+
+#ifdef ASM_DEBUG
+       mov     si,CON(debug1)
+       call    prnstr
+#endif
+       cli
+       mov     ax,CON(TEMP_SEGMENT)    ; set new stack
+       mov     ss,ax
+       mov     sp,CON(0xFFF0)
+
+       cld     
+       int     0x12                    ; get memory size in kB
+#ifdef FREEDOS
+       push    ax                      ; save mem size in kB
+#endif /* FREEDOS */
+       mov     cl,CON(6)
+       shl     ax,cl                   ; compute last usable segment
+       mov     bx,CON(btpnew)
+       add     bx,LOC(btplen)
+       mov     dx,bx
+       mov     cl,CON(4)
+       shr     bx,cl                   ; compute size of code segment in
+       inc     bx                      ; paragraphs
+#ifdef FREEDOS
+       push    bx                      ; save size in paragraphs
+#endif /* FREEDOS */
+       sub     ax,bx                   ; compute new code segment
+       mov     LOC(resseg),ax
+       mov     es,ax                   ; set source and destination ptrs
+       mov     si,CON(start_resident)
+       mov     di,si
+       mov     cx,dx
+       sub     cx,si                   ; compute size of resident area
+       rep
+       movsb                           ; move it
+
+#ifdef FREEDOS
+; New code for FreeDOS, adjust the value of the top of memory returned by
+; int 0x12. Currently there is no code to restore the original size
+       pop     bx                      ; restore size in paragraphs
+       add     bx,BCON(63)             ; round up to next kB
+       mov     cl,CON(6)               ; divide by 64
+       shr     bx,cl
+       pop     ax                      ; restore size in kB
+       sub     ax,bx
+       mov     bx,CON(0x40)
+       mov     es,bx
+       SEGES
+       mov     LOC(0x13),ax            ; store at 0x40:0x13 for int 12h
+; End of new code
+#endif /* FREEDOS */
+
+; Setup all interrupt vectors
+
+       mov     bx,LOC(resseg)
+       push    ds
+       mov     ds,bx
+       xor     ax,ax
+       mov     es,ax
+       SEGES
+       mov     ax,LOC(I13_INT+0)
+       mov     LOC(old13h+0),ax
+       SEGES
+       mov     ax,LOC(I13_INT+2)
+       mov     LOC(old13h+2),ax
+       SEGES
+       mov     ax,LOC(I2F_INT+0)
+       mov     LOC(old2Fh+0),ax
+       SEGES
+       mov     ax,LOC(I2F_INT+2)
+       mov     LOC(old2Fh+2),ax
+       SEGES
+       mov     ax,LOC(IF1_INT+0)
+       mov     LOC(oldF1h+0),ax
+       SEGES
+       mov     ax,LOC(IF1_INT+2)
+       mov     LOC(oldF1h+2),ax
+       SEGES
+       mov     ax,LOC(IF8_INT+0)
+       mov     LOC(oldF8h+0),ax
+       SEGES
+       mov     ax,LOC(IF8_INT+2)
+       mov     LOC(oldF8h+2),ax
+       pop     ds
+
+       SEGES
+       mov     LOC(I13_INT+2),bx       ; interrupt vector 13h
+       SEGES
+       mov     WLOC(I13_INT+0),CON(int13)
+       SEGES
+       mov     LOC(I2F_INT+2),bx       ; interrupt vector 2Fh
+       SEGES
+       mov     WLOC(I2F_INT+0),CON(int2F)
+       SEGES
+       mov     LOC(IF8_INT+2),bx       ; interrupt vector F8h
+       SEGES
+       mov     WLOC(IF8_INT+0),CON(intF8)
+       mov     di,CON(IF1_INT)
+       mov     si,CON(if1sig)          ; interrupt vector F1h
+       mov     cx,CON(4)               ; contains the string "NetB"
+       rep                             ; to provide as an installation
+       movsb                           ; check
+       sti
+
+; Output some debugging messages by simply calling the interrupt vectors
+; which we just created.
+
+#ifdef DRV_DEBUG
+       mov     ax,CON(0x4A06)
+       int     0x2F
+       mov     si,CON(consz)
+       call    prnstr                  ; print out amount of conventional
+       mov     ax,dx                   ; memory
+       mov     cl,CON(12)
+       shr     ax,cl
+       call    prnwrd
+       mov     ax,dx
+       mov     cl,CON(4)
+       shl     ax,cl
+       call    prnwrd
+       mov     si,CON(bytes)
+       call    prnstr
+
+       mov     ax,CON(0x8800)
+       int     0x15
+       push    ax
+       mov     si,CON(extsz)
+       call    prnstr                  ; print out amount of extended memory
+       mov     cl,CON(6)
+       shr     ax,cl
+       call    prnwrd
+       pop     ax
+       mov     cl,CON(10)
+       shl     ax,cl
+       call    prnwrd
+       mov     si,CON(bytes)
+       call    prnstr
+#endif
+
+#ifndef        FREEDOS
+; The boot sector had to be placed into a temporary memory area to
+; avoid overwriting any bootrom structures. However, a call back
+; to the bootrom is no longer possible, so we can move the bootblock
+; where it belongs.
+
+       push    ds
+       mov     ax,CON(TEMP_SEGMENT)
+       mov     ds,ax
+       xor     ax,ax
+       mov     es,ax
+       xor     si,si
+       mov     di,CON(BOOT_OFFSET)
+       mov     cx,CON(SECT_SIZE)
+       rep
+       movsb                           ; move it
+       pop     ds
+
+; Finally call the boot sector
+#else
+; Finally call kernel.sys
+#endif /* FREEDOS */
+
+#ifdef ASM_DEBUG
+       mov     si,CON(debug2)
+       call    prnstr
+#endif
+#ifdef FREEDOS
+       mov     bl,LOC(drvid)           ; FreeDOS gets boot drive from bl, 0=A
+#else
+       mov     dl,LOC(drvid)           ; boot block may expect this
+#endif
+#ifdef ASM_FREEZE_AFTER_INIT
+lop:   JMP(lop)
+#else
+#ifdef FREEDOS
+       jmp     FDKSEG:0                ; FreeDOS kernel.sys entry point
+#else
+       jmp     0:BOOT_OFFSET
+#endif /* FREEDOS */
+#endif
+
+
+
+;====================================================================
+;
+; Setup DOS drive parameter block (DPB) for floppy disk drive. The
+; DPB is required to activate the floppy disk drive after the ramdisk
+; has been turned off.
+; Input:  none
+; Output: none
+; Registers changed: AX, BX, CX, DX
+
+setdpb:        push    es
+       push    si
+       push    di
+       mov     dl,LOC(drvid)
+       cmp     dl,CON(0x80)            ; can only restore floppy drives
+       jae     setdp8
+
+; First get the drive parameters from the BIOS
+
+       mov     LOC(dpb_phys),dl        ; set physical drive ID
+       mov     ah,CON(0x08)            ; get drive parameters from BIOS
+       int     0x13
+       jc      setdp8
+       xor     ah,ah
+       mov     al,ch                   ; set max number of cylinders
+       inc     ax
+       mov     LOC(dpb_cyls),ax
+       mov     si,CON(dpb_bpb_cur)
+       mov     al,cl                   ; set max number of sectors per track
+       mov     [si+BPB_SPT],ax
+       mov     al,dh
+       inc     ax                      ; set max number of heads
+       mov     [si+BPB_HEADS],ax
+
+; Determine DOS disk parameters by drive type
+
+       cmp     bl,CON(5)
+       jb      setdp1                  ; check for invalid drive type
+       mov     bl,CON(4)
+setdp1:        xor     bh,bh
+       dec     bx
+       push    bx
+       shl     bx,CON(1)
+       shl     bx,CON(1)               ; compute address into drive para-
+       add     bx,CON(drvtab)          ; meter table
+       xor     ah,ah
+       mov     al,[bx+0]               ; get # of entries in root dir
+       mov     [si+BPB_DIR],al
+       mov     al,[bx+1]               ; get # of sectors per FAT
+       mov     [si+BPB_SPF],ax
+       mov     al,[bx+2]               ; get # of sectors per cluster
+       mov     [si+BPB_SPC],al
+       mov     al,[bx+3]               ; get media ID
+       mov     [si+BPB_MEDIA_ID],al
+       pop     bx
+       mov     al,[bx+typtab]          ; get drive type
+       mov     LOC(dpb_type),al
+
+; Determine number of bytes per sector
+
+       SEGES
+       mov     cl,[di+3]               ; get shift value from BIOS media
+       mov     ax,CON(128)             ; parameter table
+       shl     ax,cl                   ; shift base value
+       mov     [si+BPB_BPS],ax
+       JMP(setdp4)
+setdp8:        JMP(setdp9)                     ; needed for short jumps
+
+; Determine total number of sectors
+
+setdp4:        mov     ax,[si+BPB_SPT]
+       mul     WLOC(dpb_cyls)
+       or      dx,dx                   ; should not overflow
+       jnz     setdp8
+#ifdef USE_AS86
+       cmp     [si+BPB_HEADS],BCON(2)
+#endif
+#ifdef USE_NASM
+       cmp     word [si+BPB_HEADS],BCON(2)
+#endif
+       jb      setdp3
+       shl     ax,CON(1)
+       jc      setdp8
+setdp3:        mov     [si+BPB_TOT_SECTS],ax
+
+; Determine if the drive can detect disk changes
+
+       mov     ah,CON(0x15)
+       mov     dl,LOC(drvid)
+       int     0x13                    ; get DASD type from BIOS
+       mov     bl,ah
+       mov     ax,CON(DPB_F_DEFAULT)
+       jc      setdp2
+       cmp     bl,CON(0x02)            ; check if drive detects disk changes
+       jne     setdp2
+       or      ax,CON(DPB_F_DOOR)
+setdp2:        mov     LOC(dpb_flags),ax       ; set flags
+
+; Thats it
+
+       inc     BLOC(dpb_valid)         ; increment valid flag
+setdp9:        pop     di
+       pop     si
+       pop     es
+       ret
+
+
+
+;====================================================================
+;
+; Find a load record in the boot header. The ID number of the load
+; record is in AL, and ES:DI points to requested load record, or is
+; the NULL pointer if load record not found.
+;
+; Changed registers: AX, DI, ES
+
+fndldr:        push    cx
+       mov     ch,al
+       les     di,LOC(header)          ; examine boot image header
+       SEGES
+       mov     al,[di+BOOT_HD_LENGTH]  ; get length of image header
+       call    getlen
+       add     di,ax                   ; get the pointer to first load record
+fndl1: SEGES
+       cmp     ch,[di+BOOT_LD_TAG1]    ; is it the desired one ?
+       je      fndl3
+       SEGES
+       mov     al,[di+BOOT_LD_FLAGS]   ; no, so check if its the last record
+       test    al,CON(BOOT_FLAG_EOF)
+       jnz     fndl2
+       SEGES
+       mov     al,[di+BOOT_LD_LENGTH]  ; no, get the address of the next one
+       call    getlen
+       add     di,ax
+       JMP(fndl1)
+
+fndl2: xor     ax,ax                   ; couldnt find the desired record
+       mov     es,ax
+       mov     di,ax
+fndl3: pop     cx
+       ret
+
+
+
+;====================================================================
+;
+; Compute the length of a load record address from a length byte
+; in AL. Return the offset in AX.
+;
+; Changed registers: AX
+
+getlen:        push    cx
+       mov     ah,al
+       mov     cl,CON(4)
+       shr     ah,cl
+       and     ax,CON(0x0f0f)          ; compute the total length in
+       add     al,ah                   ; bytes from the length of the
+       xor     ah,ah                   ; record and that of the vendor
+       shl     ax,1                    ; information.
+       shl     ax,1
+       pop     cx
+       ret
+
+
+
+;====================================================================
+; Print a string in DS:SI onto the console
+;
+; Changed registers: AL
+
+prnstr:        push    si
+       cld
+prns1: lodsb                           ; loop over all characters of
+       or      al,al                   ; string
+       jz      prns2
+       push    bx
+       mov     ah,CON(0x0E)            ; print it
+       mov     bl,CON(0x07)
+       xor     bh,bh
+       int     0x10
+       pop     bx
+       JMP(prns1)
+prns2: pop     si
+       ret
+
+
+
+#ifdef DRV_DEBUG
+;====================================================================
+;
+; Print hexadecimal values (in AX or AL) or characters onto the console
+;
+; Changed registers: AX
+
+prnwrd:        push    ax
+       mov     al,ah
+       call    prnbyt                  ; print the upper byte
+       pop     ax
+prnbyt:        push    ax
+       shr     al,1                    ; prepare upper nibble
+       shr     al,1
+       shr     al,1
+       shr     al,1
+       call    prnnib                  ; print it
+       pop     ax
+prnnib:        and     al,CON(0x0F)            ; prepare lower nibble
+       add     al,CON(0x30)
+       cmp     al,CON(0x39)            ; convert it into hex
+       jle     prnchr
+       add     al,CON(7)
+prnchr:        push    bx
+       mov     ah,CON(0x0E)            ; print it
+       mov     bl,CON(0x07)
+       xor     bh,bh
+       int     0x10
+       pop     bx
+       ret
+#endif
+
+
+
+;====================================================================
+;
+; String and constants definitions
+
+
+; Startup signature
+
+sigmsg:        db      0x0D, 0x0A
+       STRDECL('DOS Net Boot Image Loader ')
+       STRDECL(VERSION)
+       db      0x0D, 0x0A
+       STRDECL(COPYRIGHT)
+       db      0x0D, 0x0A
+crlf:  db      0x0D, 0x0A
+       db      0
+
+
+; Magic numbers for boot record and bootp entry
+
+bmagic:        dd      BOOT_MAGIC              ; boot image magic number
+vmagic:        STRDECL(VENDOR_MAGIC)           ; vendor magic ID
+       db      0                       ; end of vendor magic ID
+pmagic:        db      BOOTP_MAGIC_RFC         ; bootp magic ID for RFC 1048
+       db      BOOTP_MAGIC_CMU         ; bootp magic ID for CMU
+       db      BOOTP_MAGIC_STA         ; bootp magic ID for Stanford
+       db      0
+
+
+; Specifications for different types of disk drives. The order is:
+; # dir entries, # sects per FAT, # sects per cluster, media ID
+
+drvtab:        db      112, 2, 2, 0xfd         ; 360kB disk
+       db      224, 7, 1, 0xf9         ; 1.2MB disk
+       db      112, 3, 2, 0xf9         ; 720kB disk
+       db      224, 9, 1, 0xf0         ; 1.44 MB disk
+
+typtab:        db      DPB_T_360               ; type values for drive parameter block
+       db      DPB_T_1200
+       db      DPB_T_720
+       db      DPB_T_1440
+
+
+; Error messages
+
+recerr:        STRDECL('Error in load record data')
+       db      0x0D, 0x0A
+       db      0
+
+bmerr: STRDECL('Invalid boot header magic number')
+       db      0x0D, 0x0A
+       db      0
+
+vmerr: STRDECL('Invalid vendor magic ID')
+       db      0x0D, 0x0A
+       db      0
+
+rderr: STRDECL('Error while accessing ramdisk')
+       db      0x0D, 0x0A
+       db      0
+
+dskerr:        STRDECL('Wrong ramdisk image')
+       db      0x0D, 0x0A
+       db      0
+
+btperr:        STRDECL('BOOTP record invalid')
+       db      0x0D, 0x0A
+       db      0
+
+
+; Debug messages
+
+#ifdef ASM_DEBUG
+debug1:        STRDECL('Making driver resident')
+       db      0x0D, 0x0A
+       db      0
+
+debug2:
+#ifdef ASM_FREEZE_AFTER_INIT
+       STRDECL('Freezing;')
+#else
+       STRDECL('Calling boot block')
+#endif
+       db      0x0D, 0x0A
+       db      0
+#endif
+
+#ifdef DRV_DEBUG
+consz: STRDECL('RAMDISK: reporting conventional memory size: ')
+       db      0
+extsz: STRDECL('RAMDISK: reporting extended memory size: ')
+       db      0
+bytes: STRDECL(' bytes')
+       db      0x0D,0x0A
+       db      0
+#endif
+
+
+
+;====================================================================
+;
+; Variable definitions
+
+header:        dd      0                       ; pointer to boot header from boot rom
+bootp: dd      0                       ; pointer to bootp block from boot rom
+
+resseg:        dw      0                       ; segment of resident section
+
+oldDS: dw      0                       ; old DS from boot rom
+oldES: dw      0                       ; old ES from boot rom
+oldBP: dw      0                       ; old BP from boot rom
+oldSI: dw      0                       ; old SI from boot rom
+oldDI: dw      0                       ; old DI from boot rom
+
+
+
+;====================================================================
+;
+; Start of resident section. This will be placed at the end of the
+; low 640kB RAM area.
+;
+;====================================================================
+;
+
+       ALIGN(16)                       ; has to be paragraph aligned
+
+start_resident:                                ; indicate start of resident section
+
+
+;====================================================================
+;
+; New interrupt 2Fh routine. This routine gets called by IO.SYS
+; in order to determine the maximum amount of memory usable to
+; DOS. This only works with DOS versions 5.0 and higher. The DOS
+; function which gets installed into the interrupt 2Fh vector
+; does not call the old vector (i.e. it does not daisy-chain),
+; and therefore we can redirect 2Fh without a problem even when
+; considering to remove the ram disk lateron.
+;
+; NOTE THAT THIS INTERRUPT HAS TO BE THE FIRST ROUTINE IN THE
+; RESIDENT SECTION!
+;
+; Input:  AX     -  Magic ID
+;         DX     -  segment following last usable byte
+; Output: DX     -  new segment  following last usable byte
+; Registers changed: DX
+
+int2F: JMP(int2F1)                     ; this has to be a relative jump
+       nop
+
+       STRDECL('RPL')                  ; magic ID string for DOS
+
+int2F1:        cmp     ax,CON(0x4A06)          ; check for magic ID
+       jne     int2F9
+       push    cx
+       mov     dx,CON(start_resident)  ; determine last usable segment
+       mov     cl,CON(4)               ; from segment and offset of
+       shr     dx,cl                   ; the resident section
+       pop     cx
+       push    ax
+       push    cs
+       pop     ax
+       add     dx,ax                   ; add offset to segment
+       dec     dx
+       pop     ax
+       iret
+
+int2F9:        SEGCS
+       jmp     far [old2Fh]            ; jump to old interrupt routine
+
+
+
+;====================================================================
+;
+; New interrupt F8h routine. It can be used to retrieve several
+; values from the resident driver.
+; Input:  AX  -  function code
+; Output: depends on function:
+;
+; Installation check (AX = 0x9C00)
+;         AX     -  contains 0x009C
+;
+; Return ramdisk size and address (AX = 0x9C01)
+;         BX:DX  -  address of ramdisk
+;         CX     -  size of ramdisk in kb
+;
+; Return size and address of BOOTP block (AX = 0x9C02)
+;         BX:DX  -  address of BOOTP block
+;         CX     -  size of BOOTP block
+;
+; Return miscellaneous values for handling the ramdisk (AX = 0x9C03)
+;         AX     -  XMS handle for ram disk
+;         BX:DX  -  address of old interrupt vector table
+;         CL     -  ramdisk id
+;
+; Remove ramdisk (AX = 0x9C04)
+;         AL     -  non-zero if error
+;
+; Registers changed: depends on function
+
+intF8: cmp     ah,CON(0x9C)            ; check for magic ID
+       jne     intF89
+       cmp     al,CON(01)              ; check for function number
+       jne     intF81
+       SEGCS
+       mov     bx,LOC(rdaddr+2)        ; return ramdisk address
+       SEGCS
+       mov     dx,LOC(rdaddr+0)
+       SEGCS
+       mov     cx,LOC(rdsize)          ; return ramdisk size
+       iret
+
+intF81:        cmp     al,CON(0x02)
+       jne     intF82
+       mov     bx,cs                   ; return address of BOOTP record
+       mov     dx,CON(btpnew)
+       SEGCS
+       mov     cx,LOC(btplen)          ; return BOOTP length
+       iret
+
+intF82:        cmp     al,CON(0x03)
+       jne     intF83
+       mov     bx,cs                   ; return address of old interrupt
+       mov     dx,CON(oldints)         ; vector table
+       SEGCS
+       mov     cl,LOC(drvid)           ; return drive id
+       SEGCS
+       mov     ax,LOC(xmshdl)          ; return XMS handle
+       iret
+
+intF83:        cmp     al,CON(0x04)
+       jne     intF88
+       call    rmrd                    ; remove ramdisk
+       iret
+
+intF88:        or      al,al
+       jnz     intF89
+       xchg    al,ah                   ; return installation check code
+intF89:        iret
+
+
+
+;====================================================================
+;
+; New interrupt 13h routine to handle disk accesses. It is different
+; for simulating either a floppy drive or a hard disk. DOS provides
+; a way for restoring this interrupt to its original value when we
+; want to remove the ramdisk lateron.
+; Input:  AH  -  function code
+;         DL  -  driver number
+; Output: carry flag set if error
+; Registers changed: depends on function
+
+int13: sti                             ; we dont need interrupts disabled
+       push    ax
+       SEGCS
+       mov     ax,LOC(xmsadr+0)
+       SEGCS
+       or      ax,LOC(xmsadr+2)        ; check if XMS already initialized
+       jnz     int13s
+       mov     ax,CON(0x4300)          ; check if XMS available
+       int     0x2f
+       cmp     al,CON(0x80)            ; XMS not available
+       jne     int13s
+       push    bx
+       push    es
+       mov     ax,CON(0x4310)          ; get XMS driver address
+       int     0x2f
+       SEGCS
+       mov     LOC(xmsadr+0),bx        ; save driver address
+       SEGCS
+       mov     LOC(xmsadr+2),es
+       pop     es
+       call    inixms                  ; initialize XMS
+       pop     bx
+int13s:        pop     ax
+
+       SEGCS
+       cmp     dl,LOC(drvid)           ; check if its for us
+       je      int132
+       SEGCS
+       cmp     BLOC(drvid),CON(0x80)   ; check if we are to simulate a hard
+       jae     int13h                  ; disk drive
+
+
+; First comes the floppy drive redirector
+
+       cmp     dl,CON(0x80)            ; check if its for a hard disk
+       jb      int133
+       SEGCS
+       test    BLOC(nohd),CON(0xff)    ; check if hard disk accesses allowed
+       jz      int131
+       cmp     ah,CON(0x08)            ; function 0x08 should not return error
+       mov     ah,CON(0x80)            ; return with error
+       jne     int135
+       xor     dl,dl                   ; indicate no hard disk present
+       JMP(int13f)                     ; return without error
+
+; Handle function 0x08 for disk drives other than the ramdisk.
+
+int133:        cmp     ah,CON(0x08)
+       jne     int131
+       pushf
+       SEGCS                           ; function 0x08 has to return the
+       call    far [old13h]            ; correct number of disk drives
+       SEGCS
+       mov     dl,LOC(drvnum)
+int13f:        xor     ah,ah                   ; never return an error
+       JMP(int136)
+
+; Jump directly to the BIOS
+
+int131:        SEGCS
+       jmp     far [old13h]            ; call the old interrupt routine
+
+; Now handle all ramdisk functions. First check if the function number
+; is correct.
+
+int132:        cmp     ah,CON(0x18)
+       jbe     int134
+       mov     ah,CON(0x01)            ; unknown command
+int135:        stc
+int136:        push    ds
+       JMP(int13e)
+
+; Determine the handlers address according to the function number in AH
+; and jump to it.
+
+int134:        push    ds
+       push    cs
+       pop     ds                      ; set data segment
+       push    bx
+       mov     bl,ah
+       xor     bh,bh
+       shl     bx,CON(1)               ; compute pointer into routine table
+       jmp     [bx+fntab]
+
+
+; Now comes the hard disk drive redirector
+
+int13h:        cmp     dl,CON(0x80)            ; check if its for a floppy drive
+       jb      int131
+
+; Handle function 0x08 for hard disk drives other than the ramdisk.
+
+       cmp     ah,CON(0x08)
+       jne     int137
+       dec     dl
+       pushf
+       SEGCS                           ; function 0x08 has to return the
+       call    far [old13h]            ; correct number of disk drives
+       SEGCS
+       mov     dl,LOC(drvnum)
+       JMP(int13f)                     ; always return without error
+
+; Handle function 0x15 for disk drives other than the ramdisk. This is
+; the only function besides 0x08 which returns a value in DX and therefore
+; has to have special handling.
+
+int137:        push    dx
+       dec     dl
+       cmp     ah,CON(0x15)
+       jne     int138
+       pushf
+       SEGCS
+       call    far [old13h]            ; call the BIOS for handling
+       jc      int139
+       cmp     ah,CON(0x03)            ; DX is only used if AH = 0x03
+       jne     int139
+       add     sp,BCON(0x0002)         ; remove DX from stack if the BIOS
+       JMP(int136)                     ; returned a value in it
+
+; Handle all other functions for drives other than the ramdisk. This will
+; just call the original BIOS handler.
+
+int138:        pushf
+       SEGCS
+       call    far [old13h]            ; simply call the old int 13h routine
+int139:        pop     dx
+       JMP(int136)
+
+
+; Save the return status into the BIOS data area and return to the caller
+; while preserving the carry flag.
+
+int13e:        push    es                      ; return from function handler
+       push    bx                      ; this code is not allowed to change
+       call    getsts                  ; any register or flag
+       SEGES
+       mov     [bx],ah                 ; set disk operation status
+       pop     bx
+       pop     es
+intend:        pop     ds
+       push    ax                      ; general exit point for interrupts
+       pushf
+       pop     ax
+       push    bp
+       mov     bp,sp
+       mov     [bp+8],al               ; put the flags onto the stack
+       pop     bp
+       pop     ax
+       iret
+
+
+; Function table
+
+fntab: dw      f1300                   ; function 00: reset disk system
+       dw      f1301                   ; function 01: return last error
+       dw      f1302                   ; function 02: read disk
+       dw      f1303                   ; function 03: write disk
+       dw      f1304                   ; function 04: verify disk
+       dw      f1305                   ; function 05: format disk
+       dw      f1306                   ; function 06: format track
+       dw      f1307                   ; function 07: format disk
+       dw      f1308                   ; function 08: get drive parameters
+       dw      f1309                   ; function 09: intialize controller
+       dw      f130A                   ; function 0A: read long sectors
+       dw      f130B                   ; function 0B: write long sectors
+       dw      f130C                   ; function 0C: seek for cylinder
+       dw      f130D                   ; function 0D: disk reset
+       dw      f130E                   ; function 0E: read sector buffer
+       dw      f130F                   ; function 0F: write sector buffer
+       dw      f1310                   ; function 10: check if drive ready
+       dw      f1311                   ; function 11: recalibrate drive
+       dw      f1312                   ; function 12: controller ram diagnostic
+       dw      f1313                   ; function 13: drive diagnostic
+       dw      f1314                   ; function 14: controller int diagnostic
+       dw      f1315                   ; function 15: get disk type
+       dw      f1316                   ; function 16: detect disk change
+       dw      f1317                   ; function 17: set media type for format
+       dw      f1318                   ; function 18: set media type for format
+
+f13end:        JMP(int13e)
+
+
+
+;====================================================================
+;
+; Function 00 - reset disk system
+;
+
+f1300: test    WLOC(syscnf),CON(0x0001)        ; check if we have physical floppy
+       jz      f13001                  ; drives at all
+       push    dx
+       xor     dl,dl                   ; always reset the floppy system
+       pushf
+       call    far [old13h]            ; call old disk interrupt
+       pop     dx
+       jc      f13002
+f13001:        xor     ah,ah                   ; no error
+f13002:        pop     bx
+       JMP(f13end)
+
+
+
+;====================================================================
+;
+; Function 01 - return last error status
+;
+
+f1301: push    es
+       call    getsts                  ; get offset to status byte
+       SEGES
+       mov     ah,[bx]                 ; get disk operation status
+       pop     es
+       pop     bx
+       clc
+       JMP(intend)
+
+
+
+;====================================================================
+;
+; Function 02/03 - read/write from disk
+
+f1302:
+f1303: pop     bx                      ; get old BX from stack
+       push    dx
+       push    ax
+       call    cvtsec                  ; get linear sector number
+       pop     ax
+       jnc     f13021
+       mov     ah,CON(0x04)            ; error: sector not found
+f13028:        pop     dx
+       stc
+       JMP(f13end)                     ; terminate
+
+f13021:        push    cx
+       push    bx
+       push    ax
+       mov     cl,al                   ; move number of sectors into CX
+       xor     ch,ch
+f13022:        jcxz    f13026
+       cmp     dx,LOC(secnum)          ; check if sector is still correct
+       jb      f13023
+       pop     ax
+       mov     ah,CON(0x04)            ; error: sector not found
+f13027:        sub     al,cl                   ; compute number of sectors processed
+       pop     bx
+       pop     cx
+       JMP(f13028)
+
+f13023:        pop     ax
+       push    ax
+       xor     al,al
+       cmp     ah,CON(0x02)            ; check if read or write sector
+       je      f13024
+       inc     al
+f13024:        call    rwsect                  ; actually handle request
+       jnc     f13025
+       pop     ax
+       mov     ah,CON(0x20)            ; error: disk controller error
+       JMP(f13027)
+
+f13025:        inc     dx
+       dec     cx                      ; proceed with next sector
+       add     bx,CON(SECT_SIZE)
+       JMP(f13022)
+
+f13026:        pop     ax
+       pop     bx
+       pop     cx
+       pop     dx
+       xor     ah,ah                   ; no error
+f13e1: JMP(f13end)
+
+
+
+;====================================================================
+;
+; Function 08  -  get disk drive parameters
+
+f1308: pop     bx                      ; get old BX from stack
+       mov     dl,LOC(drvnum)          ; get number of disk drives
+       mov     cl,LOC(secptk)          ; get sectors per track
+       mov     ch,LOC(cylnum)          ; number of cylinders
+       mov     dh,CON(1)               ; number of heads - 1
+       xor     bx,bx                   ; ramdisk drive type
+       xor     ax,ax
+       dec     ch
+       JMP(f13e1)
+
+
+
+;====================================================================
+;
+; Function 04, 05, 06, 07, 09, 0D, 10, 11, 12, 13, 14, 16  -  no operation
+
+f1304:
+f1305:
+f1306:
+f1307:
+f1309:
+f130D:
+f1310:
+f1311:
+f1312:
+f1313:
+f1314:
+f1316: pop     bx                      ; get old BX from stack
+       xor     ah,ah                   ; no error
+       JMP(f13e1)
+
+
+
+;====================================================================
+;
+; Function 0A, 0B, 0E, 0F, 17, 18  -  not implemented
+
+f130A:
+f130B:
+f130E:
+f130F:
+f1317:
+f1318: pop     bx                      ; get old BX from stack
+       mov     ah,CON(0x01)            ; invalid opcode
+       stc
+       JMP(f13e1)
+
+
+
+;====================================================================
+;
+; Function 0C  -  seek for cylinder
+
+f130C: pop     bx                      ; get old BX from stack
+       push    dx
+       push    ax
+       call    cvtsec                  ; get linear sector number
+       pop     ax
+       mov     ah,CON(0x00)            ; no error
+       jnc     f130C1
+       mov     ah,CON(0x04)            ; error: sector not found
+f130C1:        pop     dx
+       JMP(f13e1)                      ; terminate
+
+
+
+;====================================================================
+;
+; Function 15  -  get disk type
+
+f1315: pop     bx                      ; get old BX from stack
+       mov     ah,CON(0x02)            ; indicate floppy disk
+       cmp     dl,CON(0x80)            ; check if floppy disk type requested
+       jb      f13159
+       inc     ah                      ; indicate hard disk
+       mov     dx,LOC(secnum)          ; get number of sectors on disk
+       xor     cx,cx
+f13159:        clc
+       JMP(f13e1)
+
+
+
+;====================================================================
+;
+; Convert Cyl/Sec/Head notation into a linear sector number
+; Input:  CH  -  cylinder number
+;         CL  -  sector number
+;         DH  -  head number
+; Output: DX  -  linear sector number
+;         carry flag set if invalid sector number
+; Registers changed: AX,DX
+
+cvtsec:        push    cx
+       cmp     dh,CON(2)               ; check if head number is correct
+       jae     cvts8                   ; maximum number of heads is always 2
+       mov     dl,dh
+       xor     dh,dh                   ; move current head number into DX
+       mov     al,ch
+       mov     ch,cl
+       mov     ah,cl                   ; compute track number into AX, the
+       mov     cl,CON(6)               ; upper two bits of CL are the high
+       shr     ah,cl                   ; bits of the 10 bit cylinder number
+       shl     ax,CON(1)               ; compute track number from cylinders
+       add     ax,dx
+       mov     dl,LOC(secptk)
+       xor     dh,dh
+       mul     dx                      ; compute number of track starting
+       or      dx,dx                   ; sector
+       jnz     cvts8                   ; should not be more than 65535 sectors
+
+       mov     dl,ch
+       and     dl,CON(0x3F)            ; move sector number into AX
+       cmp     dl,LOC(secptk)          ; check if sector number is correct
+       ja      cvts8
+       xor     dh,dh
+       dec     dx                      ; sector numbers start with 1
+       js      cvts8                   ; therefore sector 0 does not exist
+       add     dx,ax                   ; compute final sector number
+       jc      cvts8                   ; should never overflow
+       cmp     dx,LOC(secnum)          ; check if the sector is valid
+       jae     cvts8
+       clc                             ; no error
+       JMP(cvts9)
+cvts8: stc                             ; return with error
+cvts9: pop     cx
+       ret
+
+
+
+;====================================================================
+;
+; Read/write a sector from the ram disk. This routine requires a
+; sector to be 512 bytes long.
+; Input:  AL     -  non-zero if write to ram disk
+;         DX     -  logical sector number
+;         ES:BX  -  pointer to destination buffer
+; Output: carry flag set if error
+; Registers changed: AX
+
+rwsect:        push    cx
+       push    dx
+       mov     ch,al                   ; save direction indicator
+       mov     dx,es
+       mov     ax,dx
+       mov     cl,CON(12)
+       shr     ax,cl
+       mov     cl,CON(4)
+       shl     dx,cl                   ; compute linear buffer address
+       add     dx,bx
+       adc     ax,CON(0)
+       or      ch,ch                   ; check direction of transfer
+       jz      rwsec1
+       mov     LOC(rd_srcb+0),dx       ; set source address for write
+       mov     LOC(rd_srcb+2),al
+       JMP(rwsec2)
+rwsec1:        mov     LOC(rd_dstb+0),dx       ; set destination address for read
+       mov     LOC(rd_dstb+2),al
+rwsec2:        pop     dx
+
+       push    dx
+       mov     ax,dx
+       mov     cl,CON(9)
+       shl     dx,cl                   ; compute linear ramdisk address
+       mov     cl,CON(7)               ; from sector number
+       shr     ax,cl
+       add     dx,LOC(rdaddr+0)
+       adc     ax,LOC(rdaddr+2)
+       or      ch,ch                   ; check direction of transfer
+       jz      rwsec3
+       mov     LOC(rd_dstb+0),dx       ; set destination address for write
+       mov     LOC(rd_dstb+2),al
+       JMP(rwsec4)
+rwsec3:        mov     LOC(rd_srcb+0),dx       ; set source address for read
+       mov     LOC(rd_srcb+2),al
+rwsec4:        push    es
+       push    si
+       mov     ax,cs
+       mov     es,ax
+       mov     si,CON(rd_gdt)
+       mov     cx,CON(SECT_SIZE/2)     ; copy 512 bytes, e.g. 256 words
+       mov     ax,CON(0x8700)          ; let the BIOS move the sector
+       int     0x15
+       pop     si
+       pop     es
+       pop     dx
+       pop     cx
+       ret
+
+
+
+;====================================================================
+;
+; Return a pointer to the disk drive status byte. This routine should
+; not change any flags!
+; Input:  none
+; Output: ES:BX  -  pointer to disk drive status byte
+; Registers changed: BX, ES
+
+getsts:        mov     bx,CON(BIOS_SEG)        ; status byte is in BIOS data
+       mov     es,bx                   ; segment
+       SEGCS
+       mov     bx,LOC(statof)
+       ret
+
+
+
+;====================================================================
+;
+; Initialize the XMS interface. This is necessary to prevent the
+; ram disk from getting overwritten. The way this works is to
+; first allocate all of the available XMS, then resize the memory
+; block to end just above the ramdisk and lock it. Unfortunately
+; we have to do it this complicated because there is no way of
+; knowing how the XMS is going to allocate the available memory.
+; Another problem is that at least HIMEM.SYS requires up to 256
+; bytes of stack, and we cannot assume the caller of INT 13 to
+; provide that much so we have to change stacks.
+; Input:  none
+; Output: none
+; Registers changed: AX, BX
+
+inixms:        call    setstk                  ; set new stack
+
+; First check that the XMS version number is correct. To support all
+; necessary functions it has to be version 2.0+.
+
+       xor     ah,ah
+       call    callxm                  ; get version number
+       cmp     ah,CON(0x02)
+       jb      inixm8                  ; wrong XMS version
+
+; Determine how much memory we can allocate.
+
+       mov     ah,CON(0x08)
+       xor     bl,bl                   ; get amount of extended memory
+       call    callxm
+       or      bl,bl                   ; check for error
+       jnz     inixm8
+       mov     bx,LOC(rdsize)          ; get size of ramdisk
+       add     bx,BCON(65)             ; care for a missing HMA
+       cmp     bx,ax                   ; check if enough memory for ram disk
+       ja      inixm8
+
+; Grab all of the extended memory.
+
+       push    bx
+       mov     dx,ax                   ; grab largest block - which is whole
+       mov     ah,CON(0x09)            ; memory because there should be no
+       call    callxm                  ; other process using XMS
+       pop     bx
+       or      ax,ax                   ; check for error
+       jz      inixm8
+       mov     LOC(xmshdl),dx          ; save handle
+
+; Now resize the memory block so that it will contain the ramdisk image.
+
+       mov     ah,CON(0x0f)            ; reallocate memory block
+       call    callxm
+       or      ax,ax                   ; check for error
+       jnz     inixm1
+
+inixm8:        mov     WLOC(xmshdl),CON(0)     ; in case of error dont return handle
+       JMP(inixm9)
+
+; Now lock the memory block and check that the physical address of the
+; memory block is correct.
+
+inixm1:        mov     dx,LOC(xmshdl)
+       mov     ah,CON(0x0c)            ; lock memory block
+       call    callxm
+       add     bx,CON(0x03ff)
+       adc     dx,BCON(0x0001)         ; add 65kb - maximum difference
+       sub     bx,LOC(rdaddr+0)        ; check that ramdisk address is below
+       sbb     dx,LOC(rdaddr+2)
+       jc      inixm8
+
+; Thats it. Restore all registers and swap the stack back to its
+; original state.
+
+inixm9:        call    rststk                  ; restore old stack
+       ret
+
+
+
+;====================================================================
+;
+; Call XMS driver.
+; Input:  AH  -  function code
+;         other registers depending on function code
+; Output: depends on called function
+; Registers changed: depends on called function
+
+callxm:        push    ax
+       push    bp
+       push    ax
+       mov     bp,sp
+       mov     ax,[bp+6]
+       mov     [bp+4],ax               ; make far return address from
+       mov     [bp+6],cs               ; near call
+       pop     ax
+       pop     bp
+       SEGCS
+       push    WLOC(xmsadr+2)          ; push address of XMS driver
+       SEGCS
+       push    WLOC(xmsadr+0)
+       retf                            ; call XMS driver
+
+
+
+;====================================================================
+;
+; Set new stack
+; Input:  none
+; Output: none
+; Registers changed: AX, BX, DS, SS, SP
+
+setstk:        cli
+       pop     bx                      ; get return address
+       mov     ax,sp
+       SEGCS
+       mov     LOC(oldstk+0),ax
+       SEGCS
+       mov     LOC(oldstk+2),ss        ; save old stack pointer
+       mov     ax,cs
+       mov     ss,ax
+       mov     sp,CON(newtos - 2)      ; change to new stack
+       sti
+       push    cx
+       push    dx
+       push    si                      ; save all registers
+       push    di
+       push    es
+       push    ds
+       mov     ax,cs                   ; set DS to current segment
+       mov     ds,ax
+       jmp     bx                      ; return to caller
+
+
+
+;====================================================================
+;
+; Reset stack to old stack
+; Input:  none
+; Output: none
+; Registers changed: all (reset to old state)
+
+
+rststk:        pop     bx                      ; get return address
+       pop     ds
+       pop     es
+       pop     di
+       pop     si                      ; restore all registers
+       pop     dx
+       pop     cx
+       cli
+       SEGCS
+       mov     ax,LOC(oldstk+0)        ; restore old stack
+       SEGCS
+       mov     ss,LOC(oldstk+2)
+       mov     sp,ax
+       sti
+       jmp     bx                      ; return to caller
+
+
+
+;====================================================================
+;
+; Remove ramdisk from memory. This involves restoring all interrupt
+; vectors, freeing all used memory and restoring the DOS drive para-
+; meter table. Since we need to call the XMS drive, we have to switch
+; stacks like with inixms.
+; Input:  none
+; Output: AL  -  non-zero if error
+; Registers changed: AX
+
+rmrd:  push    bx
+       call    setstk                  ; set new stack
+       mov     al,LOC(drvid)
+       cmp     al,CON(0x80)            ; can only restore floppy drives
+       jb      rmrd1
+rmrd8: call    rststk                  ; return with error
+       pop     bx
+       mov     al,CON(0x01)
+       ret
+
+; First determine the address of the DOS disk parameter block for the
+; ramdisk and check that the open count is zero, i.e. no open file on
+; the device.
+
+rmrd1: push    ds
+       mov     ax,CON(0x0803)
+       int     0x2f                    ; get address of drive parameter
+       mov     ax,ds                   ; table from DOS
+       mov     es,ax
+       pop     ds
+
+rmrd2: SEGES
+       mov     al,[di+4]               ; get physical unit number
+       cmp     al,LOC(drvid)           ; is it our drive?
+       je      rmrd3
+       cmp     di,CON(0xffff)          ; error if we couldnt find the DPB
+       je      rmrd8                   ; for the ramdisk
+       SEGES
+       les     di,[di]                 ; get pointer to next entry
+       JMP(rmrd2)
+
+rmrd3: mov     LOC(dpb_addr+0),di
+       mov     LOC(dpb_addr+2),es
+       SEGES
+       mov     ax,[di+0x20]            ; get device open count
+       or      ax,ax
+       jnz     rmrd8
+
+; Next restore the interrupt vectors. Int 13h is special as it is
+; redirected by DOS. However, DOS provides a function to restore
+; that interrupt. Interrupt 2Fh doesnt have to get restored because
+; DOS does never call the old interrupt again.
+
+       xor     ax,ax
+       mov     es,ax
+       mov     ax,cs
+       SEGES                           ; first check that nobody redirected
+       cmp     ax,LOC(IF8_INT+2)       ; our own interrupts. In that case
+       jne     rmrd8                   ; there is no chance of removing the
+       SEGES                           ; ramdisk.
+       mov     ax,LOC(IF8_INT+0)
+       cmp     ax,CON(intF8)
+       jne     rmrd8
+       mov     si,CON(if1sig)
+       mov     di,CON(IF1_INT)
+       mov     cx,CON(4)               ; interrupt F1h contains a signature
+       repz                            ; and no vector
+       cmpsb
+       jnz     rmrd8
+
+       push    ds
+       les     bx,LOC(old13h)          ; get old interrupt vector 13h
+       mov     dx,bx
+       mov     ax,es                   ; save it into DS:DX and ES:BX
+       mov     ds,ax
+       mov     ah,CON(0x13)
+       int     0x2f                    ; call DOS to restore vector
+       mov     ax,ds
+       mov     cx,cs
+       cmp     ax,cx                   ; check that its indeed our interrupt
+       jne     rmrd4                   ; which we are replacing
+       mov     ax,es
+       cmp     ax,cx
+       jne     rmrd4
+       cmp     bx,CON(int13)
+       jne     rmrd4
+       cmp     dx,CON(int13)
+       je      rmrd5
+
+rmrd4: mov     ah,CON(0x13)
+       int     0x2f                    ; restore old interrupt
+       pop     ds                      ; someone redirected the interrupt
+#ifdef USE_AS86
+       jmp     near rmrd8              ; already, cant restore
+#endif
+#ifdef USE_NASM
+       jmp     rmrd8                   ; already, cant restore
+#endif
+
+rmrd5: pop     ds                      ; restore the other interrupts
+       cli
+       xor     ax,ax
+       mov     es,ax
+       mov     ax,LOC(oldF8h+0)
+       SEGES
+       mov     LOC(IF8_INT+0),ax
+       mov     ax,LOC(oldF8h+2)
+       SEGES
+       mov     LOC(IF8_INT+2),ax
+       sti
+
+; OK, we can now setup the DOS drive parameter table to contain the
+; correct values for the physical floppy drive. If we couldnt create
+; a valid parameter table entry for this drive, simply mark the DOS
+; entry as invalid. This will cause "Not Ready" errors in DOS. This
+; doesnt work with DR-DOS 5.0!
+
+       les     di,LOC(dpb_addr)        ; get address of DPB
+       SEGES
+#ifdef USE_AS86
+       or      [di+0x1f],BCON(80)      ; mark drive as invalid
+#endif
+#ifdef USE_NASM
+       or      word [di+0x1f],BCON(80) ; mark drive as invalid
+#endif
+       test    BLOC(dpb_valid),CON(0xff)       ; check if DPB valid
+       jz      rmrd6
+       cld                             ; got correct table entry
+       mov     cx,CON(dpb_end - dpb)
+       mov     si,CON(dpb)
+       add     di,BCON(4)
+       rep
+       movsb                           ; simply copy the DPB
+
+; Next remove the ramdisk image from extended memory using the XMS driver.
+
+rmrd6: mov     dx,LOC(xmshdl)
+       or      dx,dx                   ; only free memory if we really
+       jz      rmrd7                   ; assigned it with XMS
+       push    dx
+       mov     ah,CON(0x0d)
+       call    callxm                  ; unlock memory block
+       pop     dx
+       or      ax,ax                   ; dont free block if error
+       jz      rmrd7
+       mov     ah,CON(0x0a)
+       call    callxm                  ; free memory block
+
+; Finally we can remove the memory for the ramdisk driver. We only
+; reset the owner field of the memory control block to 0 to indicate
+; it as free.
+
+rmrd7: mov     dx,CON(start_resident)  ; determine last usable segment
+       mov     cl,CON(4)               ; from segment and offset of
+       shr     dx,cl                   ; the resident section
+       mov     ax,cs
+       add     dx,ax                   ; add offset to segment
+       sub     dx,BCON(2)
+       mov     es,dx
+       mov     di,CON(1)
+       xor     ax,ax                   ; set owner field to 0
+       stosw
+       add     di,BCON(5)
+       mov     cx,CON(4)               ; clear owner name
+       rep
+       stosw
+
+; Thats it. Return to caller.
+
+rmrd9: call    rststk                  ; restore old stack
+       pop     bx
+       xor     al,al                   ; return without error
+       ret
+
+
+
+;====================================================================
+;
+; Variables for the resident section
+
+               ALIGN(2)
+
+oldints:
+old13h:                dd      0               ; old interrupt 13h vector
+old2Fh:                dd      0               ; old interrupt 2Fh vector
+oldF1h:                dd      0               ; old interrupt F1h vector
+oldF8h:                dd      0               ; old interrupt F8h vector
+
+
+; Disk parameters for ram disk
+
+statof:                dw      BIOS_FDSTAT     ; offset to BIOS disk status byte
+rdaddr:                dd      0               ; base address of ram disk
+rdsize:                dw      0               ; size of ram disk in kb
+cylnum:                dw      80              ; number of cylinders
+secnum:                dw      2400            ; number of sectors on disk
+secptk:                dw      15              ; number of sectors per track
+drvnum:                db      1               ; number of disk drives
+drvid:         db      0               ; ram disk drive id
+nohd:          db      0               ; no-hard-disk flag
+syscnf:                dw      0               ; system configuration from BIOS
+
+               ALIGN(2)
+
+
+; Variables used to access the XMS interface
+
+xmshdl:                dw      0               ; XMS handle for ram disk
+xmsadr:                dd      0               ; address of XMS driver interface
+
+
+; Variables used to redirect the stack
+
+oldstk:                dd      0               ; old stack pointer
+newstk:                SPACE(512)              ; new stack for calling XMS driver
+newtos:                                        ; new top of stack
+
+
+; Signature to put into interrupt vector F1h
+
+if1sig:                STRDECL('NetB')
+
+
+; Descriptor table to access ram disk using the BIOS
+
+rd_gdt:                dw      0,0,0,0
+               dw      0,0,0,0
+rd_src:                dw      0xffff          ; length
+rd_srcb:       db      0,0,0           ; base
+               db      0x93            ; typebyte
+               dw      0               ; limit16,base24 =0
+rd_dst:                dw      0xffff          ; length
+rd_dstb:       db      0,0,0           ; base
+               db      0x93            ; typebyte
+               dw      0               ; limit16,base24 =0
+               dw      0,0,0,0         ; BIOS CS
+               dw      0,0,0,0         ; BIOS DS
+
+
+; DOS disk parameter block. It contains the definitions for the
+; floppy disk drive which is redirected by the ramdisk, and used
+; for removing the ramdisk drive. Note that this DPB is only
+; valid for DOS 4.0 and higher.
+
+dpb_addr:      dd      0               ; address of DPB in DOS data area
+dpb_valid:     db      0               ; non-zero if DPB is valid
+
+dpb:
+dpb_phys:      db      0               ; BIOS ID of physical drive
+dpb_log:       db      0               ; logical DOS drive ID
+
+dpb_bpb_low:   dw      512             ; BIOS param block for lowest capacity
+               db      0xff
+               dw      1
+               db      2
+               dw      64
+               dw      360
+               db      0x00
+               dw      2
+               dw      9
+               dw      1
+               dd      0
+               dd      0
+
+dpb_fat:       db      0               ; flag indicating 16-bit FAT
+dpb_open:      dw      0               ; device open count
+dpb_type:      db      0x01            ; device type
+dpb_flags:     dw      DPB_F_DEFAULT   ; flags describing drive
+dpb_cyls:      dw      80              ; number of cylinders
+
+dpb_bpb_cur:   dw      512             ; BIOS parameter block for current
+               db      1
+               dw      1
+               db      2
+               dw      224
+               dw      2400
+               db      0xf9
+               dw      7
+               dw      15
+               dw      2
+               dd      0
+               dd      0
+
+dpb_rsvd:      db      0, 0, 0, 0, 0, 0
+dpb_ltrack:    db      0xff            ; last accessed track
+dpb_lacc:      dd      0xffffffff      ; time of last disk access
+dpb_volname:   STRDECL('NO NAME    ')  ; volume name
+               db      0
+dpb_sernum:    dd      0               ; volume serial number
+dpb_fsname:    STRDECL('FAT12   ')     ; file system name
+               db      0
+dpb_end:
+
+
+; Copy of bootp block from bootrom. This has to be last in the data area!
+
+btplen:                dw      0               ; length of bootp block
+btpnew:                                        ; bootp block has to be at the very end
+
diff --git a/first-dos.h b/first-dos.h
new file mode 100644 (file)
index 0000000..b3ef499
--- /dev/null
@@ -0,0 +1,215 @@
+/*
+ * first.inc  -  constants for assembler module for DOS boot loader
+ *
+ * Copyright (C) 1996-1998 Gero Kuhlmann   <gero@gkminix.han.de>
+ *
+ *  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
+ *  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.
+ */
+
+
+
+/*
+ *====================================================================
+ *
+ * Definitions for accessing the BIOS data area
+ */
+
+#define BIOS_SEG       0x0040          /* Segment of BIOS data area */
+#define BIOS_FDSTAT    0x0041          /* Floppy disk status byte */
+#define BIOS_HDSTAT    0x0074          /* Hard disk status byte */
+
+#define I13_INT                0x13 * 4        /* interrupt vector 13h */
+#define I2F_INT                0x2F * 4        /* interrupt vector 2Fh */
+#define IF1_INT                0xF1 * 4        /* interrupt vector F1h */
+#define IF8_INT                0xF8 * 4        /* interrupt vector F8h */
+
+
+
+/*
+ *====================================================================
+ *
+ * Layout of disk boot sector
+ */
+
+#define SECT_SIZE      512             /* sector size must be 512 bytes */
+
+#define DISK_BPS       11              /* offset of bytes per sectors */
+#define DISK_SECTS     19              /* offset of total number of sects */
+#define DISK_SPT       24              /* offset of sectors per track */
+#define DISK_HEADS     26              /* offset of number of heads */
+#define DISK_BOOT      36              /* offset of disk ID to boot from */
+
+#define PART_STATUS    0x01BE          /* offset of partition status */
+#define PART_FRST_HEAD 0x01BF          /* offset of first head number */
+#define PART_FRST_SECT 0x01C0          /* offset of first sector number */
+#define PART_FRST_CYL  0x01C1          /* offset of first cylinder number */
+#define PART_TYPE      0x01C2          /* offset of partition id */
+#define PART_LAST_HEAD 0x01C3          /* offset of last head number */
+#define PART_LAST_SECT 0x01C4          /* offset of last sector number */
+#define PART_LAST_CYL  0x01C5          /* offset of last cylinder number */
+#define PART_ABS_SECT  0x01C6          /* offset of first sector number */
+#define PART_SEC_NUM   0x01CA          /* offset of number of sectors */
+
+#define PART_ACTIVE    0x80            /* indicates active partition */
+#define PART_FAT12     0x01            /* indicates partition type */
+#define PART_FAT16     0x04            /* indicates partition type */
+
+#define BOOT_ID_FD     0x00            /* BIOS id of floppy boot disk */
+#define BOOT_ID_HD     0x80            /* BIOS id of hard boot disk */
+#define BOOT_OFFSET    0x7C00          /* offset for boot block in segment 0 */
+#define TEMP_SEGMENT   0x7000          /* segment for temporary storage */
+
+
+
+/*
+ *====================================================================
+ *
+ * Layout of BIOS parameter block as used by DOS
+ */
+
+#define BPB_BPS                 0              /* offset to bytes per sector */
+#define BPB_SPC                 2              /* offset to sectors per cluster */
+#define BPB_RES                 3              /* offset to # of reserved sectors */
+#define BPB_FATS        5              /* offset to # of FATs */
+#define BPB_DIR                 6              /* offset to # of root dir entries */
+#define BPB_TOT_SECTS   8              /* offset to total # of sectors */
+#define BPB_MEDIA_ID   10              /* offset to media ID */
+#define BPB_SPF                11              /* offset to # of sectors per FAT */
+#define BPB_SPT                13              /* offset to # of sectors per track */
+#define BPB_HEADS      15              /* offset to # of heads */
+#define BPB_HIDDEN     17              /* offset to # of hidden sectors */
+
+
+
+/*
+ *====================================================================
+ *
+ * Flags for DOS drive parameter block
+ */
+
+#define DPB_F_FIXED    0x0001          /* fixed media */
+#define DPB_F_DOOR     0x0002          /* drive supports door lock status */
+#define DPB_F_TSIZE    0x0004          /* all sectors in track are same size */
+#define DPB_F_MULTI    0x0008          /* multiple logical units in drive */
+#define DPB_F_SHARED   0x0010          /* logical for shared physical drive */
+#define DPB_F_CHANGE   0x0020          /* disk change detected */
+#define DPB_F_NEWPARA  0x0040          /* device parameters were changed */
+#define DPB_F_FORMAT   0x0080          /* disk reformatted */
+#define DPB_F_ACCESS   0x0100          /* access flag - fixed media only */
+
+#define DPB_F_DEFAULT  DPB_F_CHANGE + DPB_F_NEWPARA
+
+#define DPB_T_360      0               /* type ID for 360kB disk */
+#define DPB_T_1200     1               /* type ID for 1.2MB disk */
+#define DPB_T_720      2               /* type ID for 720kB disk */
+#define DPB_T_SD8      3               /* type ID for SD 8 inch disk */
+#define DPB_T_HD8      4               /* type ID for HD 8 inch disk */
+#define DPB_T_FIXED    5               /* type ID for fixed disk */
+#define DPB_T_TAPE     6               /* type ID for tape drive */
+#define DPB_T_1440     7               /* type ID for 1.44MB disk */
+#define DPB_T_OPTICAL  8               /* type ID for optical disk */
+#define DPB_T_2880     9               /* type ID for 2.88MB disk */
+
+
+
+/*
+ *====================================================================
+ *
+ * Vendor information for the boot rom image. These values have to be
+ * identical to those in mknbi.h.
+ */
+
+#ifdef FREEDOS
+#define VENDOR_SIZE    5               /* sizeof "mknbi-fdos-1.x-y" in dwords */
+#else
+#define VENDOR_SIZE    4               /* sizeof "mknbi-dos-1.x-y" in dwords */
+#endif
+
+#define VENDOR_BOOTL   16              /* tag for boot loader segment */
+#ifdef FREEDOS
+#define VENDOR_RAMDISK 18              /* tag for ramdisk image */
+#else
+#define VENDOR_RAMDISK 17              /* tag for ramdisk image */
+#endif
+
+
+
+/*
+ *====================================================================
+ *
+ * Layout and magic ID of boot rom image header. These values have to
+ * be identical to those used by the boot rom. See SPEC.DOC of the
+ * boot rom source for further information.
+ */
+
+#define        BOOT_MAGIC      0x1B031336      /* boot image header magic cookie */
+#define BOOT_SIZE      512             /* total size of boot image header */
+
+#define BOOT_HD_SIZE   4               /* size of header in dwords */
+#define BOOT_HD_MAGIC  0               /* offset for header magic number */
+#define BOOT_HD_LENGTH 4               /* offset for header length */
+#define BOOT_HD_FLAG1  5               /* offset for header flag 1 */
+#define BOOT_HD_FLAG2  6               /* offset for header flag 2 */
+#define BOOT_HD_FLAG3  7               /* offset for header flag 3 */
+#define BOOT_HD_LOCN   8               /* offset for header location */
+#define BOOT_HD_EXEC   12              /* offset for execute address */
+#define BOOT_HD_VENDOR 16              /* offset for header vendor information */
+
+#define BOOT_LD_SIZE   4               /* size of load record in dwords */
+#define BOOT_LD_LENGTH 0               /* offset for load record length */
+#define BOOT_LD_TAG1   1               /* offset for load record tag 1 */
+#define BOOT_LD_TAG2   2               /* offset for load record tag 2 */
+#define BOOT_LD_FLAGS  3               /* offset for load record flags */
+#define BOOT_LD_ADDR   4               /* offset for absolute address */
+#define BOOT_LD_ILENGTH        8               /* offset for image length */
+#define BOOT_LD_MLENGTH        12              /* offset for memory length */
+#define BOOT_LD_VENDOR 16              /* offset for vendor information */
+
+#define BOOT_FLAG_B0   0x01            /* mask for load record flag B0 */
+#define BOOT_FLAG_B1   0x02            /* mask for load record flag B1 */
+#define BOOT_FLAG_EOF  0x04            /* mask for load record flag EOF */
+
+#define BOOT_LD_SECNUM 0               /* offset of total number of sectors */
+#define BOOT_LD_SPT    2               /* offset of sectors per track */
+#define BOOT_LD_CYL    4               /* offset of number of cylinders */
+#define BOOT_LD_DRIVE  6               /* offset of boot drive ID */
+#define BOOT_LD_NOHD   7               /* offset of no-hard-disk flag */
+
+
+
+/*
+ *====================================================================
+ *
+ * Layout and magic ID of bootp record. Refer to RFC 951, RFC 1048
+ * and RFC 1533 for further information. The BOOTP record can be
+ * longer than the standard length as the bootrom might be able to
+ * load a vendor extension file via tftp.
+ */
+
+#define BOOTP_MAGIC_RFC        0x63, 0x82, 0x53, 0x63  /* RFC 1048 vendor ID */
+#define BOOTP_MAGIC_CMU        0x43, 0x4D, 0x55, 0x00  /* CMU vendor ID */
+#define BOOTP_MAGIC_STA        0x53, 0x54, 0x41, 0x4E  /* Stanford vendor ID */
+#define BOOTP_MAGIC_LEN        4                       /* length of vendor ID */
+
+#define BOOTP_REQUEST  1               /* bootp OP-code */
+#define BOOTP_REPLY    2
+
+#define BOOTP_OP       0               /* offset to bootp OP-code */
+#define BOOTP_VEND     236             /* offset to vendor information */
+#define BOOTP_SIZE     300             /* size of complete bootp record */
+
+#define BOOTP_RFC_NOP  0               /* RFC vendor tag for NO-OP */
+#define BOOTP_RFC_END  255             /* RFC vendor tag for end of record */
+
diff --git a/first32.c b/first32.c
new file mode 100644 (file)
index 0000000..2af4a71
--- /dev/null
+++ b/first32.c
@@ -0,0 +1,637 @@
+#include       "stddef.h"
+#include       "string.h"
+#include       "linux-asm-io.h"
+#include       "etherboot.h"
+#include       "elf_boot.h"
+
+#define SERIAL_CONSOLE 0
+/*
+ * 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, or (at
+ * your option) any later version.
+ */
+
+/*
+
+Memory layout assumed by mknbi and this program
+
+0x07C00-0x07FFF   0.5 kB       floppy boot sector if loaded from floppy
+0x0F???-0x0FFFF     ? kB       large Etherboot data buffers (deprecated)
+0x10000-0x8FFFF 512.0 kB       kernel (from tagged image)
+0x90000-0x901FF          0.5 kB        Linux floppy boot sector (from Linux image)
+0x90200-0x921FF          8.0 kB        kernel setup (from Linux image)
+0x92200-0x923FF          0.5 kB        tagged image header ("directory")
+0x92400-0x927FF          1.0 kB        kernel parameters (generated by mknbi)
+0x92800-0x93FFF          6.0 kB        this program (generated by mknbi)
+0x94000-0x9FFFF         48.0 kB        Etherboot (top few kB may be used by BIOS)
+                               Normally Etherboot starts at 0x94000
+0x100000-                      kernel (if bzImage) (from tagged image)
+after bzImage kernel           ramdisk (optional) (from tagged image)
+                               moved to below top of memory by this program
+                               but not higher than 896kB or what the
+                               limit in setup.S says
+
+*/
+
+#define                PARAMSIZE       512
+
+extern void printf(const char *, ...);
+extern int sprintf(char *, const char *, ...);
+extern void console_putc(int);
+extern unsigned long memsize(void);
+extern void xstart(unsigned long);
+extern void exit(int);
+
+#ifdef FIRST32ELF
+static Elf32_Phdr      *seg[S_END] = { 0 };
+#else
+static struct segment  *seg[S_END] = { 0 };
+#endif
+static unsigned char   *ip, *op;
+static short           *vgamode;
+static struct bootp_t  *bp;
+static unsigned char   *vendortags;
+unsigned long          top_of_initrd = 0;
+static enum { RD_TOP, RD_ASIS, RD_HEXADDR } rdmode = RD_TOP;
+static unsigned long   rdaddr;
+
+
+#if SERIAL_CONSOLE
+/* Base Address */
+#define TTYS0 0x3f8
+/* Data */
+#define TTYS0_RBR (TTYS0+0x00)
+#define TTYS0_TBR (TTYS0+0x00)
+/* Control */
+#define TTYS0_IER (TTYS0+0x01)
+#define TTYS0_IIR (TTYS0+0x02)
+#define TTYS0_FCR (TTYS0+0x02)
+#define TTYS0_LCR (TTYS0+0x03)
+#define TTYS0_MCR (TTYS0+0x04)
+
+#define TTYS0_DLL (TTYS0+0x00)
+#define TTYS0_DLM (TTYS0+0x01)
+/* Status */
+#define TTYS0_LSR (TTYS0+0x05)
+#define TTYS0_MSR (TTYS0+0x06)
+#define TTYS0_SCR (TTYS0+0x07)
+
+static void ttys0_tx_byte(unsigned byte)
+{
+       while((inb(TTYS0_LSR) & 0x20) == 0)
+               ;
+       outb(byte, TTYS0_TBR);
+}
+
+#endif
+void putchar(int c)
+{
+       if (c == '\n')
+               putchar('\r');
+#if SERIAL_CONSOLE
+       ttys0_tx_byte(c);
+#endif
+       console_putc(c);
+}
+
+static inline void quit(void)
+{
+       printf("Bad argument\n");
+       exit(0);
+}
+
+static void nomem(void)
+{
+       printf("Out of parameter space\n");
+       exit(0);
+}
+
+static inline void checkvendor(void)
+{
+       union {
+               unsigned long   l;
+               unsigned char   c[4];
+       } u;
+
+       memcpy(u.c, vendortags, sizeof(u));
+       if (u.l == RFC_1048 || u.l == VEND_CMU || u.l == VEND_STAN)
+               vendortags += 4;
+       else
+               vendortags = 0;
+}
+
+#ifdef FIRST32ELF
+static inline void locate_segs(union infoblock *header)
+{
+       int             i;
+       Elf32_Phdr      *s;
+
+       s = (Elf32_Phdr *)((char *)header + header->ehdr.e_phoff);
+       for (i = 0; i < S_END && i < header->ehdr.e_phnum; i++, s++) {
+               seg[i] = s;
+#if    DEBUG > 1
+               printf("%d %#X\n", i, s->p_paddr);
+#endif
+       }
+}
+
+#else
+
+static inline void locate_segs(union infoblock *header)
+{
+       int             i;
+       struct segment  *s;
+
+       s = (struct segment *)((char *)header + sizeof(struct imgheader)
+               + ((header->img.length & 0xF0) >> 2));
+       for (i = 0; i < S_END; i++, s++) {
+               seg[i] = s;
+#if    DEBUG > 1
+               printf("%d %#X\n", i, s->p_paddr);
+#endif
+               if (s->flags & F_FINAL)
+                       break;
+               s = (struct segment *)((char *)s + ((s->lengths & 0xF0) >> 2));
+       }
+}
+#endif /* !FIRST32ELF */
+
+/*
+ *     Find DHCP vendor tag, return pointer to tag length
+ */
+static unsigned char *gettag(unsigned int tag)
+{
+       unsigned char           *p;
+       unsigned char           c;
+       static unsigned char    emptytag[] = { 0, 0 };
+
+       if (vendortags == 0)
+               return (emptytag);
+       for (p = vendortags; (c = *p) != RFC1533_END; ) {
+               if (c == RFC1533_PAD)
+                       p++;
+               else if (c == tag)
+                       return (p + 1);
+               else
+                       p += p[1] + 2;
+       }
+       return (emptytag);
+}
+
+static void outtag(unsigned char *value)
+{
+       int                     len;
+
+       len = *value++;
+       if (op + len > ip)
+               nomem();
+       while (len-- > 0)
+               *op++ = *value++;
+}
+
+/* Return 1 if s2 is a prefix of s1 */
+static int strprefix(const unsigned char *s1, const unsigned char *s2)
+{
+       while (*s1 != '\0' && *s2 != '\0' && *s1++ == *s2++)
+               ;
+       /* Have we reached the end of s2? */
+       return (*s2 == '\0');
+}
+
+enum keyword { K_VGA, K_NFSROOT, K_IP, K_RDBASE, K_MEM };
+
+static inline int match_keyword(const unsigned char *start)
+{
+       if (strprefix(start, "vga"))
+               return (K_VGA);
+       if (strprefix(start, "nfsroot"))
+               return (K_NFSROOT);
+       if (strprefix(start, "ip"))
+               return (K_IP);
+       if (strprefix(start, "rdbase"))
+               return (K_RDBASE);
+       if (strprefix(start, "mem"))
+               return (K_MEM);
+       return (-1);
+}
+
+#define        isws(c) ((c) == ' ' || (c) == '\t')
+
+static inline int copy_and_match(void)
+{
+       int                     c;
+       unsigned char           *start;
+
+       start = ip;
+       /* Stop copying at = if it exists */
+       while ((c = *ip) != '\0' && !isws(c) && c != '=') {
+               *op++ = *ip++;
+       }
+       if (c == '=') {
+               ip++;
+               *op++ = '=';
+               return (match_keyword(start));
+       }
+       return (-1);
+}
+
+static unsigned long gethex(const unsigned char *p)
+{
+       unsigned long           value = 0;
+
+       for (;;) {
+               int c = *p++;
+               if (c >= '0' && c <= '9')
+                       c -= '0';
+               else if (c >= 'a' && c <= 'f')
+                       c -= 'a' - 10;
+               else if (c >= 'A' && c <= 'F')
+                       c -= 'A' - 10;
+               else
+                       break;
+               value <<= 4;
+               value |= c;
+       }
+       return (value);
+}
+
+static int getdec(const unsigned char *p)
+{
+       int                     value = 0, sign = 0;
+
+       if (*p == '-') {
+               sign = 1;
+               p++;
+       }
+       for (;;) {
+               int c = *p++;
+               if (c >= '0' && c <= '9')
+                       c -= '0';
+               else
+                       break;
+               value *= 10;
+               value += c;
+       }
+       return (sign ? -value : value);
+}
+
+static void copy_nonws(void)
+{
+       int                     c;
+
+       /* Copy up to next whitespace */
+       while ((c = *ip) != '\0' && !isws(c))
+               *op++ = *ip++;
+}
+
+static void discard_arg(void)
+{
+       int                     c;
+
+       /* Discard up to next whitespace */
+       while ((c = *ip) != '\0' && !isws(c))
+               ip++;
+}
+
+static int outip(const unsigned char *p)
+{
+       long            ip;
+
+       if (*p == 0)
+               return (0);
+       memcpy(&ip, p + 1, sizeof(ip));
+       return sprintf(op, "%@", ip);
+}
+
+static inline void subst_value(int kwindex)
+{
+       int                     c;
+       unsigned char           *p;
+
+       if (kwindex == K_VGA) {
+               /* backup over "vga=" */
+               op -= sizeof("vga=") - 1;
+               if (strprefix(ip, "ask"))
+                       c = -3;
+               else if (strprefix(ip, "extended"))
+                       c = -2;
+               else if (strprefix(ip, "normal"))
+                       c = -1;
+               else if (strprefix(ip, "0x"))
+                       c = gethex(ip+2);
+               else    /* assume decimal mode number */
+                       c = getdec(ip);
+               *vgamode = c;
+               discard_arg();
+       } else if (kwindex == K_NFSROOT && strprefix(ip, "rom") &&
+               (ip[3] == '\0' || isws(ip[3]))) {
+               outtag(gettag(RFC1533_ROOTPATH));
+               discard_arg();
+       } else if (kwindex == K_IP && strprefix(ip, "rom") &&
+               (ip[3] == '\0' || isws(ip[3]))) {
+               long            ip;
+               op += sprintf(op, "%@:%@:", bp->bp_yiaddr, bp->bp_siaddr);
+               p = gettag(RFC1533_GATEWAY);
+               op += outip(p);
+               *op++ = ':';
+               p = gettag(RFC1533_NETMASK);
+               op += outip(p);
+               *op++ = ':';
+               outtag(gettag(RFC1533_HOSTNAME));
+               p = gettag(RFC1533_VENDOR_ETHDEV);
+               if (*p)
+                       *op++ = ':';
+               outtag(p);
+               discard_arg();
+       } else if (kwindex == K_RDBASE) {
+               if (strprefix(ip, "top"))
+                       rdmode = RD_TOP;
+               else if (strprefix(ip, "asis"))
+                       rdmode = RD_ASIS;
+               else if (strprefix(ip, "0x")) {
+                       rdmode = RD_HEXADDR;
+                       rdaddr = gethex(ip+2);
+               }
+               discard_arg();
+       } else if (kwindex == K_MEM) {
+               unsigned char   *p;
+               unsigned long   memsize;
+               memsize = getdec(p = ip);
+               while (*p >= '0' && *p <= '9')
+                       ++p;
+               if (*p == 'G')
+                       memsize <<= 30;
+               else if (*p == 'M')
+                       memsize <<= 20;
+               else if (*p == 'K')
+                       memsize <<= 10;
+               top_of_initrd = memsize;
+               copy_nonws();
+       } else
+               copy_nonws();
+}
+
+static inline int skipws(void)
+{
+       int                     c;
+
+       while ((c = *ip) != '\0' && isws(c))
+               ip++;
+       return (c);
+}
+
+/*
+ *     The parameters are copied from the input area to the output
+ *     area, looking out for keyword=value pairs while doing so.
+ *     If a possible keyword is found, indicated by an =,
+ *     it is matched against a small list.
+ *     If it matches none of the keywords on the list,
+ *     the value is copied unchanged.
+ *     If it matches a keyword, then the appropriate substitutions
+ *     are made.
+ *     While doing the substitution, a check is made that the output
+ *     pointer doesn't overrun the input pointer. This is the only
+ *     place it could happen, as the substitution may be longer than
+ *     the original.
+ */
+static inline void process_params(void)
+{
+       int                     i;
+
+       while (skipws() != '\0') {
+               if ((i = copy_and_match()) >= 0)
+                       subst_value(i);
+               else
+                       copy_nonws();
+               *op++ = ' ';
+       }
+       /* There may be a space after the last arg, probably does not matter
+           but this is a reminder */
+       *op = '\0';
+}
+
+/*
+ * String is not null terminated, count of chars following is in first element
+ * If there are 6 colons, returns char position after 6th colon
+ * Else returns one position after string
+ * which forces the length calculated below to be negative
+ * Length of 7th argument can be calculated by subtracting the
+ * length of the string preceding from the total length.
+ */
+static inline unsigned char *skip6colons(unsigned char *p)
+{
+       int     len, coloncount;
+
+       for (len = *p++, coloncount = 6; len > 0 && coloncount > 0; p++, len--)
+               if (*p == ':')
+                       coloncount--;
+       return (p + (coloncount > 0));
+}
+
+static void parse_elf_boot_notes(
+       void *notes, union infoblock **rheader, struct bootp_t **rbootp)
+{
+       unsigned char *note, *end;
+       Elf_Bhdr *bhdr;
+       Elf_Nhdr *hdr;
+
+       bhdr = notes;
+       if (bhdr->b_signature != ELF_BHDR_MAGIC) {
+               return;
+       }
+
+       note = ((char *)bhdr) + sizeof(*bhdr);
+       end  = ((char *)bhdr) + bhdr->b_size;
+       while (note < end) {
+               unsigned char *n_name, *n_desc, *next;
+               hdr = (Elf_Nhdr *)note;
+               n_name = note + sizeof(*hdr);
+               n_desc = n_name + ((hdr->n_namesz + 3) & ~3);
+               next = n_desc + ((hdr->n_descsz + 3) & ~3);
+               if (next > end) 
+                       break;
+#if 0
+               printf("n_type: %x n_name(%d): n_desc(%d): \n", 
+                       hdr->n_type, hdr->n_namesz, hdr->n_descsz);
+#endif
+
+               if ((hdr->n_namesz == 10) &&
+                       (memcmp(n_name, "Etherboot", 10) == 0)) {
+                       switch(hdr->n_type) {
+                       case EB_BOOTP_DATA:
+                               *rbootp = *((void **)n_desc);
+                               break;
+                       case EB_HEADER:
+                               *rheader = *((void **)n_desc);
+                               break;
+                       default:
+                               break;
+                       }
+               }
+               note = next;
+       }
+}
+
+int first(struct ebinfo *eb, union infoblock *header, struct bootp_t *bootp)
+{
+       int                     i;
+       unsigned char           *p, *q, *params;
+       struct bootblock        *boot;
+       struct setupblock       *setup;
+       union {
+               unsigned long   l;
+               unsigned char   c[4];
+       } u;
+
+#if DEBUG > 1
+       printf("&eb = %#X\n", &eb);
+#endif
+       printf(MKNBI_VERSION "/" __FILE__
+#ifdef FIRST32ELF
+               " (ELF)"
+#endif
+               " (GPL)\n");
+#if DEBUG > 1
+       printf("eb = %#X, header = %#X, bootp = %#X\n", eb, header, bootp);
+#endif
+       /* Sanity checks */
+#ifdef FIRST32ELF
+       parse_elf_boot_notes(eb, &header, &bootp);
+       if (header->img.magic != ELF_MAGIC
+#else
+       if (header->img.magic != TAG_MAGIC
+#endif
+               || bootp->bp_op != BOOTP_REPLY)
+               quit();
+       bp = bootp;
+       vendortags = (unsigned char *)bootp->bp_vend;
+       checkvendor();
+       locate_segs(header);
+       /* Locate boot block */
+       boot = (struct bootblock *)seg[S_BOOT]->p_paddr;
+       /* Point to word to alter if vga=... specified */
+       vgamode = &boot->vgamode;
+       /* Locate setup block */
+       setup = (struct setupblock *)seg[S_SETUP]->p_paddr;
+       /* Adjust loader type byte */
+       setup->su_type = SU_MY_LOADER_TYPE;
+       /* If setup version >= 0x202, use new command line protocol.
+          This frees setup.S from being tied to 0x90000 */
+       if (setup->su_version >= 0x202)
+               setup->su_cmd_line_ptr = params = (unsigned char *)seg[S_PARAMS]->p_paddr;
+       else {  /* Use old protocol */
+               /* Adjust boot block pointers to point to command line */
+               boot->cl_magic = CL_MAGIC;
+               boot->cl_offset = (params = (unsigned char *)seg[S_PARAMS]->p_paddr) - ((unsigned char *)boot);
+       }
+       p = params + (i = strlen(params) + 1);
+       /* Append T129 if present */
+       q = gettag(RFC1533_VENDOR_MAGIC);
+       /* Check T128 present and correct */
+       if (*q == 6 && (memcpy(u.c, q + 1, sizeof(u)), u.l == VEND_EB)) {
+               q = gettag(RFC1533_VENDOR_ADDPARM);
+               i = PARAMSIZE - 1 - i;          /*  +1 for SPACE */
+               if (i > *q)                     /* enough space? */
+                       i = *q;
+               q++;
+               if (i > 0)
+                       p[-1] = ' ';            /* NUL -> space */
+               while (i-- > 0)
+                       *p++ = *q++;
+               *p++ = '\0';                    /* position past NUL */
+               if (*(q = gettag(RFC1533_VENDOR_SELECTION)) == 1
+                 && *(q = gettag(q[1])) > 0) {
+                       unsigned char   *r = skip6colons(q);
+                       /* If we have an argument and enough space, copy it */
+                       if ((i = *q - (r - q) + 1) > 0
+                         && i < PARAMSIZE - 2 - strlen(params)) {
+                       /* +2 for SPACE and final NUL */
+                               p[-1] = ' ';
+                               while (i-- > 0) {
+                                       /* escapes: ~b -> \\, ~c -> : */
+                                       if (i > 0 && r[0] == '~' && r[1] == 'b')
+                                               *p++ = '\\', r += 2, --i;
+                                       else if (i > 0 && r[0] == '~' && r[1] == 'c')
+                                               *p++ = ':', r += 2, --i;
+                                       else
+                                               *p++ = *r++;
+                               }
+                               *p++ = '\0';
+                       }
+               }
+       }
+       /* Move parameters to end of parameter area,
+          tail first to avoid overwriting */
+       q = params + PARAMSIZE;
+       /* At least 1 byte is copied, the NUL */
+       do {
+               *--q = *--p;
+       } while (p != params);
+       ip = q;
+       op = params;
+#ifdef DEBUG
+       printf("Parameters: %s\n", p);
+#endif
+       /* mem= param affects top_of_initrd */
+       process_params();
+       if (seg[S_RAMDISK] != 0) {
+               unsigned long           max;
+
+               get_memsizes();
+               max = (setup->su_version >= 0x203) ? setup->ramdisk_max : 0x37FFFFFF;
+               /* compute top of initrd only if user has not overridden it */
+               if (top_of_initrd == 0) {
+                       struct e820entry        *e;
+                       /* look for highest E820_RAM that is under ramdisk_max
+                          strictly speaking we should also check that
+                          we have room for the ramdisk in the memory segment */
+                       for (i = 0; i < meminfo.map_count; i++) {
+                               e = &meminfo.map[i];
+                               if (e->type == E820_RAM
+                                       && e->addr < max
+                                       && (e->addr + e->size) > top_of_initrd)
+                                       top_of_initrd = e->addr + e->size;
+                       }
+               }
+               if (top_of_initrd > max)
+                       top_of_initrd = max;
+               /* Round down to next lower 4k boundary */
+               top_of_initrd &= ~0xFFF;
+               printf("Top of ramdisk is %#X\n", top_of_initrd);
+               if (rdmode == RD_TOP || rdmode == RD_HEXADDR) {
+                       long                    *dp, *sp;
+
+                       sp = (long *)((p = (unsigned char *)seg[S_RAMDISK]->p_paddr) +
+                               (i = seg[S_RAMDISK]->p_filesz));
+                       /*
+                        * If user specified address, align to next lower
+                        * longword boundary
+                        */
+                       if (rdmode == RD_HEXADDR)
+                               dp = (long *)((rdaddr + i) & ~0x3);
+                       else
+                               dp = (long *)top_of_initrd;
+                       /* Copy to destination by longwords, tail first */
+                       while (sp > (long *)p)
+                               *--dp = *--sp;
+                       printf("Ramdisk at %#X, size %#X\n",
+                               (setup->su_ramdisk_start = (unsigned long)dp),
+                               (setup->su_ramdisk_size = i));
+               } else {        /* leave ramdisk as loaded, just report */
+                       printf("Ramdisk at %#X, size %#X\n",
+                               (setup->su_ramdisk_start = (unsigned long)seg[S_RAMDISK]->p_paddr),
+                               (setup->su_ramdisk_size = seg[S_RAMDISK]->p_filesz));
+               }
+       }
+#ifdef DEBUG
+       printf("Ready\n");
+#endif
+#if DEBUG > 3
+       /* Delay so we can read display */
+       for (i = 0; i < 0x7ffffff; i++)
+               ;
+#endif
+       xstart(seg[S_SETUP]->p_paddr);
+       return (0);
+}
diff --git a/linux-asm-io.h b/linux-asm-io.h
new file mode 100644 (file)
index 0000000..4fe91fb
--- /dev/null
@@ -0,0 +1,187 @@
+#ifndef        _ASM_IO_H
+#define _ASM_IO_H
+
+/*
+ * This file contains the definitions for the x86 IO instructions
+ * inb/inw/inl/outb/outw/outl and the "string versions" of the same
+ * (insb/insw/insl/outsb/outsw/outsl). You can also use "pausing"
+ * versions of the single-IO instructions (inb_p/inw_p/..).
+ *
+ * This file is not meant to be obfuscating: it's just complicated
+ * to (a) handle it all in a way that makes gcc able to optimize it
+ * as well as possible and (b) trying to avoid writing the same thing
+ * over and over again with slight variations and possibly making a
+ * mistake somewhere.
+ */
+
+/*
+ * Thanks to James van Artsdalen for a better timing-fix than
+ * the two short jumps: using outb's to a nonexistent port seems
+ * to guarantee better timings even on fast machines.
+ *
+ * On the other hand, I'd like to be sure of a non-existent port:
+ * I feel a bit unsafe about using 0x80 (should be safe, though)
+ *
+ *             Linus
+ */
+
+#ifdef SLOW_IO_BY_JUMPING
+#define __SLOW_DOWN_IO __asm__ __volatile__("jmp 1f\n1:\tjmp 1f\n1:")
+#else
+#define __SLOW_DOWN_IO __asm__ __volatile__("outb %al,$0x80")
+#endif
+
+#ifdef REALLY_SLOW_IO
+#define SLOW_DOWN_IO { __SLOW_DOWN_IO; __SLOW_DOWN_IO; __SLOW_DOWN_IO; __SLOW_DOWN_IO; }
+#else
+#define SLOW_DOWN_IO __SLOW_DOWN_IO
+#endif
+
+/*
+ * 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.
+ */
+#define readb(addr) (*(volatile unsigned char *) (addr))
+#define readw(addr) (*(volatile unsigned short *) (addr))
+#define readl(addr) (*(volatile unsigned int *) (addr))
+
+#define writeb(b,addr) ((*(volatile unsigned char *) (addr)) = (b))
+#define writew(b,addr) ((*(volatile unsigned short *) (addr)) = (b))
+#define writel(b,addr) ((*(volatile unsigned int *) (addr)) = (b))
+
+#define memset_io(a,b,c)       memset((void *)(a),(b),(c))
+#define memcpy_fromio(a,b,c)   memcpy((a),(void *)(b),(c))
+#define memcpy_toio(a,b,c)     memcpy((void *)(a),(b),(c))
+
+/*
+ * Again, i386 does not require mem IO specific function.
+ */
+
+#define eth_io_copy_and_sum(a,b,c,d)   eth_copy_and_sum((a),(void *)(b),(c),(d))
+
+/*
+ * Talk about misusing macros..
+ */
+
+#define __OUT1(s,x) \
+extern void __out##s(unsigned x value, unsigned short port); \
+extern inline void __out##s(unsigned x value, unsigned short port) {
+
+#define __OUT2(s,s1,s2) \
+__asm__ __volatile__ ("out" #s " %" s1 "0,%" s2 "1"
+
+#define __OUT(s,s1,x) \
+__OUT1(s,x) __OUT2(s,s1,"w") : : "a" (value), "d" (port)); } \
+__OUT1(s##c,x) __OUT2(s,s1,"") : : "a" (value), "id" (port)); } \
+__OUT1(s##_p,x) __OUT2(s,s1,"w") : : "a" (value), "d" (port)); SLOW_DOWN_IO; } \
+__OUT1(s##c_p,x) __OUT2(s,s1,"") : : "a" (value), "id" (port)); SLOW_DOWN_IO; }
+
+#define __IN1(s,x) \
+extern unsigned x __in##s(unsigned short port); \
+extern inline unsigned x __in##s(unsigned short port) { unsigned x _v;
+
+#define __IN2(s,s1,s2) \
+__asm__ __volatile__ ("in" #s " %" s2 "1,%" s1 "0"
+
+#define __IN(s,s1,x,i...) \
+__IN1(s,x) __IN2(s,s1,"w") : "=a" (_v) : "d" (port) ,##i ); return _v; } \
+__IN1(s##c,x) __IN2(s,s1,"") : "=a" (_v) : "id" (port) ,##i ); return _v; } \
+__IN1(s##_p,x) __IN2(s,s1,"w") : "=a" (_v) : "d" (port) ,##i ); SLOW_DOWN_IO; return _v; } \
+__IN1(s##c_p,x) __IN2(s,s1,"") : "=a" (_v) : "id" (port) ,##i ); SLOW_DOWN_IO; return _v; }
+
+#define __INS(s) \
+extern void ins##s(unsigned short port, void * addr, unsigned long count); \
+extern inline void ins##s(unsigned short port, void * addr, unsigned long count) \
+{ __asm__ __volatile__ ("cld ; rep ; ins" #s \
+: "=D" (addr), "=c" (count) : "d" (port),"0" (addr),"1" (count)); }
+
+#define __OUTS(s) \
+extern void outs##s(unsigned short port, const void * addr, unsigned long  count); \
+extern inline void outs##s(unsigned short port, const void * addr, unsigned long count) \
+{ __asm__ __volatile__ ("cld ; rep ; outs" #s \
+: "=S" (addr), "=c" (count) : "d" (port),"0" (addr),"1" (count)); }
+
+__IN(b,"", char)
+__IN(w,"",short)
+__IN(l,"", long)
+
+__OUT(b,"b",char)
+__OUT(w,"w",short)
+__OUT(l,,int)
+
+__INS(b)
+__INS(w)
+__INS(l)
+
+__OUTS(b)
+__OUTS(w)
+__OUTS(l)
+
+/*
+ * Note that due to the way __builtin_constant_p() works, you
+ *  - can't use it inside a inline function (it will never be true)
+ *  - you don't have to worry about side effects within the __builtin..
+ */
+#define outb(val,port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+       __outbc((val),(port)) : \
+       __outb((val),(port)))
+
+#define inb(port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+       __inbc(port) : \
+       __inb(port))
+
+#define outb_p(val,port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+       __outbc_p((val),(port)) : \
+       __outb_p((val),(port)))
+
+#define inb_p(port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+       __inbc_p(port) : \
+       __inb_p(port))
+
+#define outw(val,port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+       __outwc((val),(port)) : \
+       __outw((val),(port)))
+
+#define inw(port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+       __inwc(port) : \
+       __inw(port))
+
+#define outw_p(val,port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+       __outwc_p((val),(port)) : \
+       __outw_p((val),(port)))
+
+#define inw_p(port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+       __inwc_p(port) : \
+       __inw_p(port))
+
+#define outl(val,port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+       __outlc((val),(port)) : \
+       __outl((val),(port)))
+
+#define inl(port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+       __inlc(port) : \
+       __inl(port))
+
+#define outl_p(val,port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+       __outlc_p((val),(port)) : \
+       __outl_p((val),(port)))
+
+#define inl_p(port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+       __inlc_p(port) : \
+       __inl_p(port))
+
+#endif
diff --git a/linux-asm-string.h b/linux-asm-string.h
new file mode 100644 (file)
index 0000000..e5ccc0e
--- /dev/null
@@ -0,0 +1,289 @@
+/*
+ * Taken from Linux /usr/include/asm/string.h
+ * All except memcpy, memmove, memset and memcmp removed.
+ */
+
+#ifndef        _I386_STRING_H_
+#define _I386_STRING_H_
+
+/*
+ * This string-include defines all string functions as inline
+ * functions. Use gcc. It also assumes ds=es=data space, this should be
+ * normal. Most of the string-functions are rather heavily hand-optimized,
+ * see especially strtok,strstr,str[c]spn. They should work, but are not
+ * very easy to understand. Everything is done entirely within the register
+ * set, making the functions fast and clean. String instructions have been
+ * used through-out, making for "slightly" unclear code :-)
+ *
+ *             NO Copyright (C) 1991, 1992 Linus Torvalds,
+ *             consider these trivial functions to be PD.
+ */
+
+extern void *__memcpy(void * to, const void * from, size_t n);
+extern void *__constant_memcpy(void * to, const void * from, size_t n);
+extern void *memmove(void * dest,const void * src, size_t n);
+extern void *__memset_generic(void * s, char c,size_t count);
+extern void *__constant_c_memset(void * s, unsigned long c, size_t count);
+extern void *__constant_c_and_count_memset(void * s, unsigned long pattern, size_t count);
+
+
+extern inline void * __memcpy(void * to, const void * from, size_t n)
+{
+int d0, d1, d2;
+__asm__ __volatile__(
+       "cld\n\t"
+       "rep ; movsl\n\t"
+       "testb $2,%b4\n\t"
+       "je 1f\n\t"
+       "movsw\n"
+       "1:\ttestb $1,%b4\n\t"
+       "je 2f\n\t"
+       "movsb\n"
+       "2:"
+       : "=&c" (d0), "=&D" (d1), "=&S" (d2)
+       :"0" (n/4), "q" (n),"1" ((long) to),"2" ((long) from)
+       : "memory");
+return (to);
+}
+
+/*
+ * This looks horribly ugly, but the compiler can optimize it totally,
+ * as the count is constant.
+ */
+extern inline void * __constant_memcpy(void * to, const void * from, size_t n)
+{
+       switch (n) {
+               case 0:
+                       return to;
+               case 1:
+                       *(unsigned char *)to = *(const unsigned char *)from;
+                       return to;
+               case 2:
+                       *(unsigned short *)to = *(const unsigned short *)from;
+                       return to;
+               case 3:
+                       *(unsigned short *)to = *(const unsigned short *)from;
+                       *(2+(unsigned char *)to) = *(2+(const unsigned char *)from);
+                       return to;
+               case 4:
+                       *(unsigned long *)to = *(const unsigned long *)from;
+                       return to;
+               case 6: /* for Ethernet addresses */
+                       *(unsigned long *)to = *(const unsigned long *)from;
+                       *(2+(unsigned short *)to) = *(2+(const unsigned short *)from);
+                       return to;
+               case 8:
+                       *(unsigned long *)to = *(const unsigned long *)from;
+                       *(1+(unsigned long *)to) = *(1+(const unsigned long *)from);
+                       return to;
+               case 12:
+                       *(unsigned long *)to = *(const unsigned long *)from;
+                       *(1+(unsigned long *)to) = *(1+(const unsigned long *)from);
+                       *(2+(unsigned long *)to) = *(2+(const unsigned long *)from);
+                       return to;
+               case 16:
+                       *(unsigned long *)to = *(const unsigned long *)from;
+                       *(1+(unsigned long *)to) = *(1+(const unsigned long *)from);
+                       *(2+(unsigned long *)to) = *(2+(const unsigned long *)from);
+                       *(3+(unsigned long *)to) = *(3+(const unsigned long *)from);
+                       return to;
+               case 20:
+                       *(unsigned long *)to = *(const unsigned long *)from;
+                       *(1+(unsigned long *)to) = *(1+(const unsigned long *)from);
+                       *(2+(unsigned long *)to) = *(2+(const unsigned long *)from);
+                       *(3+(unsigned long *)to) = *(3+(const unsigned long *)from);
+                       *(4+(unsigned long *)to) = *(4+(const unsigned long *)from);
+                       return to;
+       }
+#define COMMON(x) \
+__asm__ __volatile__( \
+       "cld\n\t" \
+       "rep ; movsl" \
+       x \
+       : "=&c" (d0), "=&D" (d1), "=&S" (d2) \
+       : "0" (n/4),"1" ((long) to),"2" ((long) from) \
+       : "memory");
+{
+       int d0, d1, d2;
+       switch (n % 4) {
+               case 0: COMMON(""); return to;
+               case 1: COMMON("\n\tmovsb"); return to;
+               case 2: COMMON("\n\tmovsw"); return to;
+               default: COMMON("\n\tmovsw\n\tmovsb"); return to;
+       }
+}
+
+#undef COMMON
+}
+
+#define __HAVE_ARCH_MEMCPY
+#define memcpy(t, f, n) \
+(__builtin_constant_p(n) ? \
+ __constant_memcpy((t),(f),(n)) : \
+ __memcpy((t),(f),(n)))
+
+#define __HAVE_ARCH_MEMMOVE
+extern inline void * memmove(void * dest,const void * src, size_t n)
+{
+int d0, d1, d2;
+if (dest<src)
+__asm__ __volatile__(
+       "cld\n\t"
+       "rep\n\t"
+       "movsb"
+       : "=&c" (d0), "=&S" (d1), "=&D" (d2)
+       :"0" (n),"1" (src),"2" (dest)
+       : "memory");
+else
+__asm__ __volatile__(
+       "std\n\t"
+       "rep\n\t"
+       "movsb\n\t"
+       "cld"
+       : "=&c" (d0), "=&S" (d1), "=&D" (d2)
+       :"0" (n),
+        "1" (n-1+(const char *)src),
+        "2" (n-1+(char *)dest)
+       :"memory");
+return dest;
+}
+
+#define memcmp __builtin_memcmp
+
+extern inline void * __memset_generic(void * s, char c,size_t count)
+{
+int d0, d1;
+__asm__ __volatile__(
+       "cld\n\t"
+       "rep\n\t"
+       "stosb"
+       : "=&c" (d0), "=&D" (d1)
+       :"a" (c),"1" (s),"0" (count)
+       :"memory");
+return s;
+}
+
+/* we might want to write optimized versions of these later */
+#define __constant_count_memset(s,c,count) __memset_generic((s),(c),(count))
+
+/*
+ * memset(x,0,y) is a reasonably common thing to do, so we want to fill
+ * things 32 bits at a time even when we don't know the size of the
+ * area at compile-time..
+ */
+extern inline void * __constant_c_memset(void * s, unsigned long c, size_t count)
+{
+int d0, d1;
+__asm__ __volatile__(
+       "cld\n\t"
+       "rep ; stosl\n\t"
+       "testb $2,%b3\n\t"
+       "je 1f\n\t"
+       "stosw\n"
+       "1:\ttestb $1,%b3\n\t"
+       "je 2f\n\t"
+       "stosb\n"
+       "2:"
+       : "=&c" (d0), "=&D" (d1)
+       :"a" (c), "q" (count), "0" (count/4), "1" ((long) s)
+       :"memory");
+return (s);
+}
+
+/*
+ * This looks horribly ugly, but the compiler can optimize it totally,
+ * as we by now know that both pattern and count is constant..
+ */
+extern inline void * __constant_c_and_count_memset(void * s, unsigned long pattern, size_t count)
+{
+       switch (count) {
+               case 0:
+                       return s;
+               case 1:
+                       *(unsigned char *)s = pattern;
+                       return s;
+               case 2:
+                       *(unsigned short *)s = pattern;
+                       return s;
+               case 3:
+                       *(unsigned short *)s = pattern;
+                       *(2+(unsigned char *)s) = pattern;
+                       return s;
+               case 4:
+                       *(unsigned long *)s = pattern;
+                       return s;
+       }
+#define COMMON(x) \
+__asm__  __volatile__("cld\n\t" \
+       "rep ; stosl" \
+       x \
+       : "=&c" (d0), "=&D" (d1) \
+       : "a" (pattern),"0" (count/4),"1" ((long) s) \
+       : "memory")
+{
+       int d0, d1;
+       switch (count % 4) {
+               case 0: COMMON(""); return s;
+               case 1: COMMON("\n\tstosb"); return s;
+               case 2: COMMON("\n\tstosw"); return s;
+               default: COMMON("\n\tstosw\n\tstosb"); return s;
+       }
+}
+
+#undef COMMON
+}
+
+#define __constant_c_x_memset(s, c, count) \
+(__builtin_constant_p(count) ? \
+ __constant_c_and_count_memset((s),(c),(count)) : \
+ __constant_c_memset((s),(c),(count)))
+
+#define __memset(s, c, count) \
+(__builtin_constant_p(count) ? \
+ __constant_count_memset((s),(c),(count)) : \
+ __memset_generic((s),(c),(count)))
+
+#define __HAVE_ARCH_MEMSET
+#define memset(s, c, count) \
+(__builtin_constant_p(c) ? \
+ __constant_c_x_memset((s),(c),(count)) : \
+ __memset((s),(c),(count)))
+
+#define __HAVE_ARCH_STRNCMP
+static inline int strncmp(const char * cs,const char * ct,size_t count)
+{
+register int __res;
+int d0, d1, d2;
+__asm__ __volatile__(
+       "1:\tdecl %3\n\t"
+       "js 2f\n\t"
+       "lodsb\n\t"
+       "scasb\n\t"
+       "jne 3f\n\t"
+       "testb %%al,%%al\n\t"
+       "jne 1b\n"
+       "2:\txorl %%eax,%%eax\n\t"
+       "jmp 4f\n"
+       "3:\tsbbl %%eax,%%eax\n\t"
+       "orb $1,%%al\n"
+       "4:"
+                    :"=a" (__res), "=&S" (d0), "=&D" (d1), "=&c" (d2)
+                    :"1" (cs),"2" (ct),"3" (count));
+return __res;
+}
+
+#define __HAVE_ARCH_STRLEN
+static inline size_t strlen(const char * s)
+{
+int d0;
+register int __res;
+__asm__ __volatile__(
+       "repne\n\t"
+       "scasb\n\t"
+       "notl %0\n\t"
+       "decl %0"
+       :"=c" (__res), "=&D" (d0) :"1" (s),"a" (0), "0" (0xffffffff));
+return __res;
+}
+
+#endif
diff --git a/md5.c b/md5.c
new file mode 100644 (file)
index 0000000..c0d9960
--- /dev/null
+++ b/md5.c
@@ -0,0 +1,122 @@
+#ifdef PASSWD
+
+/*
+ * This is an implementation of the "MD5 Message Digest Algorithm" as
+ * described in
+ *   Bruce Schneier 'Applied Cryptography', pages 436-441, 2nd Edition, 1996,
+ *     John Wiley & Sons, Inc., ISBN 0-471-11709-9.
+ * The MD5 algorithm has been invented by Ron Rivest/"RSA Data Security, Inc.";
+ * Bruce Schneier refers to RFC1321 as the original publication.
+ *
+ * This implementation is copyright 1997 by M. Gutschke.
+ *
+ * This implementation has been optimized for size rather than for speed;
+ * there are a few assumptions that require a little-endian architecture.
+ * The maximum input length is 4GB.
+ */
+
+#include "stddef.h"
+#include "string.h"
+#include "etherboot.h"
+
+static unsigned long md5_buf[16];
+static unsigned long ABCD[4]={0x67452301L,0xefcdab89L,0x98badcfeL,0x10325476L};
+static unsigned long md5_len = 0;
+
+static unsigned long F(unsigned long X,unsigned long Y,unsigned long Z)
+{ return((X&Y)|((~X)&Z)); }
+
+static unsigned long G(unsigned long X,unsigned long Y,unsigned long Z)
+{ return((X&Z)|(Y&(~Z))); }
+
+static unsigned long H(unsigned long X,unsigned long Y,unsigned long Z)
+{ return(X^Y^Z); }
+
+static unsigned long I(unsigned long X,unsigned long Y,unsigned long Z)
+{ return(Y^(X|(~Z))); }
+
+static inline unsigned long rotate(unsigned long i,int s)
+{ return((i << s) | (i >> (32-s))); }
+
+static unsigned long ff(unsigned long (*fnc)(unsigned long,unsigned long,
+                                            unsigned long),
+                       unsigned long a,unsigned long b,unsigned long c,
+                       unsigned long d,unsigned long M,int s,
+                       unsigned long t)
+{ return(b+(rotate((a+fnc(b,c,d)+M+t),s))); }
+
+static void md5_loop(void)
+{
+  static unsigned char shifts[4][4] = {
+    {22,17,12,7},{20,14,9,5},{23,16,11,4},{21,15,10,6}};
+  static unsigned char idx[64] = {
+    0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,
+    1, 6,11, 0, 5,10,15, 4, 9,14, 3, 8,13, 2, 7,12,
+    5, 8,11,14, 1, 4, 7,10,13, 0, 3, 6, 9,12,15, 2,
+    0, 7,14, 5,12, 3,10, 1, 8,15, 6,13, 4,11, 2, 9 };
+  static unsigned long masks[64] = {
+    0xd76aa478L,0xe8c7b756L,0x242070dbL,0xc1bdceeeL,
+    0xf57c0fafL,0x4787c62aL,0xa8304613L,0xfd469501L,
+    0x698098d8L,0x8b44f7afL,0xffff5bb1L,0x895cd7beL,
+    0x6b901122L,0xfd987193L,0xa679438eL,0x49b40821L,
+    0xf61e2562L,0xc040b340L,0x265e5a51L,0xe9b6c7aaL,
+    0xd62f105dL,0x02441453L,0xd8a1e681L,0xe7d3fbc8L,
+    0x21e1cde6L,0xc33707d6L,0xf4d50d87L,0x455a14edL,
+    0xa9e3e905L,0xfcefa3f8L,0x676f02d9L,0x8d2a4c8aL,
+    0xfffa3942L,0x8771f681L,0x6d9d6122L,0xfde5380cL,
+    0xa4beea44L,0x4bdecfa9L,0xf6bb4b60L,0xbebfbc70L,
+    0x289b7ec6L,0xeaa127faL,0xd4ef3085L,0x04881d05L,
+    0xd9d4d039L,0xe6db99e5L,0x1fa27cf8L,0xc4ac5665L,
+    0xf4292244L,0x432aff97L,0xab9423a7L,0xfc93a039L,
+    0x655b59c3L,0x8f0ccc92L,0xffeff47dL,0x85845dd1L,
+    0x6fa87e4fL,0xfe2ce6e0L,0xa3014314L,0x4e0811a1L,
+    0xf7537e82L,0xbd3af235L,0x2ad7d2bbL,0xeb86d391L};
+
+  static unsigned long (*fncs[4])(unsigned long,unsigned long,
+                                 unsigned long) = { F,G,H,I };
+  int i,j,k,l;
+  unsigned long abcd[4];
+
+  memcpy(abcd, ABCD, 16);
+  for (i = j = 0; j < 4; j++)    /* rounds FF..II    */
+    for (k = 0; k < 4; k++)      /* 0..3             */
+      for (l = 4; l--; i++)      /* a,b,c,d..b,c,d,a */
+       abcd[(l+1)&3] = ff(fncs[j],
+                          abcd[(l+1)&3],abcd[(l+2)&3],abcd[(l+3)&3],abcd[l],
+                          md5_buf[idx[i]],shifts[j][l],masks[i]);
+  for (i = 4; i--; )
+    ABCD[i] += abcd[i];
+  return;
+}
+
+void md5_put(unsigned int ch)
+{
+  /* this code assumes a little endian architecture! */
+  ((unsigned char *)md5_buf)[md5_len%64] = ch;
+  if (((++md5_len)%64) == 0)
+    md5_loop();
+  return;
+}
+
+void md5_done(unsigned char *buf)
+{
+  unsigned long len = md5_len;
+  int           i;
+
+  /* this code assumes a little endian architecture! */
+  md5_put(0x80);
+  while ((md5_len%64) != 56)
+    md5_put(0);
+  md5_put(len <<  3); md5_put(len >>  5);
+  md5_put(len >> 13); md5_put(len >> 21);
+  for (i = 4; i--; )
+    md5_put(0);
+  memcpy(buf, ABCD, 16);
+  md5_len = 0;
+  {static unsigned long init[4]={
+    0x67452301L,0xefcdab89L,0x98badcfeL,0x10325476L};
+  memcpy(ABCD, init, 16); }
+  return;
+}
+
+#endif
diff --git a/md5.h b/md5.h
new file mode 100644 (file)
index 0000000..34ff609
--- /dev/null
+++ b/md5.h
@@ -0,0 +1,2 @@
+extern void md5_done(unsigned char *);
+extern void md5_put(unsigned int);
diff --git a/memsizes.c b/memsizes.c
new file mode 100644 (file)
index 0000000..ed69ca3
--- /dev/null
@@ -0,0 +1,27 @@
+#include       "etherboot.h"
+
+/* Taken from Etherboot */
+/* by Eric Biederman */
+
+extern unsigned int memsize P((void));
+extern unsigned short basememsize P((void));
+extern int meme820(struct e820entry *buf, int count);
+
+struct meminfo meminfo;
+
+void get_memsizes(void)
+{
+       int i;
+       meminfo.basememsize = basememsize();
+       meminfo.memsize = memsize();
+       meminfo.map_count = meme820(meminfo.map, E820MAX);
+       if (meminfo.map_count == 0) {
+               /* If we don't have an e820 memory map fake it */
+               meminfo.map_count = 2;
+               meminfo.map[0].size = meminfo.basememsize << 10;
+               meminfo.map[0].type = E820_RAM;
+               meminfo.map[1].addr = 1024*1024;
+               meminfo.map[1].size = meminfo.memsize << 10;
+               meminfo.map[1].type = E820_RAM;
+       }
+}
diff --git a/menu-nfl.eg b/menu-nfl.eg
new file mode 100644 (file)
index 0000000..3cd7ce6
--- /dev/null
@@ -0,0 +1,2 @@
+eppgm4-fdos.nb
+tomsrtbt-2.0.103.nb
diff --git a/menu-simple.c b/menu-simple.c
new file mode 100644 (file)
index 0000000..bb3a6da
--- /dev/null
@@ -0,0 +1,270 @@
+#include       "stddef.h"
+#include       "string.h"
+#include       "linux-asm-io.h"
+#include       "string.h"
+#include       "etherboot.h"
+#include       "elf_boot.h"
+
+/*
+ * 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, or (at
+ * your option) any later version.
+ */
+
+/*
+
+This is an example program which shows how the extension routine
+feature in Etherboot 5.0 works.
+
+This program presents a list of valid boot images from a data segment
+that is loaded into another area of memory, and prompts the user for a
+number, and modifies the bootp record to the filename to be loaded.  You
+can make the menu program as elaborate as you like, the sky is the
+limit.
+
+Ideally, there should be a menu generation program that takes a
+high-level description of menus and valid inputs and creates a data
+file to be loaded to the data area. The menu program should agree with
+the menu generator on the layout of the data area.
+
+This program is linked to run at 0x60000, and expects to find config
+data at 0x80000. This means the code can be up to 128kB long.
+
+When the program starts it receives 3 parameters from Etherboot:
+
+Pointer to ebinfo structure
+Pointer to image header structure (either a tagged or ELF image header)
+Pointer to bootp/DHCP reply obtained by Etherboot from bootpd or DHCPD
+
+Etherboot expects this program to return an int. The values have these
+meanings:
+
+<0     Do not use
+0      Same as 1, for implementation reasons
+1      Redo tftp with possibly modified bootp record
+2      Redo bootp and tftp
+255    Exit Etherboot
+
+Observe that this program causes Etherboot to load a different program
+next by modifying the contents of the filename field in the bootp record
+and then returning 1. It can also send parameters to the next program by
+modifying tag 129 in the bootp record. This is how the menu system
+works.
+
+The data segment that this particular program expects is of the form
+
+       choice 1\nchoice 2\nchoice 3\n\0
+
+where the \n are newlines and the \0 the teminating zero byte. Therefore
+you can create this file with a Unix text editor (but see next
+paragraph). choice 1, etc are the pathnames or filenames of the next
+file to load by TFTP. If the string starts with / then it's assumed to
+be a pathname and the whole of the bootp filename area is replaced by
+it, otherwise just the filename portion of the pathname, i.e. the same
+directory as the menu file is assumed.
+
+This program also illustrates the use of a timeout to select a default
+item by using currticks() to obtain the value of the BIOS clock and
+console_ischar to determine if a character has been typed at the
+keyboard.
+
+Commentary: This program is just to illustrate a very simple menu
+system.  There are known bugs:
+
+mknbi-menu/mkelf-menu does not add the ending NUL byte, but this is
+present due to the NUL fill to the next block boundary. If the size of
+the data goes exactly up to a block boundary, then it is possible there
+will be a spurious final item that goes up the next NUL byte in memory.
+
+Another bug is that there is no overflow checking when writing into
+bootp->bp_file.
+
+Yet another bug is that there is no facility to correct input entry on
+lines. getline() should be smarter.
+
+*/
+
+/*
+
+Memory layout assumed by mknbi and this program
+
+0x60000-0x7FFFF    128 kB      Menu program
+0x80000-0x8FFFF    64 kB       Menu data (initial)
+
+*/
+
+#define        TIMEOUT         10                      /* seconds */
+#define        MENU_DATA       ((char *)0x80000)
+
+static char    *items[10];
+
+extern void printf(const char *, ...);
+extern void ansi_putc(unsigned int);
+extern int console_getc(void);
+extern int console_ischar(void);
+extern unsigned long currticks(void);
+
+void putchar(int c)
+{
+       if (c == '\n')
+               ansi_putc('\r');
+       ansi_putc(c);
+}
+
+int getchar(void)
+{
+       int     c;
+
+       c = console_getc();
+       if (c == '\r')
+               c = '\n';
+       return (c);
+}
+
+/*
+ *     Get a line, ignore characters after array limit reached.
+ *     Echo the character if the flag says so.
+ */
+int getline(char *line, int length, int echo)
+{
+       int     c;
+       char    *p = line;
+
+       while ((c = getchar()) != '\n') {
+               if (p < &line[length-1]) {      /* within array? */
+                       if (echo)
+                               ansi_putc(c);
+                       *p++ = c;
+               }
+       }
+       *p++ = '\0';
+       if (echo)
+               ansi_putc('\n');
+       return (line - p);
+}
+
+int scan_items(void)
+{
+       int             i;
+       char            *p;
+
+       for (i = 1, p = MENU_DATA; i < 10 && *p != '\0'; i++) {
+               items[i] = p;
+               while (*p != '\0' && *p != '\n')
+                       p++;
+               if (*p == '\n')
+                       *p++ = '\0';
+       }
+       return (i);
+}
+
+/*
+ *     Find the location of the last / of the filename in the bootp
+ *     pathname, and return the next location, where the filename
+ *     starts. If no / exists, return the beginning of input string.
+ */
+char *locate_file(char *pathname)
+{
+       char            *p;
+
+       if ((p = strrchr(pathname, '/')) == 0)
+               return (pathname);
+       return (p + 1);
+}
+
+/*
+ *     Return an index from 1..last-1 if valid character, else 0
+ *     If timeout after 10 seconds occurs, return -1.
+ */
+int get_index(int last)
+{
+       int             i;
+       char            line[2];
+       unsigned long   now, timeout;
+
+       timeout = currticks() + TIMEOUT * TICKS_PER_SEC;
+       while ((now = currticks()) < timeout && !console_ischar())
+               ;
+       if (now >= timeout)
+               return (-1);
+       getline(line, sizeof(line), 1);
+       i = line[0];
+       if (i >= '1' && i <= '0' + last - 1)
+               return (i - '0');
+       return (0);
+}
+
+static void parse_elf_boot_notes(
+       void *notes, union infoblock **rheader, struct bootp_t **rbootp)
+{
+       unsigned char *note, *end;
+       Elf_Bhdr *bhdr;
+       Elf_Nhdr *hdr;
+
+       bhdr = notes;
+       if (bhdr->b_signature != ELF_BHDR_MAGIC) {
+               return;
+       }
+
+       note = ((char *)bhdr) + sizeof(*bhdr);
+       end  = ((char *)bhdr) + bhdr->b_size;
+       while (note < end) {
+               unsigned char *n_name, *n_desc, *next;
+               hdr = (Elf_Nhdr *)note;
+               n_name = note + sizeof(*hdr);
+               n_desc = n_name + ((hdr->n_namesz + 3) & ~3);
+               next = n_desc + ((hdr->n_descsz + 3) & ~3);
+               if (next > end) 
+                       break;
+#if 0
+               printf("n_type: %x n_name(%d): n_desc(%d): \n", 
+                       hdr->n_type, hdr->n_namesz, hdr->n_descsz);
+#endif
+
+               if ((hdr->n_namesz == 10) &&
+                       (memcmp(n_name, "Etherboot", 10) == 0)) {
+                       switch(hdr->n_type) {
+                       case EB_BOOTP_DATA:
+                               *rbootp = *((void **)n_desc);
+                               break;
+                       case EB_HEADER:
+                               *rheader = *((void **)n_desc);
+                               break;
+                       default:
+                               break;
+                       }
+               }
+               note = next;
+       }
+}
+
+int menu(struct ebinfo *eb, union infoblock *header, struct bootp_t *bootp)
+{
+       int             i, nitems;
+       char            *path, *file;   /* place to insert filename */
+
+       parse_elf_boot_notes(eb, &header, &bootp);
+       path = bootp->bp_file;
+       file = locate_file(path);
+       nitems = scan_items();
+       printf("\033[J\033[34mEtherboot menu system called from Etherboot %d.%d\033[37m\n\n", eb->major, eb->minor);
+       printf("Available images:\n\n");
+       for (i = 1; i < nitems; ++i)
+               printf("%d. %s\n", i, items[i]);
+       printf("\n");
+       do {
+               printf("Make a selection (timeout %d seconds => 1): ", TIMEOUT);
+               i = get_index(nitems);
+       } while (i == 0);
+       if (i == -1) {
+               ansi_putc('1');
+               ansi_putc('\n');
+               i = 1;          /* pick the first one if timeout */
+       }
+       if (*items[i] == '/')   /* absolute path? overwrite pathname */
+               strcpy(path, items[i]);
+       else                    /* use directory of current pathname */
+               strcpy(file, items[i]);
+       return (0);
+}
diff --git a/menu-simple.eg b/menu-simple.eg
new file mode 100644 (file)
index 0000000..67e6378
--- /dev/null
@@ -0,0 +1,2 @@
+floppyfw.nb
+burner.nb
diff --git a/menu-simple.h b/menu-simple.h
new file mode 100644 (file)
index 0000000..ceb25e7
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * 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, or (at
+ * your option) any later version.
+ */
+
+/*
+ *     Definitions of data structures generated by the menu compiler
+ *     and expected by the menu program. All offsets are from the
+ *     beginning of the data area.
+ */
+
+/*
+ *     Structure describing the data area, starts at 0.
+ */
+struct menu_header
+{
+       char            major, minor;   /* Versions */
+       unsigned short  flags;          /* Capabilities */
+       unsigned int    timeout;        /* Global timeout */
+       unsigned int    selectprompt;   /* Offset of string saying Select... */
+       unsigned int    confirmprompt;  /* Offset of string saying Confirm... */
+       unsigned int    nmenus;         /* Number of menus stored */
+       /* Here follow unsigned int offsets of menus */
+};
+
+/*
+ *     Structure describing one menu. The number of items is one
+ *     greater than the last valid index. The 0th item holds the
+ *     data to be displayed before any user input.
+ */
+struct menu
+{
+       unsigned int    timeout;        /* Timeout for this menu */
+       unsigned int    nitems;         /* Items in this menu */
+       /* Here follow unsigned int offsets of items */
+};
+
+/*
+ *     Structure describing one item in a menu.
+ */
+struct item
+{
+       unsigned int    title;
+};
diff --git a/menu.c b/menu.c
new file mode 100644 (file)
index 0000000..4c0b066
--- /dev/null
+++ b/menu.c
@@ -0,0 +1,210 @@
+#include       "stddef.h"
+#include       "string.h"
+#include       "printf.h"
+#include       "ansiesc.h"
+#include       "misc.h"
+#include       "linux-asm-io.h"
+#include       "etherboot.h"
+#include       "elf_boot.h"
+#include       "bootmenu.h"
+
+/*
+ * 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, or (at
+ * your option) any later version.
+ */
+
+/*
+
+This is an example program which shows how the extension routine
+feature in Etherboot 5.0 works.
+
+This program is linked to run at 0x60000, and expects to find config
+data if any at 0x80000. This means the code can be up to 128kB long.
+
+When the program starts it receives 3 parameters from Etherboot:
+
+Pointer to ebinfo structure
+Pointer to image header structure (either a tagged or ELF image header)
+Pointer to bootp/DHCP reply obtained by Etherboot from bootpd or DHCPD
+
+Etherboot expects this program to return an int. The values have these
+meanings:
+
+<0     Do not use
+0      Same as 1, for implementation reasons
+1      Redo tftp with possibly modified bootp record
+2      Redo bootp and tftp
+255    Exit Etherboot
+
+Observe that this program causes Etherboot to load a different program
+next by modifying the contents of the filename field in the bootp record
+and then returning 1. It can also send parameters to the next program by
+modifying tag 129 in the bootp record.
+
+*/
+
+/*
+
+Memory layout assumed by mknbi and this program
+
+0x60000-0x7FFFF    128 kB      Menu program
+0x80000-0x8FFFF    64 kB       Menu data (initial)
+
+*/
+
+static unsigned char   *vendortags;
+static unsigned char   *end_of_rfc1533;
+#ifdef IMAGE_FREEBSD
+       /* yes this is a pain FreeBSD uses this for swap, however,
+          there are cases when you don't want swap and then
+          you want this set to get the extra features so lets
+          just set if dealing with FreeBSD.  I haven't run into
+          any troubles with this but I have without it
+       */
+static int vendorext_is_valid = 1;
+#else
+static int vendorext_is_valid = 0;
+#endif
+static unsigned char   *motd[RFC1533_VENDOR_NUMOFMOTD] = { 0 };
+static unsigned char   *imagelist[RFC1533_VENDOR_NUMOFIMG] = { 0 };
+
+static inline void checkvendor(void)
+{
+       union {
+               unsigned long   l;
+               unsigned char   c[4];
+       } u;
+
+        memcpy(u.c, vendortags, sizeof(u));
+        if (u.l == RFC_1048 || u.l == VEND_CMU || u.l == VEND_STAN)
+                vendortags += 4;
+        else
+                vendortags = 0;
+}
+
+static void parsebootp()
+{
+       unsigned char           *p;
+       unsigned int            c;
+       static unsigned char    vendorext_magic[] = {0xE4,0x45,0x74,0x68}; /* äEth */
+
+       memset(motd, 0, sizeof(motd));
+       memset(imagelist, 0, sizeof(imagelist));
+       if (vendortags == 0)
+               return;
+       for (p = vendortags; (c = *p) != RFC1533_END; ) {
+               if (c == RFC1533_PAD) {
+                       p++;
+                       continue;
+               }
+#if    DEBUG > 1
+               printf("Tag %d\n", c);
+#endif
+               switch (c) {
+               case RFC1533_VENDOR_MAGIC:
+                       if (TAG_LEN(p) >= 6
+                               && !memcmp(p+2, vendorext_magic, 4)
+                               && p[6] == RFC1533_VENDOR_MAJOR)
+                               vendorext_is_valid = 1;
+                       break;
+               case RFC1533_VENDOR_MENUOPTS:
+                       parse_menuopts(p+2, TAG_LEN(p));
+                       break;
+               default:
+                       if (c >= RFC1533_VENDOR_MOTD && c < RFC1533_VENDOR_MOTD + RFC1533_VENDOR_NUMOFMOTD)
+                               motd[c-RFC1533_VENDOR_MOTD] = p;
+                       else if (c >= RFC1533_VENDOR_IMG && c < RFC1533_VENDOR_IMG + RFC1533_VENDOR_NUMOFIMG) 
+                               imagelist[c-RFC1533_VENDOR_IMG] = p;
+                       break;
+               }
+               p += p[1] + 2;
+       }
+       end_of_rfc1533 = p;
+}
+
+static void parse_elf_boot_notes(
+       void *notes, union infoblock **rheader, struct bootpd_t **rbootp)
+{
+       unsigned char *note, *end;
+       Elf_Bhdr *bhdr;
+       Elf_Nhdr *hdr;
+
+       bhdr = notes;
+       if (bhdr->b_signature != ELF_BHDR_MAGIC) {
+               return;
+       }
+
+       note = ((char *)bhdr) + sizeof(*bhdr);
+       end  = ((char *)bhdr) + bhdr->b_size;
+       while (note < end) {
+               unsigned char *n_name, *n_desc, *next;
+               hdr = (Elf_Nhdr *)note;
+               n_name = note + sizeof(*hdr);
+               n_desc = n_name + ((hdr->n_namesz + 3) & ~3);
+               next = n_desc + ((hdr->n_descsz + 3) & ~3);
+               if (next > end) 
+                       break;
+#if 0
+               printf("n_type: %x n_name(%d): n_desc(%d): \n", 
+                       hdr->n_type, hdr->n_namesz, hdr->n_descsz);
+#endif
+
+               if ((hdr->n_namesz == 10) &&
+                       (memcmp(n_name, "Etherboot", 10) == 0)) {
+                       switch(hdr->n_type) {
+                       case EB_BOOTP_DATA:
+                               *rbootp = *((void **)n_desc);
+                               break;
+                       case EB_HEADER:
+                               *rheader = *((void **)n_desc);
+                               break;
+                       default:
+                               break;
+                       }
+               }
+               note = next;
+       }
+}
+
+int menu(struct ebinfo *eb, union infoblock *header, struct bootpd_t *bootp)
+{
+       int             i;
+       extern int      serial_init(void);
+       extern void     serial_fini(void);
+
+#ifdef DEBUG
+       printf(MKNBI_VERSION "\n");
+#endif
+       parse_elf_boot_notes(eb, &header, &bootp);
+       /* Sanity check */
+       if (header->img.magic != ELF_MAGIC && header->img.magic != TAG_MAGIC) {
+               printf("Bad argument passed from Etherboot\n");
+               return (255);
+       }
+       vendortags = (unsigned char *)bootp->bootp_reply.bp_vend;
+       checkvendor();
+       parsebootp();
+       if (!vendorext_is_valid) {
+               printf("No menu vendor tags found, returning to Etherboot\n");
+               sleep(10);
+               return(2);
+       }
+#if defined(CONSOLE_SERIAL) || defined(CONSOLE_DUAL)
+       serial_init();
+#endif
+#ifdef ANSIESC
+       ansi_reset();
+#endif
+       show_motd(motd);
+       i = selectImage(bootp, imagelist, end_of_rfc1533);
+       if (i == 2) {
+               printf("No selection, returning to Etherboot\n");
+               sleep(10);
+       }
+#if defined(CONSOLE_SERIAL) || defined(CONSOLE_DUAL)
+       serial_fini();
+#endif
+       return (i);
+}
diff --git a/menuc.pl b/menuc.pl
new file mode 100755 (executable)
index 0000000..f62a4c8
--- /dev/null
+++ b/menuc.pl
@@ -0,0 +1,113 @@
+#!/usr/bin/perl -w
+
+# Program to compile a menu description into a binary file
+# Placed under GNU Public License by Ken Yap, January 2001
+
+# This Perl program requires Parse::RecDescent
+# If you cannot install this module in the standard directory, then
+# install RecDescent.pm in a directory that you can in and
+# edit the line below to include this directory in the search path.
+#
+# BEGIN {
+#      push(@INC, '/usr/local/lib/mknbi');
+# }
+
+use strict;
+use Getopt::Long;
+use Socket;
+
+use Parse::RecDescent;
+
+use constant;
+use constant DEBUG => 0;
+use constant MAXINPUT => 102400;
+
+use vars qw($output $status $text $grammar $parser $result);
+
+$grammar = q(
+       specs:          spec(s?) eofile
+               { $return = 1 }
+       spec:           global_option | menu | <error>
+       global_option:  timeout
+       timeout:        'timeout' number ';'
+       number:         'number'
+       menu:           'menu' name '{' menu_body '}'
+       name:           'name'
+       menu_body:      'title' strings ';'
+                       | menu_option
+                       | items
+       strings:        string(s)
+       string:         <perl_quotelike>
+       menu_option:    timeout
+       items:          item(s)
+       item:           'item'
+       eofile:         /^\Z/
+);
+
+$::RD_HINT = 1;
+
+GetOptions('output=s' => \$output);
+
+# If an output file is specified, redirect stdout to it
+if (defined($output)) {
+       die "$output: $!\n" unless open(STDOUT, ">$output");
+}
+binmode(STDOUT);
+
+# If an input file is specified, redirect stdin from it
+if ($#ARGV >= 0) {
+       die "$ARGV[0]: $!\n" unless open(STDIN, "$ARGV[0]");
+}
+
+print "Menuc doesn't work yet\n";
+
+$parser = new Parse::RecDescent($grammar);
+
+# Read up to the maximum number of bytes allowed in a specification
+$status = read(STDIN, $text, MAXINPUT);
+die "read: $!\n" unless defined($status);
+
+$result = $parser->specs($text);
+unless (defined($result)) {
+       print "Syntax error in specification\n";
+}
+__END__
+
+=head1 NAME
+
+menuc - compile a menu description into a binary file
+
+=head1 SYNOPSIS
+
+B<menuc> [--output=I<outputfile>] [I<inputfile>]
+
+=head1 DESCRIPTION
+
+B<menuc> compiles menu descriptions to data file suitable for
+interpretation by an Etherboot menu extension program.
+
+B<--output=>I<outputfile> Specify the output file, can be used with
+all variants.  Stdout is the default.
+
+The package must be installed in the destination location before the
+executables can be run, because it looks for library files.
+
+=head1 BUGS
+
+Please report all bugs to the author.
+
+=head1 SEE ALSO
+
+Etherboot tutorial at C<http://etherboot.sourceforge.net/>
+
+=head1 COPYRIGHT
+
+B<menuc> is under the GNU Public License
+
+=head1 AUTHOR
+
+Ken Yap (C<ken_yap@users.sourceforge.net>)
+
+=head1 DATE
+
+Version 0.9 January 2001
diff --git a/misc.c b/misc.c
new file mode 100644 (file)
index 0000000..91520a4
--- /dev/null
+++ b/misc.c
@@ -0,0 +1,96 @@
+#include       "ansiesc.h"
+#include       "etherboot.h"
+#include       "misc.h"
+
+/**************************************************************************
+SLEEP
+**************************************************************************/
+void sleep(int secs)
+{
+       unsigned long tmo;
+
+       for (tmo = currticks()+secs*TICKS_PER_SEC; currticks() < tmo; )
+               /* Nothing */;
+}
+
+int getdec(unsigned char **ptr)
+{
+       char *p = *ptr;
+       int ret=0;
+       if ((*p < '0') || (*p > '9')) return(-1);
+       while ((*p >= '0') && (*p <= '9')) {
+               ret = ret*10 + (*p - '0');
+               p++;
+       }
+       *ptr = p;
+       return(ret);
+}
+
+void
+putchar(int c)
+{
+#ifndef        ANSIESC
+       if (c == '\n')
+               putchar('\r');
+#endif
+
+#ifdef CONSOLE_CRT
+#ifdef ANSIESC
+       ansi_putc(c);
+#else
+       console_putc(c);
+#endif
+#endif
+#ifdef CONSOLE_SERIAL
+#ifdef ANSIESC
+       if (c == '\n')
+               serial_putc('\r');
+#endif
+       serial_putc(c);
+#endif
+}
+
+/**************************************************************************
+GETCHAR - Read the next character from input device WITHOUT ECHO
+**************************************************************************/
+int getchar(void)
+{
+       int c = 256;
+
+       do {
+#ifdef POWERSAVE
+               /* Doze for a while (until the next interrupt).  This works
+                * fine, because the keyboard is interrupt-driven, and the
+                * timer interrupt (approx. every 50msec) takes care of the
+                * serial port, which is read by polling.  This reduces the
+                * power dissipation of a modern CPU considerably, and also
+                * makes Etherboot waiting for user interaction waste a lot
+                * less CPU time in a VMware session.  */
+               cpu_nap();
+#endif /* POWERSAVE */
+#ifdef CONSOLE_CRT
+               if (console_ischar())
+                       c = console_getc();
+#endif
+#ifdef CONSOLE_SERIAL
+               if (serial_ischar())
+                       c = serial_getc();
+#endif
+       } while (c==256);
+       if (c == '\r')
+               c = '\n';
+       return c;
+}
+
+int iskey(void)
+{
+#ifdef CONSOLE_CRT
+       if (console_ischar())
+               return 1;
+#endif
+#ifdef CONSOLE_SERIAL
+       if (serial_ischar())
+               return 1;
+#endif
+       return 0;
+}
diff --git a/misc.h b/misc.h
new file mode 100644 (file)
index 0000000..3a17a9a
--- /dev/null
+++ b/misc.h
@@ -0,0 +1,5 @@
+extern void sleep(int secs);
+extern int getdec(unsigned char **ptr);
+extern void putchar(int c);
+extern int getchar(void);
+extern int iskey(void);
diff --git a/mklnim b/mklnim
new file mode 100755 (executable)
index 0000000..256c718
--- /dev/null
+++ b/mklnim
@@ -0,0 +1,48 @@
+#!/bin/sh
+#
+#      Make Linux Netinstall Image
+#      Takes a RedHat, TurboLinux or SuSE CD
+#      and creates a network bootable image
+#
+#      mklnim outputfile [path-to-cdrom]
+#
+#      path-to-cdrom defaults to /mnt/cdrom
+#
+if [ $# -lt 1 ]
+then
+       echo Usage: $0 outputfile [path-to-cdrom]
+       exit 1
+fi
+pathtocdrom=${2:-"/mnt/cdrom"}
+trap '' INT
+tmpmount=/tmp/lnim.$$
+mkdir $tmpmount
+if [ -r $pathtocdrom/images/cdboot.img ]
+then
+       bootfloppyimage="$pathtocdrom/images/cdboot.img"
+       echo TurboLinux CDROM
+elif [ -r $pathtocdrom/images/bootnet.img ]
+then
+       bootfloppyimage="$pathtocdrom/images/bootnet.img"
+       append='--append=network'
+       echo RedHat 6.x CDROM
+elif [ -r $pathtocdrom/images/boot.img ]
+then
+       bootfloppyimage="$pathtocdrom/images/boot.img"
+       echo RedHat 5.2 CDROM
+elif [ -r $pathtocdrom/disks/bootdisk ]
+then
+       bootfloppyimage="$pathtocdrom/disks/bootdisk"
+       linuximage=linux
+       initdisk=initdisk.gz
+       echo SuSE 6.x CDROM
+else
+       echo Cannot find either TurboLinux, RedHat or SuSE CD
+       trap INT
+       exit 1
+fi
+mount -t msdos -o loop,ro $bootfloppyimage $tmpmount
+mknbi-linux $append --output=$1 $tmpmount/${linuximage:-vmlinuz} $tmpmount/${initdisk:-initrd.img}
+umount $tmpmount
+rmdir $tmpmount
+trap INT
diff --git a/mklnim.1 b/mklnim.1
new file mode 100644 (file)
index 0000000..4bba2b7
--- /dev/null
+++ b/mklnim.1
@@ -0,0 +1,45 @@
+.TH MKLNIM 1 "25 April 2000"
+.SH NAME
+mklnim \- make Linux Netinstall Image
+.SH SYNOPSIS
+.B mklnim
+outputfile
+[\fBpath-to-cdrom\fR]
+.SH DESCRIPTION
+.I mklnim
+is a shell script that takes a SuSE, TurboLinux or a RedHat CDROM, or
+equivalent disk directory, and creates a network bootable image (NBI)
+that can be used with Etherboot (http://etherboot.sourceforge.net/)
+or Netboot (http://www.han.de/~gero/netboot.html).
+This NBI, when booted via the network, will make the target computer
+behave just as if a CDROM boot (TurboLinux), or a floppy boot (RedHat
+and SuSE) had been selected.  A conventional install can be done from
+this point onwards.
+.LP
+There are several occasions when this technique is useful: 1. It
+can be used to quickly boot a target computer when the floppy
+loading is very slow. 2. In the case of TurboLinux, it loads
+the CDROM initial ramdisk which does not require any further
+floppy loading. In the case of RedHat, it only loads the floppy
+initial ramdisk which does not contain the material in the
+supplementary floppy, and may require more floppy insertion.
+3. It can start the install from a floppy of any size, not just
+1.4 MB, or even from a floppyless machine, if one has a boot
+ROM (providing no further floppy access is required).
+4. It could be used as part of an automatic installation process.
+.LP
+Naturally, all this assumes that the infrastructure for diskless
+booting (bootp and tftp servers) has been set up.
+.SH BUGS
+If supplementary floppies are required, this script doesn't include
+that material in the network boot image. Please feel welcome to fix
+this problem.
+.SH "SEE ALSO"
+Etherboot tutorial at http://etherboot.sourceforge.net/
+.SH COPYRIGHT
+.I mklnim
+is under the GNU Public License
+.SH AUTHOR
+Ken Yap (ken_yap@users.sourceforge.net)
+.SH DATE
+Version 0.4 April 2000
diff --git a/mknbi.pl b/mknbi.pl
new file mode 100644 (file)
index 0000000..ad02c56
--- /dev/null
+++ b/mknbi.pl
@@ -0,0 +1,1044 @@
+#!/usr/bin/perl -w
+
+# Program to create a netboot image for ROM/FreeDOS/DOS/Linux
+# Placed under GNU Public License by Ken Yap, December 2000
+
+BEGIN {
+       push(@INC, '@@LIBDIR@@');
+}
+
+use strict;
+use Getopt::Long;
+use Fcntl qw(SEEK_SET);
+use Socket;
+
+use TruncFD;
+use Nbi;
+use Elf;
+
+use constant;
+use constant DEBUG => 0;
+
+use vars qw($libdir $version $format $target $output $module $relocseg $relocsegstr
+       $progreturns $param $append $rootdir $rootmode $ip $ramdisk $rdbase
+       $simhd $dishd $squashfd $first32);
+
+sub check_file
+{
+       my ($f, $status);
+
+       $status = 1;
+       foreach $f (@_) {
+               if (!-e $f) {
+                       print STDERR "$f: file not found\n";
+                       $status = 0;
+               } elsif (!-f $f) {
+                       print STDERR "$f: not a plain file\n";
+                       $status = 0;
+               } elsif (!-r $f) {
+                       print STDERR "$f: file not readable\n";
+                       $status = 0;
+               }
+       }
+       return ($status);
+}
+
+sub mknbi_rom ($)
+{
+       my ($format) = @_;
+       my ($romdesc);
+
+       $#ARGV >= 0 or die "Usage: $0 romimage\n";
+       return unless check_file($ARGV[0]);
+       $format->add_header("mknbi-rom-$version", $relocseg + 0x3E0, 0x6000, 6);
+       $romdesc = { file => $ARGV[0],
+               segment => 0x6000,
+               maxlen => 0x10000,
+               id => 16,
+               end => 1 };
+       $format->add_segment($romdesc);
+       $format->dump_segments();
+       $format->copy_file($romdesc);
+}
+
+sub inet_aton_warn
+{
+       my ($ip);
+
+       print STDERR "Warning: $_[0] cannot be resolved to an IP address\n" unless defined($ip = inet_aton($_[0]));
+       return ($ip);
+}
+
+sub resolve_names
+{
+       my ($i);
+
+       my ($client, $server, $gateway, $netmask, $hostname) = split(/:/, $_[0], 5);
+       unless (defined($hostname)) {
+               print STDERR "$_[0]: invalid specification\n";
+               return ($_[0]);
+       }
+       $client = inet_ntoa($i) if defined($i = &inet_aton_warn($client));
+       $server = inet_ntoa($i) if defined($i = &inet_aton_warn($server));
+       $gateway = inet_ntoa($i) if defined($i = &inet_aton_warn($gateway));
+       return (join(':', $client, $server, $gateway, $netmask, $hostname));
+}
+
+sub make_paramstring ($)
+{
+       my ($paramsize) = @_;
+       my ($string, $nfsroot);
+
+       # --param= overrides everything
+       return ($param) if (defined($param));
+       # String substitute various options, should do sanity checks also
+       if (!defined($rootdir)) {
+               $rootdir = '/dev/nfs';
+       } elsif ($rootdir !~ m(^/dev/)) {
+               $nfsroot = $rootdir;
+               undef($nfsroot) if ($nfsroot eq 'kernel');
+               $rootdir = '/dev/nfs';
+       }
+       if (defined($ip)) {
+               if ($ip eq 'kernel') {
+                       undef($ip);
+               } elsif ($ip !~ /^(rom|off|none|on|any|dhcp|bootp|rarp|both)$/) {
+                       $ip = &resolve_names($ip);
+               }
+       } elsif (!defined($ramdisk)) {
+               print STDERR "Warning: The --ip option was not used; you may need it if you use NFSroot.\n\tPlease see the documentation.\n";
+       }
+       die "Ramdisk mode should be one of: top asis 0xNNNNNNNN (hex address)\n"
+               if (defined($rdbase) and $rdbase !~ /^(top|asis|0x[\da-fA-F]{1,8})$/);
+       # If rootmode is set, then check if it's rw or ro, and if so, use it
+       if (defined($rootmode) and $rootmode !~ /^(rw|ro)$/) {
+               die "-rootmode should be either rw or ro\n";
+               undef($rootmode);
+       }
+       $string = defined($rootmode) ? $rootmode : 'rw';
+       $string .= " root=$rootdir";
+       $string .= " nfsroot=$nfsroot" if (defined($nfsroot));
+       $string .= " ip=$ip" if (defined($ip));
+       $string .= " rdbase=$rdbase" if (defined($rdbase));
+       $string .= " $append" if (defined($append));
+       return ($string);
+}
+
+use constant HEADER_SEG_OFFSET => 0x220;       # in units of 16 bytes
+use constant START_OFFSET => 0x280;            # in units of 16 bytes
+use constant START_MAX_LENGTH => 6144;
+use constant PARAM_SEG_OFFSET => 0x240;                # in units of 16 bytes
+use constant PARAM_MAX_LENGTH => 1024;
+
+sub mknbi_linux ($)
+{
+       my ($format) = @_;
+       my ($startaddr, $setupfile, $setupfile32, $libfile, $kernelfile, $setupdesc);
+       my ($paramseg, $paramstring, $bootseg, $block);
+       my ($setupseg, $kernelseg, $kernellen, $ramdiskseg, $rdloc);
+       my ($setupsects, $flags, $syssize, $swapdev,
+               $ramsize, $vidmode, $rootdev, $sig, $ver, $bigker);
+
+       $startaddr = sprintf("%#x", ($relocseg + START_OFFSET) * 0x10);
+       $libfile = ($main::format eq 'elf') ? "first32elf\@${startaddr}.linux" : "first32\@${startaddr}.linux";
+       # if empty, use default
+       $setupfile = $first32 eq '' ? "$libdir/$libfile" : $first32;
+       $#ARGV >= 0 or die "Usage: $0 kernelimage [ramdisk]\n";
+       $kernelfile = $ARGV[0];
+       return unless check_file($setupfile, $kernelfile);
+       if (defined($ramdisk = $ARGV[1])) {
+               return unless check_file($ramdisk);
+       }
+       $format->add_pm_header("mknbi-linux-$version", $relocseg + HEADER_SEG_OFFSET, hex($startaddr), $progreturns);
+       $setupdesc = { file => $setupfile,
+               segment => hex($startaddr) / 0x10,
+               maxlen => START_MAX_LENGTH,
+               id => 16 };
+       $paramstring = &make_paramstring(PARAM_MAX_LENGTH);
+       $paramseg = { string => $paramstring,
+               segment => $relocseg + PARAM_SEG_OFFSET,
+               maxlen => 2048,
+               id => 17 };
+       $bootseg = { file => $kernelfile,
+               segment => $relocseg + 0x0,
+               len => 512,
+               maxlen => 512,
+               id => 18 };
+       $format->peek_file($bootseg, \$block, 512) == 512
+               or die "Error reading boot sector of $kernelfile\n";
+       (undef, $setupsects, $flags, $syssize, $swapdev, $ramsize, $vidmode,
+               $rootdev, $sig) = unpack('a497Cv7', $block);
+       if ($sig != 0xAA55) {
+               print STDERR "$kernelfile: not a Linux kernel image\n";
+               return;
+       }
+       print STDERR 'setupsects flags syssize swapdev ramsize vidmode rootdev sig', "\n" if (DEBUG);
+       print STDERR "$setupsects $flags $syssize $swapdev $ramsize $vidmode $rootdev $sig\n" if (DEBUG);
+       $setupseg = { file => $kernelfile,
+               segment => $relocseg + 0x20,
+               fromoff => 512,
+               len => $setupsects * 512,
+               maxlen => 8192,
+               id => 19 };
+       $format->peek_file($setupseg, \$block, 512) == 512
+               or die "Error reading first setup sector of $kernelfile\n";
+       (undef, $sig, $ver, undef, undef, undef, undef, undef, $flags) =
+               unpack('va4v5C2', $block);
+       print STDERR 'sig ver flags', "\n" if (DEBUG);
+       print STDERR "$sig $ver $flags\n" if (DEBUG);
+       if ($sig ne 'HdrS' or $ver < 0x201) {
+               print STDERR "$kernelfile: not a Linux kernel image\n";
+               return;
+       }
+       $bigker = ($flags & 0x1);
+       $kernelseg = { file => $ARGV[0],
+               segment => $bigker ? 0x10000 : 0x1000,
+               maxlen => $bigker ? undef : 1024 * 512, 
+               fromoff => $setupsects * 512 + 512,
+               id => 20,
+               end => 1 };
+       $ramdiskseg = { file => $ramdisk,
+               segment => 0x10000,
+               align => 4096,
+               id => 21,
+               end => 1 };
+       $$kernelseg{'end'} = 0 if (defined($ramdisk));
+       $format->add_segment($setupdesc);
+       $format->add_segment($paramseg);
+       $format->add_segment($bootseg);
+       $format->add_segment($setupseg);
+       $kernellen = $format->add_segment($kernelseg);
+       if (!$bigker and $kernellen > (($relocseg - 0x1000) * 16)) {
+               print STDERR "Warning, zImage kernel may collide with Etherboot\n";
+       }
+       # Put ramdisk following kernel at next 4096 byte boundary
+       $$ramdiskseg{'segment'} += (($kernellen + 0xFFF) & ~0xFFF) >> 4 if ($bigker);
+       # should be 0, 1 or 2 depending on rdbase
+       $format->add_segment($ramdiskseg, "\x00") if (defined($ramdisk));
+       $format->dump_segments();
+       $format->copy_file($setupdesc);
+       $format->copy_string($paramseg);
+       $format->copy_file($bootseg);
+       $format->copy_file($setupseg);
+       $format->copy_file($kernelseg);
+       $format->copy_file($ramdiskseg) if (defined($ramdisk));
+}
+
+sub get_geom ($$)
+{
+       my ($file, $block) = @_;
+       my ($usedsize, $declsize, $firsttracksize, $geom_string, $fstype);
+       my ($secttot, $secttrk, $heads, $bootid, $sig, $cyltot);
+
+       ($usedsize = $squashfd ? &TruncFD::truncfd($file) : -s $file) > 0
+               or die "Error reading $file\n";
+       (undef, $secttot, undef, $secttrk, $heads, undef, $bootid, undef,
+               $fstype, $sig) = unpack('a19va3vva8Ca17a5@510a2', $$block);
+       print STDERR "Warning, this doesn't appear to be a DOS boot sector\n"
+               if ($sig ne "\x55\xAA");
+       if ($simhd) {
+               # change MediaDescriptor
+               substr($$block, 0x15, 1) = "\xF8";
+               # change HiddenSectors
+               substr($$block, 0x1c, 4) = pack('V', $secttrk * $heads);
+               # change the boot drive
+               substr($$block, 0x24, 1) = "\x80";
+       }
+       $cyltot = $secttot / ($secttrk * $heads);
+       $declsize = $secttot * 512;
+       $firsttracksize = $secttrk * $heads * 512;
+       print STDERR "Warning, used size $usedsize is greater than declared size $declsize\n"
+               if ($usedsize > $declsize);
+       $geom_string = pack('v3C2', $secttot, $secttrk, $cyltot, $simhd ? 0x80 : 0, $dishd);
+       return ($usedsize, $declsize, $firsttracksize, $geom_string, $fstype);
+}
+
+sub mod_geom_string ($)
+{
+       my ($geom_string) = @_;
+       my ($secttot, $secttrk, $cyltot, $simhd, $dishd) = unpack('v3C2', $geom_string);
+       $cyltot++;      # for partition table
+       return (pack('v3C2', $secttot, $secttrk, $cyltot, $simhd, $dishd));
+}
+
+sub encode_chs ($$$)
+{
+       my ($c, $h, $s) = @_;
+
+       $s = ($s & 0x3F) | (($c & 0x300) >> 2);
+       $c &= 0xFF;
+       return ($h, $s, $c);
+}
+
+sub make_mbr ($$)
+{
+       my ($geom_string, $fstype) = @_;
+       my ($heads, $bootsect);
+       my ($secttot, $secttrk, $cyltot, $simhd, $x) = unpack('v3C2', $geom_string);
+
+       $cyltot--;
+       # $cyltot was incremented in mod_geom_string
+       $heads = $secttot / ($secttrk * $cyltot);
+       # bootsect is first sector of track 1
+       $bootsect = $secttrk * $heads;
+       # CHS stupidity:
+       # cylinders is 0 based, heads is 0 based, but sectors is 1 based
+       # 0x01 for FAT12, 0x04 for FAT16
+       return (pack('@446C8V2@510v', 0x80, &encode_chs(1, 0, 1),
+               $fstype eq 'FAT12' ? 0x01 : 0x04, &encode_chs($cyltot, $heads - 1, $secttrk),
+               $bootsect, $secttot, 0xAA55));
+}
+
+sub mknbi_fdos ($)
+{
+       my ($format) = @_;
+       my ($setupfile, $bootblock);
+       my ($usedsize, $declsize, $firsttracksize, $geom_string, $fstype);
+       my ($setupdesc, $kerneldesc, $firsttrackdesc, $bootdesc, $floppydesc);
+
+       $setupfile = "$libdir/first.fdos";
+       $#ARGV >= 1 or die "Usage: $0 kernel.sys floppyimage\n";
+       return unless check_file($setupfile, $ARGV[0], $ARGV[1]);
+       $format->add_header("mknbi-fdos-$version", $relocseg + 0x200, $relocseg + 0x300, 0);
+       $setupdesc = { file => $setupfile,
+               segment => $relocseg + 0x300,
+               maxlen => 4096,
+               id => 16 };
+       $kerneldesc = { file => $ARGV[0],
+               segment => @@FDKSEG@@,
+               id => 17 };
+       die "Ramdisk base should be of the form 0xNNNNNNNN (linear hex address)\n"
+               if (defined($rdbase) and $rdbase !~ /^0x[\da-fA-F]{1,8}$/);
+       $floppydesc = { file => $ARGV[1],
+               segment => (defined($rdbase) ? (hex($rdbase) >> 4) : 0x11000),
+               id => 18,
+               end => 1 };
+       $format->add_segment($setupdesc);
+       $format->add_segment($kerneldesc);
+       $format->peek_file($floppydesc, \$bootblock, 512) == 512
+               or die "Error reading boot sector of $ARGV[1]\n";
+       ($usedsize, $declsize, $firsttracksize, $geom_string, $fstype)
+               = &get_geom($ARGV[1], \$bootblock);
+       $firsttrackdesc = { align => $firsttracksize };
+       $$floppydesc{'fromoff'} = 512;
+       $$floppydesc{'len'} = $usedsize;
+       $$floppydesc{'len'} += $firsttracksize if $simhd;
+       $$floppydesc{'maxlen'} = $declsize;
+       $geom_string = &mod_geom_string($geom_string) if $simhd;
+       $format->add_segment($floppydesc, $geom_string);
+       $format->dump_segments();
+       $format->copy_file($setupdesc);
+       $format->copy_file($kerneldesc);
+       if ($simhd) {
+               $$firsttrackdesc{'string'} = &make_mbr($geom_string, $fstype);
+               $format->copy_string($firsttrackdesc);
+       }
+       # write out modified bootblock, not the one in the file
+       $bootdesc = { string => $bootblock };
+       $format->copy_string($bootdesc);
+       # Restore correct value of len and account for bootblock skipped
+       $$floppydesc{'len'} = $usedsize - 512;
+       $format->copy_file($floppydesc);
+}
+
+sub mknbi_dos ($)
+{
+       my ($format) = @_;
+       my ($setupfile, $bootblock);
+       my ($usedsize, $declsize, $firsttracksize, $geom_string, $fstype);
+       my ($setupdesc, $firsttrackdesc, $bootdesc, $floppydesc);
+
+       $setupfile = "$libdir/first.dos";
+       $#ARGV >= 0 or die "Usage: $0 floppyimage\n";
+       return unless check_file($setupfile, $ARGV[0]);
+       $format->add_header("mknbi-dos-$version", 0x1000, 0x1040, 0);
+       $setupdesc = { file => $setupfile,
+               segment => 0x1040,
+               maxlen => 64512,
+               id => 16 };
+       die "Ramdisk base should be of the form 0xNNNNNNNN (linear hex address)\n"
+               if (defined($rdbase) and $rdbase !~ /^0x[\da-fA-F]{1,8}$/);
+       $floppydesc = { file => $ARGV[0],
+               segment => (defined($rdbase) ? (hex($rdbase) >> 4) : 0x11000),
+               id => 17,
+               end => 1 };
+       $format->add_segment($setupdesc);
+       $format->peek_file($floppydesc, \$bootblock, 512) == 512
+               or die "Error reading boot sector of $ARGV[0]\n";
+       ($usedsize, $declsize, $firsttracksize, $geom_string, $fstype)
+               = &get_geom($ARGV[0], \$bootblock);
+       $firsttrackdesc = { align => $firsttracksize };
+       $$floppydesc{'fromoff'} = 512;
+       $$floppydesc{'len'} = $usedsize;
+       $$floppydesc{'len'} += $firsttracksize if $simhd;
+       $$floppydesc{'maxlen'} = $declsize;
+       $geom_string = &mod_geom_string($geom_string) if $simhd;
+       $format->add_segment($floppydesc, $geom_string);
+       $format->dump_segments();
+       $format->copy_file($setupdesc);
+       if ($simhd) {
+               $$firsttrackdesc{'string'} = &make_mbr($geom_string, $fstype);
+               $format->copy_string($firsttrackdesc);
+       }
+       # write out modified bootblock, not the one in the file
+       $bootdesc = { string => $bootblock };
+       $format->copy_string($bootdesc);
+       # Restore correct value of len and account for bootblock skipped
+       $$floppydesc{'len'} = $usedsize - 512;
+       $format->copy_file($floppydesc);
+}
+
+sub mknbi_menu ($)
+{
+       my ($module) = @_;
+       my ($menudesc, $datadesc);
+
+       $#ARGV >= -1 or die "Usage: mk$format-menu [menudata]\n";
+       print STDERR "Warning: mk$format-menu requires Etherboot 5.0 or later\n";
+       return unless check_file("$libdir/menu");
+       # $progreturns == 1
+       $module->add_pm_header("mknbi-menu-$version", $relocseg + 0x0, 0x60000, 1);
+       $menudesc = { file => "$libdir/menu",
+               segment => 0x6000,
+               maxlen => 0x20000,
+               id => 16 };
+       $module->add_segment($menudesc);
+       if ($#ARGV >= 0) {
+               return unless check_file($ARGV[0]);
+               $datadesc = { file => $ARGV[0],
+                       segment => 0x8000,
+                       maxlen => 0x10000,
+                       id => 17,
+                       end => 1 };
+               $module->add_segment($datadesc);
+       } else {
+               $$menudesc{'end'} = 1;
+       }
+       $module->dump_segments();
+       $module->copy_file($menudesc);
+       $module->copy_file($datadesc) if ($#ARGV >= 0);
+}
+
+sub mknbi_nfl ($)
+{
+       my ($module) = @_;
+       my ($menudesc, $datadesc);
+
+       $#ARGV >= -1 or die "Usage: mk$format-nfl [menudata]\n";
+       print STDERR "Warning: mk$format-nfl requires Etherboot 5.0 or later\n";
+       return unless check_file("$libdir/nfl");
+       # $progreturns == 1
+       $module->add_pm_header("mknbi-nfl-$version", $relocseg + 0x0, 0x60000, 1);
+       $menudesc = { file => "$libdir/nfl",
+               segment => 0x6000,
+               maxlen => 0x20000,
+               id => 16 };
+       $module->add_segment($menudesc);
+       if ($#ARGV >= 0) {
+               return unless check_file($ARGV[0]);
+               $datadesc = { file => $ARGV[0],
+                       segment => 0x8000,
+                       maxlen => 0x10000,
+                       id => 17,
+                       end => 1 };
+               $module->add_segment($datadesc);
+       } else {
+               $$menudesc{'end'} = 1;
+       }
+       $module->dump_segments();
+       $module->copy_file($menudesc);
+       $module->copy_file($datadesc) if ($#ARGV >= 0);
+}
+
+sub mkelf_lua ($)
+{
+       my ($module) = @_;
+       my ($menudesc, $datadesc);
+
+       $format eq 'elf' or die "Only ELF images are catered for\n";
+       $#ARGV >= -1 or die "Usage: mkelf-lua [luaprog]\n";
+       print STDERR "Warning: mkelf-lua requires Etherboot 5.0 or later\n";
+       return unless check_file("$libdir/lua");
+       # $progreturns == 1
+       $module->add_pm_header("mkelf-lua-$version", $relocseg + 0x0, 0x60000, 1);
+       $menudesc = { file => "$libdir/lua",
+               segment => 0x6000,
+               maxlen => 0x20000,
+               id => 16 };
+       $module->add_segment($menudesc);
+       if ($#ARGV >= 0) {
+               return unless check_file($ARGV[0]);
+               $datadesc = { file => $ARGV[0],
+                       segment => 0x8000,
+                       maxlen => 0x10000,
+                       id => 17,
+                       end => 1 };
+               $module->add_segment($datadesc);
+       } else {
+               $$menudesc{'end'} = 1;
+       }
+       $module->dump_segments();
+       $module->copy_file($menudesc);
+       $module->copy_file($datadesc) if ($#ARGV >= 0);
+}
+
+$libdir = '@@LIBDIR@@';                # where config and auxiliary files are stored
+
+$version = '@@VERSION@@';
+$simhd = 0;
+$dishd = 0;
+$squashfd = 1;
+$relocsegstr = '0x9000';
+$progreturns = 0;
+GetOptions('format=s' => \$format,
+       'target=s' => \$target,
+       'output=s' => \$output,
+       'param=s' => \$param,
+       'append=s' => \$append,
+       'rootdir=s' => \$rootdir,
+       'rootmode=s' => \$rootmode,
+       'ip=s' => \$ip,
+       'rdbase=s' => \$rdbase,
+       'harddisk!' => \$simhd,
+       'disableharddisk!' => \$dishd,
+       'squash!' => \$squashfd,
+       'first32:s' => \$first32,
+       'progreturns!' => \$progreturns,
+       'relocseg=s' => \$relocsegstr);
+
+$0 =~ /mk([a-z]*)-([a-z]+)$/ and ($format = $1, $target = $2);
+if (!defined($format)) {
+       print STDERR "No format specified with program name or --format=\n";
+       exit 1;
+}
+if (!defined($target)) {
+       print STDERR "No target specified with program name or --target=\n";
+       exit 1;
+}
+if (defined($output)) {
+       die "$output: $!\n" unless open(STDOUT, ">$output");
+}
+binmode(STDOUT);
+
+if ($format eq 'nbi') {
+       $first32 = '' if !defined($first32);
+       $module = Nbi->new($libdir);
+} elsif ($format eq 'elf') {
+       $first32 = '' if !defined($first32);
+       $module = Elf->new($libdir);
+       die "Output must be file\n" unless (seek(STDOUT, 0, SEEK_SET));
+} else {
+       die "Format $format not supported\n";
+}
+if ($relocsegstr eq '0x9000' or $relocsegstr eq '0x8000') {
+       $relocseg = hex($relocsegstr);
+} else {
+       print STDERR "relocseg must be 0x9000 or 0x8000 only, setting to 0x9000\n";
+       $relocseg = 0x9000;
+}
+if ($target eq 'rom') {
+       &mknbi_rom($module);
+} elsif ($target eq 'linux') {
+       &mknbi_linux($module);
+} elsif ($target eq 'fdos') {
+       if ($simhd and $dishd) {
+               print STDERR "Warning: --harddisk and --disableharddisk are incompatible\n";
+       }
+       &mknbi_fdos($module);
+} elsif ($target eq 'dos') {
+       if ($simhd and $dishd) {
+               print STDERR "Warning: --harddisk and --disableharddisk are incompatible\n";
+       }
+       &mknbi_dos($module);
+} elsif ($target eq 'menu') {
+       &mknbi_menu($module);
+} elsif ($target eq 'nfl') {
+       &mknbi_nfl($module);
+} elsif ($target eq 'lua') {
+       &mkelf_lua($module);
+} else {
+       print STDERR "Target $target not supported\n";
+       exit;
+}
+$module->finalise_image();
+close(STDOUT);
+exit 0;
+
+__END__
+
+=head1 NAME
+
+mknbi - make network bootable image
+
+=head1 SYNOPSIS
+
+B<mknbi> --format=I<format> --target=I<target> [--output=I<outputfile>] I<target-specific-arguments>
+
+B<mknbi-linux> [--output=I<outputfile>] I<kernelimage> [I<ramdisk>]
+
+B<mkelf-linux> [--output=I<outputfile>] I<kernelimage> [I<ramdisk>]
+
+B<mknbi-rom> [--output=I<outputfile>] I<ROM-image>
+
+B<mknbi-menu> [--output=I<outputfile>] [I<dataimage>]
+
+B<mkelf-menu> [--output=I<outputfile>] [I<dataimage>]
+
+B<mknbi-nfl> [--output=I<outputfile>] [I<dataimage>]
+
+B<mkelf-nfl> [--output=I<outputfile>] [I<dataimage>]
+
+B<mknbi-fdos> [--output=I<outputfile>] I<kernel.sys floppyimage>
+
+B<mknbi-dos> [--output=I<outputfile>] I<floppyimage>
+
+=head1 DESCRIPTION
+
+B<mknbi> is a program that makes network bootable images for various
+operating systems suitable for network loading by Etherboot or Netboot,
+which are ROM boot loaders.  If you are looking to boot using PXE, look
+no further, mknbi is not what you want. You probably want something like
+PXELINUX which is part of the SYSLINUX package.
+
+B<mknbi> can be invoked with the B<--format> and B<--target> options or
+links can be made to it under format and target specific names. E.g.
+mkelf-linux is the same as mknbi --format=elf --target=linux.
+
+B<--format>=I<format> Specify the format of the output. Currently
+available are nbi and elf.  ELF format only works with linux and menu.
+Otherwise the invocation is the same as for mknbi. In discussions below,
+the mknbi form is used.
+
+B<--target>=I<target> Specify the target binary. Currently available are
+linux, menu, rom, fdos and dos. B<mknbi> is not needed for booting
+FreeBSD.
+
+B<--output=>I<outputfile> Specify the output file, can be used with
+all variants.  Stdout is the default.
+
+The package must be installed in the destination location before the
+executables can be run, because it looks for library files.
+
+Each of the variants will be described separately.
+
+=head1 MKNBI-LINUX
+
+B<mknbi-linux> makes a tagged image from a Linux kernel image, either
+a zImage or a bzImage.
+
+=head1 MKNBI-LINUX OPTIONS
+
+B<--param=>I<string> Replace the default parameter string with the
+specified one. This option overrides all the following options so you
+should know what you are doing.
+
+B<--append>=I<string> Appends the specified string to the existing
+parameter string. This option operates after the other parameter options
+have been evaluated.
+
+B<--rootdir>=I<rootdir> Define name of directory to mount via NFS from
+the boot server.
+
+In the absence of this option, the default is to use the directory
+C</tftpboot/>I<%s>, with the I<%s> representing the hostname or
+IP-address of the booting system, depending on whether the hostname
+attribute is present in the BOOTP/DHCP reply.
+
+If C<rom> is given, and if the BOOTP/DHCP server is able to handle the RFC 1497
+extensions, the value of the rootpath option is used as the root directory.
+
+If the name given to the option starts with C</dev/>, the corresponding
+device is used as the root device, and no NFS directory will be mounted.
+
+B<--rootmode>=C<ro|rw> Defines whether the root device will be mounted
+read-only or read-write respectively. Without this parameter, the
+default is C<rw>.
+
+B<--ip=>I<string> Define client and server IP addresses.
+
+In the absence of this option no IP addresses are defined, and the
+kernel will determine the IP addresses by itself, usually by using DHCP,
+BOOTP or RARP.  Note that the kernel's query is I<in addition to> the
+query made by the bootrom, and requires the IP: kernel level
+autoconfiguration (CONFIG_IP_PNP) feature to be included in the kernel.
+
+Important note: In Linux kernels 2.2.x where x >= 18, and 2.4.x where x
+>= 5, it is B<necessary> to specify one of the enabling options in the
+next paragraph to cause the IP autoconfiguration to be activated.
+Unlike in previous kernels, IP autoconfiguration does not happen by
+default. Also note that IP autoconfiguration and NFSroot are likely to
+go away in Linux 2.6 and that userspace IP configuration methods using
+ramdisk and userspace DHCP daemons are preferred now.
+
+If one of the following: C<off, none, on, any, dhcp, bootp, rarp, both>,
+is given, then the option will be passed unmodified to the kernel and
+cause that autoconfig option to be chosen.
+
+If C<rom> is given as the argument to this option, all necessary IP
+addresses for NFS root mounting will be inherited from the BOOTP/DHCP
+answer the bootrom got from the server.
+
+It's also possible to define the addresses during compilation of the boot
+image. Then, all addresses must be separated by a colon, and ordered in
+the following way:
+
+C<--ip=>I<client:server:gateway:netmask:hostname[:dev[:proto]]>
+
+Using this option B<mknbi-linux> will automatically convert system names
+into decimal IP addresses for the first three entries in this string.
+The B<hostname> entry will be used by the kernel to set the host name of
+the booted Linux diskless client.  When more than one network interface
+is installed in the diskless client, it is possible to specify the name
+of the interface to use for mounting the root directory via NFS by
+giving the optional value C<dev>.  This entry has to start with the
+string C<eth> followed by a number from 0 to 9. However, if only one
+interface is installed in the client, this I<dev> entry including the
+preceding semicolon can be left out. The I<proto> argument is one of the
+IP autoconfiguration enabling options listed above.  (Author: it's not
+clear to me what the IP autoconfiguration does when the parameters are
+already specified.  Perhaps it's to obtain parameters not specified,
+e.g. NIS domain.)
+
+B<--rdbase=>I<top|asis|0xNNNNNNNN> Set the ramdisk load address.  C<top>
+moves the ramdisk to the top of memory before jumping to the kernel.
+This is the default if rdbase is not specified.  This option requires
+that first-linux's kernel sizing work correctly.  C<asis> loads it at
+0x100000 (1MB) if the kernel is loaded low; or leaves it just after the
+kernel in memory, if the kernel is loaded high. For this option to work,
+the kernel must be able to handle ramdisks at these addresses.
+I<0xNNNNNNNN> moves the ramdisk to the hex address specified. The onus
+is on the user to specify a suitable address that is acceptable to the
+kernel and doesn't overlap with any other segments. It will have to be
+aligned to a 4k byte boundary so you should ensure that this is so. (The
+last three hex digits must be 0.)
+
+B<--first32=>I<program> Override the default first stage setup
+program.  It can be used to call extensions to the Etherboot code, which
+paves the way for additional useful functionality without enlarging the
+size of the Etherboot footprint.  --first32 is implied by the ELF
+format.
+
+B<--progreturns> This option is used in conjunction with and only valid
+with the --first32 option to indicate to the Etherboot loader that the
+called program will return to loader and hence Etherboot should not
+disable the network device as is the case when the program will never
+return to Etherboot.
+
+B<--relocseg=>I<segaddr> This option is used to specify a relocation of
+the Linux first, boot, setup, and parameter segments to another 64k
+band.  Currently the only valid values are 0x9000 and 0x8000,
+corresponding to linear addresses of 0x90000 and 0x80000 upwards. The
+default is 0x9000.  Usually you use this option if you have relocated
+Etherboot to 0x84000 to avoid other code in the 0x90000 segment like
+DOC. The Linux kernel must support relocation which implies a 2.4 kernel
+or later. --relocseg only works reliably with ELF or --first32=.
+
+B<mem=>I<memsize> This is not a command line option but a kernel
+parameter that is intercepted by the first32 stage and used as the top
+of memory, to match Linux's interpretation. I<memsize> can be suffixed
+by C<G> to indicate gibibytes (times 2^30), C<M> to indicate mebibytes
+(times 2^20) or C<K> to indicate kibibytes (times 2^10). Note that the
+suffixes are uppercase. This kernel parameter can be specified in
+--append= or option-129 of the DHCP/BOOTP record.
+
+Run the program thus:
+
+C<mknbi-linux> I<kernel-image> [I<ramdisk-image>] > C<linux.nb>
+
+Then move F<linux.nb> to where the network booting process expects to
+find it.
+
+=head1 MKNBI-LINUX BOOTP/DHCP VENDOR TAGS
+
+B<mknbi-linux> includes a startup code at the beginning of the Linux
+kernel which is able to detect certain BOOTP vendor defined tags. These
+can be used to modify the kernel loading process at runtime. To use
+these tags with bootpd, a publicly available BOOTP server daemon, you
+can use the following syntax in the F</etc/bootptab> file:
+
+C<T>I<number>C<=">I<string>C<">
+
+For example, to specify a different root NFS device, you can use:
+
+C<T130="eth1">
+
+The following tags are presently supported by B<mknbi-linux>:
+
+B<129> The I<string> value given with this tag is appended verbatim to
+the end of the kernel command line.  It can be used to specify arguments
+like I/O addresses or DMA channels required for special hardware
+like SCSI adapters, network cards etc. Please consult the Linux kernel
+documentation about the syntax required by those options. It is the same
+as the B<--append> command line option to B<mknbi-linux>, but works at
+boot time instead of image build time.
+
+B<130> With this tag it is possible to the select the network adapter
+used for mounting root via NFS on a multihomed diskless client. The
+syntax for the I<string> value is the same as for the C<dev> entry used
+with the B<--ip=> option as described above. However note that the
+B<mknbi-linux> runtime setup routine does not check the syntax of the
+string.
+
+The same tags will work in DHCP with the appropriate syntax for your
+DHCP server configuration file.
+
+Remember that you need to specify tag 128 in the correct format in order
+for the previous tags to be valid. See the documentation file
+vendortags.
+
+=head1 MKNBI-ROM
+
+B<mknbi-rom> makes a tagged image from an Etherboot C<.rom> or C<.lzrom>
+boot ROM image.  This allows it to be netbooted using an existing
+ROM. This is useful for developing Etherboot drivers or to load a newer
+version of Etherboot with an older one.
+
+Run mknbi like this:
+
+C<mknbi-rom nic.lzrom> > C<nic.nb>
+
+Move F<nic.nb> to where the network booting process expects to find it.
+The boot ROM will load this as the I<operating system> and execute the
+ROM image.
+
+=head1 MKNBI-MENU
+
+B<mknbi-menu> and B<mkelf-menu> make a tagged or ELF image from an
+auxiliary menu program. Etherboot has the ability to load an auxiliary
+program which can interact with the user, modify the DHCP structure, and
+return a status.  Based on the status, Etherboot can load another
+binary, restart or exit.  This makes it possible to have elaborate user
+interface programs without having to modify Etherboot. The specification
+for auxiliary program is documented in the Etherboot Developer's Manual.
+
+B<mknbi-menu> and B<mkelf-menu> take a binary named C<menu> from the
+library directory, which is assumed to have an entry point of 0x60000.
+An optional argument is accepted, and this is loaded at 0x80000. This
+can be a data file used by the menu program.
+
+Currently, the menu binary provided duplicates the builtin menu facility
+of Etherboot with the exception of a couple of small differences: no
+server or gateway specifications are used and nested TFTP loads don't
+work. You should not have MOTD or IMAGE_MENU defined in your Etherboot
+build to be able to use this external menu binary. The specifications of
+the DHCP tags required is in the vendortags document in the Etherboot
+documentation.
+
+Typical usage is like this:
+
+C<mkelf-menu> > C<menu.nb>
+
+Then put menu.nb in the TFTP boot directory and edit your DHCP tags
+according to the documentation.
+
+Alternate user interface programs are highly encouraged.
+
+=head1 MKNBI-NFL
+
+B<mknbi-nfl> and B<mkelf-nfl> make a tagged or ELF image from the NFL
+menu program. This menu program takes the names of images from a
+menu-text-file file which just contains lines with the filenames
+(relative to the tftpd root directory) of images to load. The
+user-interface is a light-bar, similar to that used in GRUB.  There is a
+sample menu-text-file in C<menu-nfl.eg>.
+
+Typical usage is:
+
+C<mknbi-nfl> C<menu-text-file> > C<nfl.nb>
+
+Then put nfl.nb in the TFTP boot directory and specify as the boot
+image. Chaining to other menus works.