use strict;
use warnings;
use POSIX;
-use Getopt::Long;
+use Getopt::Long qw(:config bundling);
use Fcntl 'SEEK_SET';
-GetOptions ("h|help" => sub { usage (0); })
+# Read Pintos.pm from the same directory as this program.
+BEGIN { my $self = $0; $self =~ s%/+[^/]*$%%; require "$self/Pintos.pm"; }
+
+our ($disk_fn); # Output disk file name.
+our (%parts); # Partitions.
+our ($format); # "partitioned" (default) or "raw"
+our (%geometry); # IDE disk geometry.
+our ($align); # Align partitions on cylinders?
+our ($loader_fn); # File name of loader.
+our ($include_loader); # Include loader?
+our (@kernel_args); # Kernel arguments.
+
+if (grep ($_ eq '--', @ARGV)) {
+ @kernel_args = @ARGV;
+ @ARGV = ();
+ while ((my $arg = shift (@kernel_args)) ne '--') {
+ push (@ARGV, $arg);
+ }
+}
+
+GetOptions ("h|help" => sub { usage (0); },
+
+ "kernel=s" => \&set_part,
+ "filesys=s" => \&set_part,
+ "scratch=s" => \&set_part,
+ "swap=s" => \&set_part,
+
+ "filesys-size=s" => \&set_part,
+ "scratch-size=s" => \&set_part,
+ "swap-size=s" => \&set_part,
+
+ "kernel-from=s" => \&set_part,
+ "filesys-from=s" => \&set_part,
+ "scratch-from=s" => \&set_part,
+ "swap-from=s" => \&set_part,
+
+ "format=s" => \$format,
+ "loader:s" => \&set_loader,
+ "no-loader" => \&set_no_loader,
+ "geometry=s" => \&set_geometry,
+ "align=s" => \&set_align)
or exit 1;
-usage (1) if @ARGV != 2;
+usage (1) if @ARGV != 1;
+
+$disk_fn = $ARGV[0];
+die "$disk_fn: already exists\n" if -e $disk_fn;
+
+# Sets the loader to copy to the MBR.
+sub set_loader {
+ die "can't specify both --loader and --no-loader\n"
+ if defined ($include_loader) && !$include_loader;
+ $include_loader = 1;
+ $loader_fn = $_[1] if $_[1] ne '';
+}
+
+# Disables copying a loader to the MBR.
+sub set_no_loader {
+ die "can't specify both --loader and --no-loader\n"
+ if defined ($include_loader) && $include_loader;
+ $include_loader = 0;
+}
+
+# Figure out whether to include a loader.
+$include_loader = exists ($parts{KERNEL}) && $format eq 'partitioned'
+ if !defined ($include_loader);
+die "can't write loader to raw disk\n" if $include_loader && $format eq 'raw';
+die "can't write command-line arguments without --loader or --kernel\n"
+ if @kernel_args && !$include_loader;
+print STDERR "warning: --loader only makes sense without --kernel "
+ . "if this disk will be used to load a kernel from another disk\n"
+ if $include_loader && !exists ($parts{KERNEL});
+
+# Open disk.
+my ($disk_handle);
+open ($disk_handle, '>', $disk_fn) or die "$disk_fn: create: $!\n";
-my ($disk, $mb) = @ARGV;
-die "$disk: already exists\n" if -e $disk;
-die "\"$mb\" is not a valid size in megabytes\n"
- if $mb <= 0 || $mb > 1024 || $mb !~ /^\d+(\.\d+)?|\.\d+/;
+# Read loader.
+my ($loader);
+$loader = read_loader ($loader_fn) if $include_loader;
-my ($cyl_cnt) = ceil ($mb * 2);
-my ($cyl_bytes) = 512 * 16 * 63;
-my ($bytes) = $cyl_bytes * $cyl_cnt;
+# Write disk.
+my (%disk) = %parts;
+$disk{DISK} = $disk_fn;
+$disk{HANDLE} = $disk_handle;
+$disk{ALIGN} = $align;
+$disk{GEOMETRY} = %geometry;
+$disk{FORMAT} = $format;
+$disk{LOADER} = $loader;
+$disk{ARGS} = \@kernel_args;
+assemble_disk (%disk);
-open (DISK, '>', $disk) or die "$disk: create: $!\n";
-sysseek (DISK, $bytes - 1, SEEK_SET) or die "$disk: seek: $!\n";
-syswrite (DISK, "\0", 1) == 1 or die "$disk: write: $!\n";
-close (DISK) or die "$disk: close: $!\n";
+# Done.
+exit 0;
sub usage {
print <<'EOF';
pintos-mkdisk, a utility for creating Pintos virtual disks
-Usage: pintos DISKFILE MB
-where DISKFILE is the file to use for the disk
- and MB is the disk size in (approximate) megabytes.
-Options:
- -h, --help Display this help message.
+Usage: pintos-mkdisk [OPTIONS] DISK [-- ARGUMENT...]
+where DISK is the virtual disk to create,
+ each ARGUMENT is inserted into the command line written to DISK,
+ and each OPTION is one of the following options.
+Partition options: (where PARTITION is one of: kernel filesys scratch swap)
+ --PARTITION=FILE Use a copy of FILE for the given PARTITION
+ --PARTITION-size=SIZE Create an empty PARTITION of the given SIZE in MB
+ --PARTITION-from=DISK Use of a copy of the given PARTITION in DISK
+ (There is no --kernel-size option.)
+Output disk options:
+ --format=partitioned Write partition table to output (default)
+ --format=raw Do not write partition table to output
+ (Pintos can only use partitioned disks.)
+Partitioned format output options:
+ --loader[=FILE] Get bootstrap loader from FILE (default: loader.bin
+ if --kernel option is specified, empty otherwise)
+ --no-loader Do not include a bootstrap loader
+ --geometry=H,S Use H head, S sector geometry (default: 16, 63)
+ --geometry=zip Use 64 head, 32 sector geometry for USB-ZIP boot
+ per http://syslinux.zytor.com/usbkey.php
+ --align=bochs Round size to cylinder for Bochs support (default)
+ --align=full Align partition boundaries to cylinder boundary to
+ let fdisk guess correct geometry and quiet warnings
+ --align=none Don't align partitions at all, to save space
+Other options:
+ -h, --help Display this help message.
EOF
- exit (@_);
+ exit ($_[0]);
}