A local mirror of sys-freedos.pl from
http://www.ibiblio.org/pub/micro/pc-stuff/freedos/files/dos/sys/sys-freedos-linux/
#!/usr/bin/perl -w
use English; # things like MATCH
use Getopt::Long;
use File::Basename; # basename, dirname
use Fcntl; # O_RDWR
# A Perl script to SYS the boot sector of a disk image or
# a device for FreeDOS. Do not forget to copy kernel.sys
# and command.com to the root directory of the target!
# For the latter, you can use mtools, or just mount the
# target. If needed, use loopback mounting. Sample line
# for ~/.mtoolsrc: drive m: file="/tmp/test.img"
# Perl by Eric Auer, 16aug2004. The boot sectors are by
# the FreeDOS project and licensed under the GNU GPL.
# This Perl script itself is free Public Domain software.
# Added 6/2007: hidden sectors / heads / sec_per_track
# override possible with command line options.
# TODO: allow some user-specified seek to skip over any
# headers / partition table, e.g for DOSEMU disk images.
my ($disk, $lba, $help); # options: target, lba flag, help flag
my ($heads, $sects, $posn); # options: heads geo, sect geo, posn geo
my ($drive); # option: BIOS drive number
my ($fsboot, $bootcode); # strings used as arrays
my ($nasmopt, $bootsource); # strings
my ($i, $fat); # numbers
my $bootdir = " " . dirname($0) . "/bootsecs/"; # boot sector source dir
my $result = GetOptions("disk=s"=>\$disk, "lba!"=>\$lba, "help!"=>\$help,
"heads=i"=>\$heads, "sectors=i"=>\$sects, "offset=i"=>\$posn,
"drive=i"=>\$drive);
# "o"=>\$oflag, "verbose"=>\$verbosebool, "string=s"=>\$stringmandatory
# "string:s"=>stringoptional ... similar for i integer and f float.
# @ARGV contains the unprocessed rest if $ARGV[0] after this.
if (($help) || (!defined $disk)) {
print "FreeDOS boot sector SYS for Linux v1.1, public domain by Eric Auer 2004-2008.\n";
print " Puts a FreeDOS boot sector on a FAT (12/16/32) filesystem.\n";
print " Note: FreeDOS boot sectors, kernel.sys and command.com license is GPL.\n";
print " You still have to copy the kernel.sys and command.com files yourself!\n";
print "\nUsage:\n";
print basename($0) . " --disk=file [--lba] [--heads=n] [--sectors=n] [--offset=n] [--drive=n]\n";
print "\nOptions:\n";
print " --disk=file_or_device target filesystem, for example /dev/sdx1\n";
print " --lba selects boot sectors with LBA BIOS support\n";
print " --heads=head_count overrides CHS setting 'heads' (cyls need no setting)\n";
print " --sectors=sector_count overrides CHS setting 'sectors per...'\n";
print " --offset=sector_count overrides partition location on disk setting\n";
print " --drive=drive_number overrides BIOS drive (255=auto, 0=A:, 128=harddisk)\n";
print "\nHints:\n";
print "If you formatted the drive with DOS or Windows, you need no overrides.\n";
print " If CHS *x?x? shows 0 for one of the ?, override it. Use offset 0 for\n";
print " diskettes, non-0 for partitions. Use overrides after using mkdosfs.\n";
print "Check the output of fdisk -l -u /dev/??? to select overrides. Example:\n";
print " fdisk -l -u /dev/hdb shows '... 255 HEADS, 63 SECTORS/track ...'\n";
print " Use OFFSET 63 for hdb1: '/dev/hdb1 * 63 1028159 514048+ 6 FAT16'\n";
exit(1);
}
sysopen(IMAGE, $disk, O_RDWR)
|| die "cannot open filesystem $disk for read/write access";
binmode(IMAGE); # not normally needed
if (sysread(IMAGE, $fsboot, 512) != 512) {
die "cannot read boot sector from filesystem $disk";
}
sysseek(IMAGE, 0, 0) || die "cannot rewind to filesystem start";
# substr syntax: substr(input, offset, length)
if (substr($fsboot, 510, 2) ne "\x55\xaa") {
die "boot sector magic value missing in filesystem";
}
if (substr($fsboot, 0x0b, 2) ne "\x00\x02") {
die "not 512 bytes per sector\n";
}
if (substr($fsboot, 0x11, 2) eq "\x00\x00") {
# other possible detection: 16 bit FAT1x size at 0x16 is zero
$fat = 32; # FAT32: zero FAT1x root directory entries
if (($lba) && (substr($fsboot, 0x1c) eq "\x00\x00\x00\x00")) {
print STDERR "FAT32 LBA warning: Hidden sector count is zero!\n";
}
} else {
$fat = 16;
# if 16 bit FAT1x size at 0x16 is at least 16, we have FAT16
# actually it would be more correct to use the sector count
# (possibly the 32 bit one), subtract reserved (boot) sector
# count and fat count * fat size, and divide by sec per clust
# and finally check if the result is at least 4096.
# unpack: v is "unsigned, little endian, 16bit", V is same for 32bit
my $fatsize = unpack('v', substr($fsboot, 0x16, 2));
# print STDERR "FAT1x size: $fatsize\n";
# letters: c/C signed/unsigned 8 bit, s/S 16, l/L 32...
# network / big endian: n/N (16/32), VAX / little endian: v/V.
if ($fatsize < 16) { $fat = 12; } # FAT very small: FAT12
}
$bootsource = $bootdir . "boot.asm"; # source for FAT12 and FAT16
if ($fat == 12) { $nasmopt = "-dISFAT12"; } # can do both CHS and LBA
if ($fat == 16) { $nasmopt = "-dISFAT16"; } # can do both CHS and LBA
if ($fat == 32) {
$nasmopt = ""; # no options for FAT32
$bootsource = $bootdir . "boot32.asm"; # CHS version
if ($lba) { $bootsource =~ s/32/32lb/; } # LBA version
}
print STDERR "DOS boot sector for $disk will be created by:\n";
print STDERR "\tnasm -o /dev/stdout $nasmopt $bootsource\n";
open(BOOTSECT, "nasm -o /dev/stdout $nasmopt $bootsource |")
|| die "cannot fork";
binmode(BOOTSECT); # not normally needed
if (sysread(BOOTSECT, $bootcode, 512) != 512) {
die "boot sector compilation problem";
}
close(BOOTSECT) || die "boot sector $bootsource nasm error $?";
if (substr($bootcode, 510, 2) ne "\x55\xaa") {
die "magic value missing in compiled boot sector";
}
substr($bootcode, 3, 8) = "LINUX4.1"; # place OEM ID, the
# "4.1" is to please M$
if ($fat != 32) {
$i = 0x3e-11; # size of classic BPB
} else {
$i = 0x5a-11; # size of FAT32 xBPB
}
substr($bootcode, 3+8, $i) = substr($fsboot, 3+8, $i); # copy BPB
# bpb: after jump and OEM ID ... initial jump target: typically $i+11
print STDERR "Using FAT$fat" . (($lba) ? " LBA." : ".")
. " Partn offset " . unpack('V', substr($fsboot, 11+17, 4)) . ","
. sprintf(" CHS *x%dx%d ", unpack('v', substr($fsboot, 11+15, 2)),
unpack('v', substr($fsboot, 11+13, 2)))
# unpack: c is signed byte char, C is unsigned
. sprintf(" Drive %x,", unpack('C', substr($fsboot, 11+$i-26, 1)))
. sprintf(" (0x%x, ", unpack('C', substr($fsboot, 11+$i-25, 1)))
. sprintf("0x%x),\n", unpack('C', substr($fsboot, 11+$i-24, 1)))
. sprintf("SerNo %X-%X, ",
unpack('v', substr($fsboot, 11+$i-21, 2)),
unpack('v', substr($fsboot, 11+$i-23, 2)))
. "Strings '" . substr($fsboot, 11+$i-19, 11) . "', " # override in rootdir
. " '" . substr($fsboot,11+$i-8,8) . "'.\n";
if (defined $drive && $drive>-1) { # at 11+$i-26 = 24/40 drive (255=auto)
substr($bootcode, 11+$i-26, 1) = pack('C', $drive);
print STDERR "Drive changed to $drive\n";
}
if (defined $posn && $posn>-1) { # at 11+17 = 1c partition offset
substr($bootcode, 11+17, 4) = pack('V', $posn);
print STDERR "Partition offset changed to $posn\n";
}
if (defined $sects && $sects>0) { # at 11+13 = 18 sectors per track/cylinder
substr($bootcode, 11+13, 2) = pack('v', $sects); # should be 1..63
print STDERR "CHS sector count changed to $sects\n";
}
if (defined $heads && $heads>0) { # at 11+15 = 1a heads
substr($bootcode, 11+15, 2) = pack('v', $heads); # should be 1..255 (256?)
print STDERR "CHS head count changed to $heads\n";
}
syswrite(IMAGE, $bootcode, 512) || die "could not write updated boot sector";
close(IMAGE);
print STDERR "Boot sector successfully updated.\n";
exit(0);
