Adjust memory layout for 2.6.22+ kernels with 32KB setup code
[mknbi.git] / mknbi.pl
1 #!/usr/bin/perl -w
2
3 # Program to create a netboot image for ROM/FreeDOS/DOS/Linux
4 # Placed under GNU Public License by Ken Yap, December 2000
5
6 # 2003.04.28 R. Main
7 #  Tweaks to work with new first-dos.S for large disk images
8
9 BEGIN {
10         push(@INC, '@@LIBDIR@@');
11 }
12
13 use strict;
14 use Getopt::Long;
15 use IO::Seekable;
16 use Socket;
17
18 use TruncFD;
19 use Nbi;
20 use Elf;
21
22 use constant;
23 use constant DEBUG => 0;
24 use constant LUA_VERSION => 0x04000100; # 4.0.1.0
25
26 use bytes;
27
28 use vars qw($libdir $version $format $target $output $module $relocseg $relocsegstr
29         $progreturns $param $append $rootdir $rootmode $ip $ramdisk $rdbase $rdnopad
30         $simhd $dishd $squashfd $first32 $showversion);
31
32 sub check_file
33 {
34         my ($f, $status);
35
36         $status = 1;
37         foreach $f (@_) {
38                 if (!-e $f) {
39                         print STDERR "$f: file not found\n";
40                         $status = 0;
41                 } elsif (!-f $f) {
42                         print STDERR "$f: not a plain file\n";
43                         $status = 0;
44                 } elsif (!-r $f) {
45                         print STDERR "$f: file not readable\n";
46                         $status = 0;
47                 }
48         }
49         return ($status);
50 }
51
52 sub mknbi_rom ($)
53 {
54         my ($module) = @_;
55         my ($romdesc);
56
57         $#ARGV >= 0 or die "Usage: $0 romimage\n";
58         return unless check_file($ARGV[0]);
59         $module->add_header("mknbi-rom-$version", $relocseg + 0x3E0, 0x6000, 6);
60         $romdesc = { file => $ARGV[0],
61                 segment => 0x6000,
62                 maxlen => 0x10000,
63                 id => 16,
64                 end => 1 };
65         $module->add_segment($romdesc);
66         $module->dump_segments();
67         $module->copy_file($romdesc);
68 }
69
70 sub mkelf_img ($)
71 {
72         my ($module) = @_;
73         my ($romdesc);
74
75         $#ARGV >= 0 or die "Usage: $0 .img-file\n";
76         return unless check_file($ARGV[0]);
77         $module->add_pm_header("mkelf-img-$version", $relocseg + 0x3E0, 0x60000, 0);
78         $romdesc = { file => $ARGV[0],
79                 segment => 0x6000,
80                 maxlen => 0x10000,
81                 id => 16,
82                 end => 1 };
83         $module->add_segment($romdesc);
84         $module->dump_segments();
85         $module->copy_file($romdesc);
86 }
87
88 sub inet_aton_warn
89 {
90         my ($ip);
91
92         print STDERR "Warning: $_[0] cannot be resolved to an IP address\n" unless defined($ip = inet_aton($_[0]));
93         return ($ip);
94 }
95
96 sub resolve_names
97 {
98         my ($i);
99
100         my ($client, $server, $gateway, $netmask, $hostname) = split(/:/, $_[0], 5);
101         unless (defined($hostname)) {
102                 print STDERR "$_[0]: invalid specification\n";
103                 return ($_[0]);
104         }
105         $client = inet_ntoa($i) if defined($i = &inet_aton_warn($client));
106         $server = inet_ntoa($i) if defined($i = &inet_aton_warn($server));
107         $gateway = inet_ntoa($i) if defined($i = &inet_aton_warn($gateway));
108         return (join(':', $client, $server, $gateway, $netmask, $hostname));
109 }
110
111 sub make_paramstring ($)
112 {
113         my ($paramsize) = @_;
114         my ($string, $nfsroot);
115
116         # --param= overrides everything
117         return ($param) if (defined($param));
118         # String substitute various options, should do sanity checks also
119         if (!defined($rootdir)) {
120                 $rootdir = '/dev/nfs';
121         } elsif ($rootdir !~ m(^/dev/)) {
122                 $nfsroot = $rootdir;
123                 undef($nfsroot) if ($nfsroot eq 'kernel');
124                 $rootdir = '/dev/nfs';
125         }
126         if (defined($ip)) {
127                 if ($ip eq 'kernel') {
128                         undef($ip);
129                 } elsif ($ip !~ /^(rom|off|none|on|any|dhcp|bootp|rarp|both)$/) {
130                         $ip = &resolve_names($ip);
131                 }
132         } elsif (!defined($ramdisk)) {
133                 print STDERR "Warning: The --ip option was not used; you may need it if you use NFSroot.\n\tPlease see the documentation.\n";
134         }
135         die "Ramdisk mode should be one of: top asis 0xNNNNNNNN (hex address)\n"
136                 if (defined($rdbase) and $rdbase !~ /^(top|asis|0x[\da-fA-F]{1,8})$/);
137         # If rootmode is set, then check if it's rw or ro, and if so, use it
138         if (defined($rootmode) and $rootmode !~ /^(rw|ro)$/) {
139                 die "-rootmode should be either rw or ro\n";
140                 undef($rootmode);
141         }
142         $string = defined($rootmode) ? $rootmode : 'rw';
143         $string .= " root=$rootdir";
144         $string .= " nfsroot=$nfsroot" if (defined($nfsroot));
145         $string .= " ip=$ip" if (defined($ip));
146         $string .= " rdbase=$rdbase" if (defined($rdbase));
147         $string .= " $append" if (defined($append));
148         return ($string);
149 }
150
151 use constant HEADER_SEG_OFFSET => 0x820;        # in units of 16 bytes
152 use constant START_OFFSET => 0x880;             # in units of 16 bytes
153 use constant START_MAX_LENGTH => 6144;
154 use constant PARAM_SEG_OFFSET => 0x840;         # in units of 16 bytes
155 use constant PARAM_MAX_LENGTH => 1024;
156
157 sub mknbi_linux ($)
158 {
159         my ($module) = @_;
160         my ($startaddr, $setupfile, $setupfile32, $libfile, $kernelfile, $setupdesc);
161         my ($paramseg, $paramstring, $bootseg, $block);
162         my ($setupseg, $kernelseg, $kernellen, $ramdiskseg, $rdloc);
163         my ($setupsects, $flags, $syssize, $swapdev,
164                 $ramsize, $vidmode, $rootdev, $sig, $ver, $bigker);
165
166         $startaddr = sprintf("%#x", ($relocseg + START_OFFSET) * 0x10);
167         $libfile = ($format eq 'elf') ? "first32elf\@${startaddr}.linux" : "first32\@${startaddr}.linux";
168         # if empty, use default
169         $setupfile = $first32 eq '' ? "$libdir/$libfile" : $first32;
170         $#ARGV >= 0 or die "Usage: $0 kernelimage [ramdisk]\n";
171         $kernelfile = $ARGV[0];
172         return unless check_file($setupfile, $kernelfile);
173         if (defined($ramdisk = $ARGV[1])) {
174                 return unless check_file($ramdisk);
175         }
176         $module->add_pm_header("mknbi-linux-$version", $relocseg + HEADER_SEG_OFFSET, hex($startaddr), $progreturns);
177         $setupdesc = { file => $setupfile,
178                 segment => hex($startaddr) / 0x10,
179                 maxlen => START_MAX_LENGTH,
180                 id => 16 };
181         $paramstring = &make_paramstring(PARAM_MAX_LENGTH);
182         $paramseg = { string => $paramstring,
183                 segment => $relocseg + PARAM_SEG_OFFSET,
184                 maxlen => 2048,
185                 id => 17 };
186         $bootseg = { file => $kernelfile,
187                 segment => $relocseg + 0x0,
188                 len => 512,
189                 maxlen => 512,
190                 id => 18 };
191         $module->peek_file($bootseg, \$block, 512) == 512
192                 or die "Error reading boot sector of $kernelfile\n";
193         (undef, $setupsects, $flags, $syssize, $swapdev, $ramsize, $vidmode,
194                 $rootdev, $sig) = unpack('a497Cv7', $block);
195         if ($sig != 0xAA55) {
196                 print STDERR "$kernelfile: not a Linux kernel image\n";
197                 return;
198         }
199         print STDERR 'setupsects flags syssize swapdev ramsize vidmode rootdev sig', "\n" if (DEBUG);
200         print STDERR "$setupsects $flags $syssize $swapdev $ramsize $vidmode $rootdev $sig\n" if (DEBUG);
201         $setupseg = { file => $kernelfile,
202                 segment => $relocseg + 0x20,
203                 fromoff => 512,
204                 len => $setupsects * 512,
205                 maxlen => 32768,
206                 id => 19 };
207         $module->peek_file($setupseg, \$block, 512) == 512
208                 or die "Error reading first setup sector of $kernelfile\n";
209         (undef, $sig, $ver, undef, undef, undef, undef, undef, $flags) =
210                 unpack('va4v5C2', $block);
211         print STDERR 'sig ver flags', "\n" if (DEBUG);
212         print STDERR "$sig $ver $flags\n" if (DEBUG);
213         if ($sig ne 'HdrS' or $ver < 0x201) {
214                 print STDERR "$kernelfile: not a Linux kernel image\n";
215                 return;
216         }
217         $bigker = ($flags & 0x1);
218         $kernelseg = { file => $ARGV[0],
219                 segment => $bigker ? 0x10000 : 0x1000,
220                 maxlen => $bigker ? undef : 1024 * 512, 
221                 fromoff => $setupsects * 512 + 512,
222                 id => 20,
223                 end => 1 };
224         $ramdiskseg = { file => $ramdisk,
225                 segment => 0x10000,
226                 align => $rdnopad ? 1 : 4096,
227                 id => 21,
228                 end => 1 };
229         $$kernelseg{'end'} = 0 if (defined($ramdisk));
230         $module->add_segment($setupdesc);
231         $module->add_segment($paramseg);
232         $module->add_segment($bootseg);
233         $module->add_segment($setupseg);
234         $kernellen = $module->add_segment($kernelseg);
235         if (!$bigker and $kernellen > (($relocseg - 0x1000) * 16)) {
236                 print STDERR "Warning, zImage kernel may collide with Etherboot\n";
237         }
238         # Put ramdisk following kernel at next 4096 byte boundary
239         $$ramdiskseg{'segment'} += (($kernellen + 0xFFF) & ~0xFFF) >> 4 if ($bigker);
240         # should be 0, 1 or 2 depending on rdbase
241         $module->add_segment($ramdiskseg, "\x00") if (defined($ramdisk));
242         $module->dump_segments();
243         $module->copy_file($setupdesc);
244         $module->copy_string($paramseg);
245         $module->copy_file($bootseg);
246         $module->copy_file($setupseg);
247         $module->copy_file($kernelseg);
248         $module->copy_file($ramdiskseg) if (defined($ramdisk));
249 }
250
251 sub get_geom ($$)
252 {
253         my ($file, $block) = @_;
254         my ($usedsize, $declsize, $firsttracksize, $geom_string, $fstype);
255         my ($secttot, $secttrk, $heads, $bigsectors, $bootid, $sig, $cyltot);
256
257         ($usedsize = $squashfd ? &TruncFD::truncfd($file) : -s $file) > 0
258                 or die "Error reading $file\n";
259         (undef, $secttot, undef, $secttrk, $heads, undef, $bigsectors, $bootid, undef,
260                 $fstype, $sig) = unpack('a19va3vva4VCa17a5@510a2', $$block);
261         print STDERR "Warning, this doesn't appear to be a DOS boot sector\n"
262                 if ($sig ne "\x55\xAA");
263         
264         if ($secttot == 0) {
265             $secttot = $bigsectors;
266         }
267         
268         print STDERR "Geometry...\n";
269         print STDERR "totSect:$secttot,spt:$secttrk,hd:$heads,bid:$bootid,fsType:$fstype,sig:$sig,simhd:$simhd\n";
270
271         if ($simhd) {
272                 # change MediaDescriptor
273                 substr($$block, 0x15, 1) = "\xF8";
274                 # change HiddenSectors
275                 substr($$block, 0x1c, 4) = pack('V', $secttrk);
276                 # change the boot drive
277                 substr($$block, 0x24, 1) = "\x80";
278         }
279         $cyltot = $secttot / ($secttrk * $heads);
280         $declsize = $secttot * 512;
281         $firsttracksize = $secttrk * 512;
282         print STDERR "Warning, used size $usedsize is greater than declared size $declsize\n"
283                 if ($usedsize > $declsize);
284                 
285         print STDERR "cyl:$cyltot,decl_size:$declsize,used_size:$usedsize\n";
286                 
287         $geom_string = pack('Vv3C2', $secttot, $heads, $secttrk, $cyltot, $simhd ? 0x80 : 0, $dishd);
288         return ($usedsize, $declsize, $firsttracksize, $geom_string, $fstype);
289 }
290
291 sub mod_geom_string ($)
292 {
293         my ($geom_string) = @_;
294         my ($secttot, $heads, $secttrk, $cyltot, $simhd, $dishd) = unpack('Vv3C2', $geom_string);
295         $cyltot++;      # for partition table
296         return (pack('Vv3C2', $secttot, $heads, $secttrk, $cyltot, $simhd, $dishd));
297 }
298
299 sub encode_chs ($$$)
300 {
301         my ($c, $h, $s) = @_;
302
303         $s = ($s & 0x3F) | (($c & 0x300) >> 2);
304         $c &= 0xFF;
305         return ($h, $s, $c);
306 }
307
308 sub make_mbr ($$)
309 {
310         my ($geom_string, $fstype) = @_;
311         my ($bootsect);
312         my ($secttot, $heads, $secttrk, $cyltot, $simhd, $x) = unpack('Vv3C2', $geom_string);
313
314         $cyltot--;
315         # $cyltot was incremented in mod_geom_string
316 #       $heads = $secttot / ($secttrk * $cyltot);
317         # bootsect is first sector of track 1
318         $bootsect = $secttrk;
319         
320         print STDERR "--- Making MBR\n";
321         print STDERR "cyls:$cyltot,heads:$heads,spt:$secttrk,totalSect:$secttot,FSTYPE:$fstype,BS:$bootsect\n";
322         
323         # CHS stupidity:
324         # cylinders is 0 based, heads is 0 based, but sectors is 1 based
325         # 0x01 for FAT12, 0x04 for FAT16
326         return (pack('@446C8V2@510v', 0x80, &encode_chs(0, 1, 1),
327                 $fstype eq 'FAT12' ? 0x01 : 0x04, &encode_chs($cyltot, $heads - 1, $secttrk),
328                 $bootsect, $secttot, 0xAA55));
329 }
330
331 sub mknbi_fdos ($)
332 {
333         my ($module) = @_;
334         my ($setupfile, $bootblock);
335         my ($usedsize, $declsize, $firsttracksize, $geom_string, $fstype);
336         my ($setupdesc, $kerneldesc, $firsttrackdesc, $bootdesc, $floppydesc);
337         my $elfformat = ($format eq 'elf');
338
339         $setupfile = "$libdir/" . ($elfformat ? "first-elf.fdos" : "first.fdos");
340         $#ARGV >= 1 or die "Usage: $0 kernel.sys floppyimage\n";
341         return unless check_file($setupfile, $ARGV[0], $ARGV[1]);
342         $module->add_header("mknbi-fdos-$version", $relocseg, $relocseg + ($elfformat ? 0x300 : 0x200), 0);
343         $setupdesc = { file => $setupfile,
344                 segment => $relocseg + 0x200,
345                 maxlen => 8192,
346                 id => 16 };
347         $kerneldesc = { file => $ARGV[0],
348                 segment => @@FDKSEG@@,
349                 id => 17 };
350         die "Ramdisk base should be of the form 0xNNNNNNNN (linear hex address)\n"
351                 if (defined($rdbase) and $rdbase !~ /^0x[\da-fA-F]{1,8}$/);
352         $floppydesc = { file => $ARGV[1],
353                 segment => (defined($rdbase) ? (hex($rdbase) >> 4) : 0x11000),
354                 id => 18,
355                 end => 1 };
356         $module->add_segment($setupdesc);
357         $module->add_segment($kerneldesc);
358         $module->peek_file($floppydesc, \$bootblock, 512) == 512
359                 or die "Error reading boot sector of $ARGV[1]\n";
360         ($usedsize, $declsize, $firsttracksize, $geom_string, $fstype)
361                 = &get_geom($ARGV[1], \$bootblock);
362         $firsttrackdesc = { align => $firsttracksize };
363         $$floppydesc{'fromoff'} = 512;
364         $$floppydesc{'len'} = $usedsize;
365         $$floppydesc{'len'} += $firsttracksize if $simhd;
366         $$floppydesc{'maxlen'} = $declsize;
367         $geom_string = &mod_geom_string($geom_string) if $simhd;
368         $module->add_segment($floppydesc, $geom_string);
369         $module->dump_segments();
370         $module->copy_file($setupdesc);
371         $module->copy_file($kerneldesc);
372         if ($simhd) {
373                 $$firsttrackdesc{'string'} = &make_mbr($geom_string, $fstype);
374                 $module->copy_string($firsttrackdesc);
375         }
376         # write out modified bootblock, not the one in the file
377         $bootdesc = { string => $bootblock };
378         $module->copy_string($bootdesc);
379         # Restore correct value of len and account for bootblock skipped
380         $$floppydesc{'len'} = $usedsize - 512;
381         $module->copy_file($floppydesc);
382 }
383
384 sub mknbi_dos ($)
385 {
386         my ($module) = @_;
387         my ($setupfile, $bootblock);
388         my ($usedsize, $declsize, $firsttracksize, $geom_string, $fstype);
389         my ($setupdesc, $firsttrackdesc, $bootdesc, $floppydesc);
390         my $elfformat = ($format eq 'elf');
391
392         $setupfile = "$libdir/" . ($elfformat ? "first-elf.dos" : "first.dos");
393         $#ARGV >= 0 or die "Usage: $0 floppyimage\n";
394         return unless check_file($setupfile, $ARGV[0]);
395         $module->add_header("mknbi-dos-$version", $relocseg, $relocseg + ($elfformat ? 0x300 : 0x200), 0);
396         $setupdesc = { file => $setupfile,
397                 segment => $relocseg + 0x200,
398                 maxlen => 8192,
399                 id => 16 };
400         die "Ramdisk base should be of the form 0xNNNNNNNN (linear hex address)\n"
401                 if (defined($rdbase) and $rdbase !~ /^0x[\da-fA-F]{1,8}$/);
402         $floppydesc = { file => $ARGV[0],
403                 segment => (defined($rdbase) ? (hex($rdbase) >> 4) : 0x11000),
404                 id => 17,
405                 end => 1 };
406         $module->add_segment($setupdesc);
407         $module->peek_file($floppydesc, \$bootblock, 512) == 512
408                 or die "Error reading boot sector of $ARGV[0]\n";
409         ($usedsize, $declsize, $firsttracksize, $geom_string, $fstype)
410                 = &get_geom($ARGV[0], \$bootblock);
411         $firsttrackdesc = { align => $firsttracksize };
412         $$floppydesc{'fromoff'} = 512;
413         $$floppydesc{'len'} = $usedsize;
414         $$floppydesc{'len'} += $firsttracksize if $simhd;
415         $$floppydesc{'maxlen'} = $declsize;
416         $geom_string = &mod_geom_string($geom_string) if $simhd;
417         $module->add_segment($floppydesc, $geom_string);
418         $module->dump_segments();
419         $module->copy_file($setupdesc);
420         if ($simhd) {
421                 $$firsttrackdesc{'string'} = &make_mbr($geom_string, $fstype);
422                 $module->copy_string($firsttrackdesc);
423         }
424         # write out modified bootblock, not the one in the file
425         $bootdesc = { string => $bootblock };
426         $module->copy_string($bootdesc);
427         # Restore correct value of len and account for bootblock skipped
428         $$floppydesc{'len'} = $usedsize - 512;
429         $module->copy_file($floppydesc);
430 }
431
432 sub mknbi_menu ($)
433 {
434         my ($module) = @_;
435         my ($menudesc, $datadesc);
436
437         $#ARGV >= -1 or die "Usage: mk$format-menu [menudata]\n";
438         print STDERR "Warning: mk$format-menu requires Etherboot 5.0 or later\n";
439         return unless check_file("$libdir/menu");
440         # $progreturns == 1
441         $module->add_pm_header("mknbi-menu-$version", $relocseg + 0x0, 0x60000, 1);
442         $menudesc = { file => "$libdir/menu",
443                 segment => 0x6000,
444                 maxlen => 0x10000,
445                 id => 16 };
446         $module->add_segment($menudesc);
447         if ($#ARGV >= 0) {
448                 return unless check_file($ARGV[0]);
449                 $datadesc = { file => $ARGV[0],
450                         segment => 0x7000,
451                         maxlen => 0x10000,
452                         id => 17,
453                         end => 1 };
454                 $module->add_segment($datadesc);
455         } else {
456                 $$menudesc{'end'} = 1;
457         }
458         $module->dump_segments();
459         $module->copy_file($menudesc);
460         $module->copy_file($datadesc) if ($#ARGV >= 0);
461 }
462
463 sub mknbi_nfl ($)
464 {
465         my ($module) = @_;
466         my ($menudesc, $datadesc);
467
468         $#ARGV >= -1 or die "Usage: mk$format-nfl [menudata]\n";
469         print STDERR "Warning: mk$format-nfl requires Etherboot 5.0 or later\n";
470         return unless check_file("$libdir/nfl");
471         # $progreturns == 1
472         $module->add_pm_header("mknbi-nfl-$version", $relocseg + 0x0, 0x60000, 1);
473         $menudesc = { file => "$libdir/nfl",
474                 segment => 0x6000,
475                 maxlen => 0x10000,
476                 id => 16 };
477         $module->add_segment($menudesc);
478         if ($#ARGV >= 0) {
479                 return unless check_file($ARGV[0]);
480                 $datadesc = { file => $ARGV[0],
481                         segment => 0x7000,
482                         maxlen => 0x10000,
483                         id => 17,
484                         end => 1 };
485                 $module->add_segment($datadesc);
486         } else {
487                 $$menudesc{'end'} = 1;
488         }
489         $module->dump_segments();
490         $module->copy_file($menudesc);
491         $module->copy_file($datadesc) if ($#ARGV >= 0);
492 }
493
494 #
495 #       Packing of LUA program:
496 #       LUA version as network int32, i.e. 4.0.1.0
497 #       progname rounded to int32 boundary
498 #       length of program as network int32
499 #       program
500 #
501 sub read_lua_prog ($) {
502         my ($progfile) = @_;
503
504         open(P, $progfile) or die "Shouldn't happen, we already did check_file!\n";
505         # remove all but last component of filename
506         $progfile =~ s:.*/::;
507         $progfile .= "\x00";
508         local $/;
509         local $_ = <P>;
510         close(P);
511         my $len = length($progfile);
512         $len = ($len + 3) & ~0x3;
513         return (pack("Na${len}Na*", LUA_VERSION, $progfile, length($_), $_));
514 }
515
516 sub mkelf_lua ($)
517 {
518         my ($module) = @_;
519         my ($menudesc, $datadesc);
520
521         $format eq 'elf' or die "Only ELF images are catered for\n";
522         $#ARGV >= 0 or die "Usage: mkelf-lua luaprog\n";
523         print STDERR "Warning: mkelf-lua requires Etherboot 5.0 or later\n";
524         return unless check_file("$libdir/lua", $ARGV[0]);
525         # $progreturns == 1
526         $module->add_pm_header("mkelf-lua-$version", 0x7c0, 0x60000, 1);
527         $menudesc = { file => "$libdir/lua",
528                 segment => 0x6000,
529                 maxlen => 0x20000,
530                 id => 16 };
531         $module->add_segment($menudesc);
532         my $progstring = &read_lua_prog($ARGV[0]);
533         my $progdesc = { string => $progstring,
534                 segment => 0x8000,
535                 maxlen => 0x4000,
536                 id => 17,
537                 end => 1 };
538         $module->add_segment($progdesc);
539         $module->dump_segments();
540         $module->copy_file($menudesc);
541         $module->copy_string($progdesc);
542 }
543
544 $libdir = '@@LIBDIR@@';         # where config and auxiliary files are stored
545
546 $version = '@@VERSION@@';
547 $showversion = '';
548 $rdnopad = 0;
549 $simhd = 0;
550 $dishd = 0;
551 $squashfd = 1;
552 $relocsegstr = '0x9000';
553 $progreturns = 0;
554 GetOptions('format=s' => \$format,
555         'target=s' => \$target,
556         'output=s' => \$output,
557         'param=s' => \$param,
558         'append=s' => \$append,
559         'rootdir=s' => \$rootdir,
560         'rootmode=s' => \$rootmode,
561         'ip=s' => \$ip,
562         'rdbase=s' => \$rdbase,
563         'rdnopad!' => \$rdnopad,
564         'harddisk!' => \$simhd,
565         'disableharddisk!' => \$dishd,
566         'squash!' => \$squashfd,
567         'first32:s' => \$first32,
568         'progreturns!' => \$progreturns,
569         'relocseg=s' => \$relocsegstr,
570         'version' => \$showversion);
571
572 if ($showversion) {
573         print STDERR "$version\n";
574         exit 0;
575 }
576
577 if ($] > 5.008 and $] < 5.008003 and defined($ENV{LANG}) and $ENV{LANG} =~ /\.UTF-8$/i) {
578         print STDERR <<'EOF';
579 Warning: Perl 5.8 may have a bug that affects handing of strings in Unicode
580 locales that may cause misbehaviour with binary files.  To work around this
581 problem, set $LANG to not have a suffix of .UTF-8 before running this program.
582 EOF
583 }
584
585 $0 =~ /mk([a-z]*)-([a-z]+)$/ and ($format = $1, $target = $2);
586 if (!defined($format)) {
587         print STDERR "No format specified with program name or --format=\n";
588         exit 1;
589 }
590 if (!defined($target)) {
591         print STDERR "No target specified with program name or --target=\n";
592         exit 1;
593 }
594 if (defined($output)) {
595         die "$output: $!\n" unless open(STDOUT, ">$output");
596 }
597 binmode(STDOUT);
598
599 if ($format eq 'nbi') {
600         $first32 = '' if !defined($first32);
601         if ($target ne 'rom') {
602                 print STDERR "mkelf-$target is preferred in future instead of mknbi-$target\n";
603         }
604         $module = Nbi->new($libdir);
605 } elsif ($format eq 'elf') {
606         $first32 = '' if !defined($first32);
607         $module = Elf->new($libdir);
608         die "Output must be file\n" unless (seek(STDOUT, 0, SEEK_SET));
609 } else {
610         die "Format $format not supported\n";
611 }
612 if ($relocsegstr eq '0x9000' or $relocsegstr eq '0x8000') {
613         $relocseg = hex($relocsegstr);
614 } else {
615         print STDERR "relocseg must be 0x9000 or 0x8000 only, setting to 0x9000\n";
616         $relocseg = 0x9000;
617 }
618 if ($target eq 'rom') {
619         &mknbi_rom($module);
620 } elsif ($target eq 'img') {
621         &mkelf_img($module);
622 } elsif ($target eq 'linux') {
623         &mknbi_linux($module);
624 } elsif ($target eq 'fdos') {
625         if ($simhd and $dishd) {
626                 print STDERR "Warning: --harddisk and --disableharddisk are incompatible\n";
627         }
628         &mknbi_fdos($module);
629 } elsif ($target eq 'dos') {
630         if ($simhd and $dishd) {
631                 print STDERR "Warning: --harddisk and --disableharddisk are incompatible\n";
632         }
633         &mknbi_dos($module);
634 } elsif ($target eq 'menu') {
635         &mknbi_menu($module);
636 } elsif ($target eq 'nfl') {
637         &mknbi_nfl($module);
638 } elsif ($target eq 'lua') {
639         &mkelf_lua($module);
640 } else {
641         print STDERR "Target $target not supported\n";
642         exit;
643 }
644 $module->finalise_image();
645 close(STDOUT);
646 exit 0;
647
648 __END__
649
650 =head1 NAME
651
652 mknbi - make network bootable image
653
654 =head1 SYNOPSIS
655
656 B<mknbi> --version
657
658 B<mknbi> --format=I<format> --target=I<target> [--output=I<outputfile>] I<target-specific-arguments>
659
660 B<mkelf-linux> [--output=I<outputfile>] I<kernelimage> [I<ramdisk>]
661
662 B<mknbi-linux> [--output=I<outputfile>] I<kernelimage> [I<ramdisk>]
663
664 B<mknbi-rom> [--output=I<outputfile>] I<.z?rom-file>
665
666 B<mkelf-img> [--output=I<outputfile>] I<.z?img-file>
667
668 B<mkelf-menu> [--output=I<outputfile>] [I<dataimage>]
669
670 B<mknbi-menu> [--output=I<outputfile>] [I<dataimage>]
671
672 B<mkelf-nfl> [--output=I<outputfile>] [I<dataimage>]
673
674 B<mknbi-nfl> [--output=I<outputfile>] [I<dataimage>]
675
676 B<mkelf-lua> [--output=I<outputfile>] I<luabin>
677
678 B<mknbi-fdos> [--output=I<outputfile>] I<kernel.sys floppyimage>
679
680 B<mknbi-dos> [--output=I<outputfile>] I<floppyimage>
681
682 =head1 DESCRIPTION
683
684 B<mknbi> is a program that makes network bootable images for various
685 operating systems suitable for network loading by Etherboot or Netboot,
686 which are ROM boot loaders.  If you are looking to boot using PXE, look
687 no further, mknbi is not what you want. You probably want something like
688 PXELINUX which is part of the SYSLINUX package.
689
690 B<mknbi> --version prints the current version. Use this before reporting
691 problems.
692
693 B<mknbi> can be invoked with the B<--format> and B<--target> options or
694 links can be made to it under format and target specific names. E.g.
695 mkelf-linux is the same as mknbi --format=elf --target=linux.
696
697 B<--format>=I<format> Specify the format of the output. Currently
698 available are nbi and elf.  ELF format only works with linux and menu.
699 Otherwise the invocation is the same as for mknbi. In discussions below,
700 the mknbi form is used.
701
702 B<--target>=I<target> Specify the target binary. Currently available are
703 linux, menu, rom, fdos and dos. B<mknbi> is not needed for booting
704 FreeBSD.
705
706 B<--output=>I<outputfile> Specify the output file, can be used with
707 all variants.  Stdout is the default.
708
709 The package must be installed in the destination location before the
710 executables can be run, because it looks for library files.
711
712 Each of the variants will be described separately.
713
714 =head1 MKELF-LINUX
715
716 B<mkelf-linux> and B<mknbi-linux> makes a boot image from a Linux kernel
717 image, either a zImage or a bzImage.
718
719 =head1 MKELF-LINUX OPTIONS
720
721 B<--param=>I<string> Replace the default parameter string with the
722 specified one. This option overrides all the following options so you
723 should know what you are doing.
724
725 B<--append>=I<string> Appends the specified string to the existing
726 parameter string. This option operates after the other parameter options
727 have been evaluated.
728
729 B<--rootdir>=I<rootdir> Define name of directory to mount via NFS from
730 the boot server.
731
732 In the absence of this option, the default is to use the directory
733 C</tftpboot/>I<%s>, with the I<%s> representing the hostname or
734 IP-address of the booting system, depending on whether the hostname
735 attribute is present in the BOOTP/DHCP reply.
736
737 If C<rom> is given, and if the BOOTP/DHCP server is able to handle the RFC 1497
738 extensions, the value of the rootpath option is used as the root directory.
739
740 If the name given to the option starts with C</dev/>, the corresponding
741 device is used as the root device, and no NFS directory will be mounted.
742
743 B<--rootmode>=C<ro|rw> Defines whether the root device will be mounted
744 read-only or read-write respectively. Without this parameter, the
745 default is C<rw>.
746
747 B<--ip=>I<string> Define client and server IP addresses.
748
749 In the absence of this option no IP addresses are defined, and the
750 kernel will determine the IP addresses by itself, usually by using DHCP,
751 BOOTP or RARP.  Note that the kernel's query is I<in addition to> the
752 query made by the bootrom, and requires the IP: kernel level
753 autoconfiguration (CONFIG_IP_PNP) feature to be included in the kernel.
754
755 Important note: In Linux kernels 2.2.x where x >= 18, and 2.4.x where x
756 >= 5, it is B<necessary> to specify one of the enabling options in the
757 next paragraph to cause the IP autoconfiguration to be activated.
758 Unlike in previous kernels, IP autoconfiguration does not happen by
759 default. Also note that IP autoconfiguration and NFSroot are likely to
760 go away in Linux 2.6 and that userspace IP configuration methods using
761 ramdisk and userspace DHCP daemons are preferred now.
762
763 If one of the following: C<off, none, on, any, dhcp, bootp, rarp, both>,
764 is given, then the option will be passed unmodified to the kernel and
765 cause that autoconfig option to be chosen.
766
767 If C<rom> is given as the argument to this option, all necessary IP
768 addresses for NFS root mounting will be inherited from the BOOTP/DHCP
769 answer the bootrom got from the server.
770
771 It's also possible to define the addresses during compilation of the boot
772 image. Then, all addresses must be separated by a colon, and ordered in
773 the following way:
774
775 C<--ip=>I<client:server:gateway:netmask:hostname[:dev[:proto]]>
776
777 Using this option B<mkelf-linux> will automatically convert system names
778 into decimal IP addresses for the first three entries in this string.
779 The B<hostname> entry will be used by the kernel to set the host name of
780 the booted Linux diskless client.  When more than one network interface
781 is installed in the diskless client, it is possible to specify the name
782 of the interface to use for mounting the root directory via NFS by
783 giving the optional value C<dev>.  This entry has to start with the
784 string C<eth> followed by a number from 0 to 9. However, if only one
785 interface is installed in the client, this I<dev> entry including the
786 preceding semicolon can be left out. The I<proto> argument is one of the
787 IP autoconfiguration enabling options listed above.  (Author: it's not
788 clear to me what the IP autoconfiguration does when the parameters are
789 already specified.  Perhaps it's to obtain parameters not specified,
790 e.g. NIS domain.)
791
792 B<--rdbase=>I<top|asis|0xNNNNNNNN> Set the ramdisk load address.  C<top>
793 moves the ramdisk to the top of memory before jumping to the kernel.
794 This is the default if rdbase is not specified.  This option requires
795 that first-linux's kernel sizing work correctly.  C<asis> loads it at
796 0x100000 (1MB) if the kernel is loaded low; or leaves it just after the
797 kernel in memory, if the kernel is loaded high. For this option to work,
798 the kernel must be able to handle ramdisks at these addresses.
799 I<0xNNNNNNNN> moves the ramdisk to the hex address specified. The onus
800 is on the user to specify a suitable address that is acceptable to the
801 kernel and doesn't overlap with any other segments. Etherboot will round
802 address down to multiple of 4k (last 3 digits to zero).
803
804 B<--rdnopad> By default, etherboot pads (with nulls) the given initrd of
805 any size to multiple of 4k bytes (aligning to memory page boundary). Use
806 this option to disable padding if it causes problem.
807
808 B<--first32=>I<program> Override the default first stage setup
809 program.  It can be used to call extensions to the Etherboot code, which
810 paves the way for additional useful functionality without enlarging the
811 size of the Etherboot footprint.  --first32 is implied by the ELF
812 format.
813
814 B<--progreturns> This option is used in conjunction with and only valid
815 with the --first32 option to indicate to the Etherboot loader that the
816 called program will return to loader and hence Etherboot should not
817 disable the network device as is the case when the program will never
818 return to Etherboot.
819
820 B<--relocseg=>I<segaddr> This option is used to specify a relocation of
821 the Linux first, boot, setup, and parameter segments to another 64k
822 band.  Currently the only valid values are 0x9000 and 0x8000,
823 corresponding to linear addresses of 0x90000 and 0x80000 upwards. The
824 default is 0x9000.  Usually you use this option if you have relocated
825 Etherboot to 0x84000 to avoid other code in the 0x90000 segment like
826 DOC. The Linux kernel must support relocation which implies a 2.4 kernel
827 or later. --relocseg only works reliably with ELF or --first32=.
828
829 B<mem=>I<memsize> This is not a command line option but a kernel
830 parameter that is intercepted by the first32 stage and used as the top
831 of memory, to match Linux's interpretation. I<memsize> can be suffixed
832 by C<G> to indicate gibibytes (times 2^30), C<M> to indicate mebibytes
833 (times 2^20) or C<K> to indicate kibibytes (times 2^10). Note that the
834 suffixes are uppercase. This kernel parameter can be specified in
835 --append= or option-129 of the DHCP/BOOTP record.
836
837 Run the program thus:
838
839 mkelf-linux I<kernel-image> [I<ramdisk-image>] > linux.nb
840
841 Then move F<linux.nb> to where the network booting process expects to
842 find it.
843
844 =head1 MKELF-LINUX BOOTP/DHCP VENDOR TAGS
845
846 B<mkelf-linux> includes a startup code at the beginning of the Linux
847 kernel which is able to detect certain DHCP vendor defined options.
848 These can be used to modify the kernel loading process at runtime. To
849 use these options with ISC DHCPD v3, a popular DHCP daemon, the syntax
850 is as below. You will need to adjust the syntax for other DHCP or BOOTP
851 daemons.
852
853 option etherboot-signature code 128 = string;
854
855 option kernel-parameters code 129 = text;
856
857 ...
858
859                 option etherboot-signature E4:45:74:68:00:00;
860
861                 option kernel-parameters "INITRD_DBG=6 NIC=3c509";
862
863 Option 128 is required to be the six byte signature above. See the
864 vendortags appendix of the Etherboot user manual for details.
865
866 The following option is presently supported by B<mkelf-linux>:
867
868 B<129> The I<string> value given with this option is appended verbatim to
869 the end of the kernel command line.  It can be used to specify arguments
870 like I/O addresses or DMA channels required for special hardware
871 like SCSI adapters, network cards etc. Please consult the Linux kernel
872 documentation about the syntax required by those options. It is the same
873 as the B<--append> command line option to B<mkelf-linux>, but works at
874 boot time instead of image build time.
875
876 B<130> With this option it is possible to the select the network adapter
877 used for mounting root via NFS on a multihomed diskless client. The
878 syntax for the I<string> value is the same as for the C<dev> entry used
879 with the B<--ip=> option as described above. However note that the
880 B<mkelf-linux> runtime setup routine does not check the syntax of the
881 string.
882
883 =head1 MKNBI-ROM
884
885 B<mknbi-rom> makes a boot image from an Etherboot C<.rom> or C<.zrom>
886 boot ROM image.  This allows it to be netbooted using an existing
887 ROM. This is useful for developing Etherboot drivers or to load a newer
888 version of Etherboot with an older one.
889
890 Run mknbi-rom like this:
891
892 mknbi-rom nic.zrom > nic.nb
893
894 Move F<nic.nb> to where the network booting process expects to find it.
895 The boot ROM will load this as the I<operating system> and execute the
896 ROM image.
897
898 =head1 MKELF-IMG
899
900 B<mkelf-img> makes a boot image from an Etherboot C<.img> or C<.zimg>
901 image.  This allows it to be netbooted using an existing ROM. This is
902 useful for developing Etherboot drivers or to load a newer version of
903 Etherboot with an older one.
904
905 Run mkelf-img like this:
906
907 mkelf-img nic.zimg > nic.nb
908
909 Move F<nic.nb> to where the network booting process expects to find it.
910 The boot ROM will load this as the I<operating system> and execute the
911 image.
912
913 Note that this does not test the ROM loader portion that's in a C<.z?rom>
914 image, but not in a C<.z?img>.
915
916 =head1 MKELF-MENU
917
918 B<mkelf-menu> and B<mknbi-menu> make a boot image from an auxiliary menu
919 program. Etherboot has the ability to load an auxiliary program which
920 can interact with the user, modify the DHCP structure, and return a
921 status.  Based on the status, Etherboot can load another binary, restart
922 or exit.  This makes it possible to have elaborate user interface
923 programs without having to modify Etherboot. The specification for
924 auxiliary program is documented in the Etherboot Developer's Manual.
925
926 B<mkelf-menu> and B<mknbi-menu> take a binary named C<menu> from the
927 library directory, which is assumed to have an entry point of 0x60000.
928 An optional argument is accepted, and this is loaded at 0x80000. This
929 can be a data file used by the menu program.
930
931 Currently, the menu binary provided duplicates the builtin menu facility
932 of Etherboot with the exception of a couple of small differences: no
933 server or gateway specifications are used and nested TFTP loads don't
934 work. You should not have MOTD or IMAGE_MENU defined in your Etherboot
935 build to be able to use this external menu binary. The specifications of
936 the DHCP option required is in the vendortags document in the Etherboot
937 user manual.
938
939 Typical usage is like this:
940
941 mkelf-menu > menu.nb
942
943 Then put menu.nb in the TFTP boot directory and edit your DHCP options
944 according to the documentation.
945
946 Alternate user interface programs are highly encouraged.
947
948 =head1 MKELF-NFL
949
950 B<mkelf-nfl> and B<mknbi-nfl> make a boot image from the NFL menu
951 program. This menu program takes the names of images from a
952 menu-text-file file which just contains lines with the filenames
953 (relative to the tftpd root directory) of images to load. The
954 user-interface is a light-bar, similar to that used in GRUB.  There is a
955 sample menu-text-file in C<menu-nfl.eg>. The special entry "Quit
956 Etherboot" (without quotes, of course) can be used in menu-text-files
957 as an entry that causes Etherboot to quit and return to the invoking
958 environment, which is the BIOS in the case of ROMs.
959
960 Typical usage is:
961
962 mkelf-nfl I<menu-text-file> > nfl.nb
963
964 Then put nfl.nb in the TFTP boot directory and specify as the boot
965 image. Chaining to other menus works.
966
967 Enhancements to the menu format accepted to specify other features such
968 as titles, timeout, colours, and so forth are highly encouraged.
969
970 =head1 MKELF-LUA
971
972 B<mkelf-lua> makes an ELF image from a precompiled Lua
973 (C<http://www.tecgraf.puc-rio.br/lua/>) program.
974
975 Typical usage is:
976
977 mkelf-lua hello.lb > luaprog.nb
978
979 where C<hello.lb> was generated from a Lua program by:
980
981 luac -o hello.lb hello.lua
982
983 The functions available to Lua programs in this environment is described
984 in a separate document.
985
986 =head1 MKNBI-FDOS
987
988 B<mknbi-fdos> makes a boot image from a FreeDOS kernel file and a floppy
989 image.  Note that the kernel image is not read from the floppy section
990 of the boot image, but is a separate section in the boot image. The
991 bootloader has been adjusted to jump to it directly. This means the
992 space that would be taken up on the I<floppy> by the kernel image file
993 can now be used for applications and data.
994
995 Obtain a distribution of FreeDOS with a recent kernel, probably at least
996 2006. It has been tested with 2012 but nothing older. You can get the
997 FreeDOS kernel here:
998
999 C<http://freedos.sourceforge.net/>
1000
1001 Follow the instructions to make a bootable floppy. Then get an image
1002 of the floppy with:
1003
1004 dd if=/dev/fd0 of=/tmp/floppyimage
1005
1006 Also extract F<kernel.sys> from the floppy. You can do this from the
1007 image using the mtools package, by specifying a file as a I<drive>
1008 with a declaration like this in F<~/.mtoolsrc>:
1009
1010 drive x: file="/tmp/floppyimage"
1011
1012 Then run:
1013
1014 mcopy x:kernel.sys .
1015
1016 Then run mknbi by:
1017
1018 mknbi-fdos kernel.sys /tmp/floppyimage > freedos.nb
1019
1020 where F<kernel.sys> and F</tmp/floppyimage> are the files extracted above.
1021 Then move F<freedos.nb> to where the network booting process expects to
1022 find it.
1023
1024 If you have got it to netboot successfully, then you can go back and
1025 add your files to the floppy image. You can delete F<kernel.sys> in
1026 the floppy image to save space, that is not needed. Note that you can
1027 create a floppy image of any size you desire with the mformat program
1028 from mtools, you are not restricted to the actual size of the boot floppy.
1029
1030 =head1 MKNBI-FDOS OPTIONS
1031
1032 B<--harddisk> Make the boot ramdisk the first hard disk, i.e. C:. One
1033 reason you might want to do this is because you want to use the real
1034 floppy. The limit on "disk size" in the boot image is not raised by this
1035 option so that is not a reason to use this option. This option is
1036 incompatible with --disableharddisk.
1037
1038 B<--disableharddisk> When the ramdisk is simulating a floppy disk drive,
1039 this switch will disable hard disk accesses.  This is necessary if the
1040 client should use a network file system as drive C:, which is only
1041 possible if there are no hard disks found by DOS. This option is
1042 incompatible with --harddisk.
1043
1044 B<--nosquash> Do not try to chop unused sectors from the end of the
1045 floppy image. This increases the boot image size and hence loading
1046 time if the FAT filesystem on the floppy is mostly empty but you may
1047 wish to use this option if you have doubts as to whether the squashing
1048 algorithm is working correctly.
1049
1050 B<--rdbase=>I<0xNNNNNNNN> Set the ramdisk load address. The default
1051 load address for the ramdisk is 0x110000. It can be moved higher
1052 (lower will not work) if for some reason you need to load other stuff
1053 at the address it currently occupies. As this is a linear address and
1054 not a segment address, the last 4 bits are not used and should be 0.
1055
1056 =head1 MKNBI-DOS
1057
1058 B<mknbi-dos> makes a boot image from a floppy image containing a
1059 bootable DOS filesystem.  It is not necessary to build the filesystem on
1060 a physical floppy if you have the mtools package, but you need a
1061 bootable floppy of any size to start with. First extract the boot block
1062 from the floppy, this boot block must match the DOS kernel files you
1063 will copy in the next step:
1064
1065 dd if=/dev/fd0 of=bootblock bs=512 count=1
1066
1067 Then get the DOS kernel files (this is correct for DR-DOS, the names
1068 are different in MS-DOS, IO.SYS and MSDOS.SYS):
1069
1070 mcopy a:IBMBIO.COM a:IBMDOS.COM a:COMMAND.COM .
1071
1072 Next make an entry in F<~/.mtoolsrc> to declare a floppy to be mapped
1073 to a file:
1074
1075 drive x: file="/tmp/floppyimage"
1076
1077 Now format a floppy of the desired size, in this example a 2.88 MB floppy,
1078 at the same time writing the bootblock onto it:
1079
1080 mformat -C -t 80 -s 36 -h 2 -B bootblock x:
1081
1082 The size of the "floppy" is only limited by the limits on the number of
1083 cylinders, sectors and heads, which are 1023, 63 and 255 respectively,
1084 and the amount of RAM you are willing to allocate to the "floppy" in
1085 memory. As RAM is precious, choose a size slightly bigger than what is
1086 needed to hold your "floppy" files.
1087
1088 Finally, copy all your desired files onto the floppy:
1089
1090 mcopy IBMBIO.COM x:
1091
1092 mcopy IBMDOS.COM x:
1093
1094 mcopy COMMAND.COM x:
1095
1096 mcopy CONFIG.SYS AUTOEXEC.BAT APP.EXE APP.DAT ... x:
1097
1098 For MS-DOS substitute IO.SYS for IBMIO.COM, and MSDOS.SYS for
1099 IBMDOS.COM.  The case of the files must be preserved, it may not work if
1100 VFAT lower case names are generated in the floppy image.  Pay attention
1101 to the order of copying as the boot block may expect the first two
1102 entries on a newly formatted disk to be IO.SYS, MSDOS.SYS.  Possibly too
1103 COMMAND.COM has to be the third entry so we play safe.  Thanks to Phil
1104 Davey and Phillip Roa for these tips.
1105
1106 I have reports that the bootblock of MS-DOS 6.22 sometimes fails to boot
1107 the ramdisk.  You could try using the boot block from Netboot instead of
1108 getting the boot block off the floppy. I have provided this boot block
1109 in the distribution as altboot.bin, and in source form as altboot.S and
1110 boot.inc. One essential thing is to make IO.SYS the first file on the
1111 disk, or this bootblock will not work.
1112
1113 If you happen to have a media of the same size you could test if the
1114 image is bootable by copying it onto the media, and then booting it:
1115
1116 dd if=/tmp/floppyimage of=/dev/fd0
1117
1118 Then run mknbi-dos over the image F</tmp/floppyimage> to create a
1119 boot image:
1120
1121 mknbi-dos /tmp/floppyimage > dos.nb
1122
1123 Move F<dos.nb> to where the network booting process expects to find it.
1124
1125 =head1 MKNBI-DOS OPTIONS
1126
1127 B<--harddisk> Make the boot ramdisk the first hard disk, i.e. C:. One
1128 reason you might want to do this is because you want to use the real
1129 floppy. The limit on "disk size" in the boot image is not raised by this
1130 option so that is not a reason to use this option. This option is
1131 incompatible with --disableharddisk.
1132
1133 B<--disableharddisk> When the ramdisk is simulating a floppy disk drive,
1134 this switch will disable hard disk accesses.  This is necessary if the
1135 client should use a network file system as drive C:, which is only
1136 possible if there are no hard disks found by DOS. This option is
1137 incompatible with --harddisk.
1138
1139 B<--nosquash> Do not try to chop unused sectors from the end of the
1140 floppy image. This increases the boot image size and hence loading
1141 time if the FAT filesystem on the floppy is mostly empty but you may
1142 wish to use this option if you have doubts as to whether the squashing
1143 algorithm is working correctly.
1144
1145 B<--rdbase=>I<0xNNNNNNNN> Set the ramdisk load address. The default
1146 load address for the ramdisk is 0x110000. It can be moved higher
1147 (lower will not work) if for some reason you need to load other stuff
1148 at the address it currently occupies. As this is a linear address and
1149 not a segment address, the last 4 bits are not used and should be 0.
1150
1151 =head1 BUGS
1152
1153 Please report all bugs to Etherboot users mailing list:
1154 <https://sourceforge.net/mail/?group_id=4233>
1155
1156 =head1 SEE ALSO
1157
1158 Etherboot tutorial at C<http://etherboot.sourceforge.net/> Mtools package
1159 is at C<http://wauug.erols.com/pub/knaff/mtools/> Make sure you have a
1160 recent version, the ability to map a drive to a file is not present in
1161 old versions.
1162
1163 =head1 COPYRIGHT
1164
1165 B<mknbi> is under the GNU Public License
1166
1167 =head1 AUTHOR
1168
1169 Ken Yap
1170
1171 mk{elf,nbi}-nfl was contributed by Robb Main of Genedyne.
1172
1173 =head1 DATE
1174
1175 See man page footer for date and version. Sorry, not available in the
1176 HTML version.