📄 mknbi.pl
字号:
#!/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# 2003.04.28 R. Main# Tweaks to work with new first-dos.S for large disk imagesBEGIN { push(@INC, '@@LIBDIR@@');}use strict;use Getopt::Long;use IO::Seekable;use Socket;use TruncFD;use Nbi;use Elf;use constant;use constant DEBUG => 0;use constant LUA_VERSION => 0x04000100; # 4.0.1.0use bytes;use vars qw($libdir $version $format $target $output $module $relocseg $relocsegstr $progreturns $param $append $rootdir $rootmode $ip $ramdisk $rdbase $simhd $dishd $squashfd $first32 $showversion);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 ($module) = @_; my ($romdesc); $#ARGV >= 0 or die "Usage: $0 romimage\n"; return unless check_file($ARGV[0]); $module->add_header("mknbi-rom-$version", $relocseg + 0x3E0, 0x6000, 6); $romdesc = { file => $ARGV[0], segment => 0x6000, maxlen => 0x10000, id => 16, end => 1 }; $module->add_segment($romdesc); $module->dump_segments(); $module->copy_file($romdesc);}sub mkelf_img ($){ my ($module) = @_; my ($romdesc); $#ARGV >= 0 or die "Usage: $0 .img-file\n"; return unless check_file($ARGV[0]); $module->add_pm_header("mkelf-img-$version", $relocseg + 0x3E0, 0x60000, 0); $romdesc = { file => $ARGV[0], segment => 0x6000, maxlen => 0x10000, id => 16, end => 1 }; $module->add_segment($romdesc); $module->dump_segments(); $module->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 bytesuse constant START_OFFSET => 0x280; # in units of 16 bytesuse constant START_MAX_LENGTH => 6144;use constant PARAM_SEG_OFFSET => 0x240; # in units of 16 bytesuse constant PARAM_MAX_LENGTH => 1024;sub mknbi_linux ($){ my ($module) = @_; 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 = ($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); } $module->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 }; $module->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 }; $module->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)); $module->add_segment($setupdesc); $module->add_segment($paramseg); $module->add_segment($bootseg); $module->add_segment($setupseg); $kernellen = $module->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 $module->add_segment($ramdiskseg, "\x00") if (defined($ramdisk)); $module->dump_segments(); $module->copy_file($setupdesc); $module->copy_string($paramseg); $module->copy_file($bootseg); $module->copy_file($setupseg); $module->copy_file($kernelseg); $module->copy_file($ramdiskseg) if (defined($ramdisk));}sub get_geom ($$){ my ($file, $block) = @_; my ($usedsize, $declsize, $firsttracksize, $geom_string, $fstype); my ($secttot, $secttrk, $heads, $bigsectors, $bootid, $sig, $cyltot); ($usedsize = $squashfd ? &TruncFD::truncfd($file) : -s $file) > 0 or die "Error reading $file\n"; (undef, $secttot, undef, $secttrk, $heads, undef, $bigsectors, $bootid, undef, $fstype, $sig) = unpack('a19va3vva4VCa17a5@510a2', $$block); print STDERR "Warning, this doesn't appear to be a DOS boot sector\n" if ($sig ne "\x55\xAA"); if ($secttot == 0) { $secttot = $bigsectors; } print STDERR "Geometry...\n"; print STDERR "totSect:$secttot,spt:$secttrk,hd:$heads,bid:$bootid,fsType:$fstype,sig:$sig,simhd:$simhd\n"; if ($simhd) { # change MediaDescriptor substr($$block, 0x15, 1) = "\xF8"; # change HiddenSectors substr($$block, 0x1c, 4) = pack('V', $secttrk); # change the boot drive substr($$block, 0x24, 1) = "\x80"; } $cyltot = $secttot / ($secttrk * $heads); $declsize = $secttot * 512; $firsttracksize = $secttrk * 512; print STDERR "Warning, used size $usedsize is greater than declared size $declsize\n" if ($usedsize > $declsize); print STDERR "cyl:$cyltot,decl_size:$declsize,used_size:$usedsize\n"; $geom_string = pack('Vv3C2', $secttot, $heads, $secttrk, $cyltot, $simhd ? 0x80 : 0, $dishd); return ($usedsize, $declsize, $firsttracksize, $geom_string, $fstype);}sub mod_geom_string ($){ my ($geom_string) = @_; my ($secttot, $heads, $secttrk, $cyltot, $simhd, $dishd) = unpack('Vv3C2', $geom_string); $cyltot++; # for partition table return (pack('Vv3C2', $secttot, $heads, $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 ($bootsect); my ($secttot, $heads, $secttrk, $cyltot, $simhd, $x) = unpack('Vv3C2', $geom_string); $cyltot--; # $cyltot was incremented in mod_geom_string# $heads = $secttot / ($secttrk * $cyltot); # bootsect is first sector of track 1 $bootsect = $secttrk; print STDERR "--- Making MBR\n"; print STDERR "cyls:$cyltot,heads:$heads,spt:$secttrk,totalSect:$secttot,FSTYPE:$fstype,BS:$bootsect\n"; # 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(0, 1, 1), $fstype eq 'FAT12' ? 0x01 : 0x04, &encode_chs($cyltot, $heads - 1, $secttrk), $bootsect, $secttot, 0xAA55));}sub mknbi_fdos ($){ my ($module) = @_; my ($setupfile, $bootblock); my ($usedsize, $declsize, $firsttracksize, $geom_string, $fstype); my ($setupdesc, $kerneldesc, $firsttrackdesc, $bootdesc, $floppydesc); my $elfformat = ($format eq 'elf'); $setupfile = "$libdir/" . ($elfformat ? "first-elf.fdos" : "first.fdos"); $#ARGV >= 1 or die "Usage: $0 kernel.sys floppyimage\n"; return unless check_file($setupfile, $ARGV[0], $ARGV[1]); $module->add_header("mknbi-fdos-$version", $relocseg, $relocseg + ($elfformat ? 0x300 : 0x200), 0); $setupdesc = { file => $setupfile, segment => $relocseg + 0x200, maxlen => 8192, 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 }; $module->add_segment($setupdesc); $module->add_segment($kerneldesc); $module->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; $module->add_segment($floppydesc, $geom_string); $module->dump_segments(); $module->copy_file($setupdesc); $module->copy_file($kerneldesc); if ($simhd) { $$firsttrackdesc{'string'} = &make_mbr($geom_string, $fstype); $module->copy_string($firsttrackdesc); } # write out modified bootblock, not the one in the file $bootdesc = { string => $bootblock }; $module->copy_string($bootdesc); # Restore correct value of len and account for bootblock skipped $$floppydesc{'len'} = $usedsize - 512; $module->copy_file($floppydesc);}sub mknbi_dos ($){ my ($module) = @_; my ($setupfile, $bootblock); my ($usedsize, $declsize, $firsttracksize, $geom_string, $fstype); my ($setupdesc, $firsttrackdesc, $bootdesc, $floppydesc); my $elfformat = ($format eq 'elf');
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -