- $bochsbin = search_path ($bin);
- $bochsshare = "$bochsbin/../share/bochs";
- $romimage = "$bochsshare/BIOS-bochs-latest";
- $vgaromimage = "$bochsshare/VGABIOS-lgpl-latest";
-
- open (BOCHSRC, ">bochsrc.txt") or die "bochsrc.txt: create: $!\n";
- print BOCHSRC "romimage: file=$romimage, address=0xf0000\n";
- print BOCHSRC "vgaromimage: $vgaromimage\n";
- print BOCHSRC bochs_disk_line ("ata0-master", $disks[0]);
- print BOCHSRC bochs_disk_line ("ata0-slave", $disks[1]);
- print BOCHSRC bochs_disk_line ("ata1-master", $disks[2]);
- print BOCHSRC bochs_disk_line ("ata1-slave", $disks[3]);
- print BOCHSRC "boot: c\n";
- print BOCHSRC "ips: 1000000\n";
- print BOCHSRC "clock: sync=none, time0=0\n";
- print BOCHSRC "megs: $mem\n";
- print BOCHSRC "com1: dev=/dev/tty\n" if $headless;
- close (BOCHSRC);
- run_command ($bin, '-q');
- } elsif ($sim eq 'qemu') {
- my (@cmd) = ('qemu');
- push (@cmd, '-hda', $disks[0]) if defined $disks[0];
- push (@cmd, '-hdb', $disks[1]) if defined $disks[1];
- push (@cmd, '-hdc', $disks[2]) if defined $disks[2];
- push (@cmd, '-hdd', $disks[3]) if defined $disks[3];
- push (@cmd, '-m', $mem);
- push (@cmd, '-nographic') if $headless;
- push (@cmd, '-S') if $debug eq 'monitor';
- push (@cmd, '-s') if $debug eq 'gdb';
- run_command (@cmd);
- }
+
+ # If there's no file name, we're done.
+ next if !defined ($disk->{FILE_NAME});
+
+ if ($disk->{FILE_NAME} =~ /^\d+(\.\d+)?|\.\d+$/) {
+ # Create a temporary disk of approximately the specified
+ # size in megabytes.
+ die "OS disk can't be temporary\n" if $disk == $disks{OS};
+
+ my ($mb) = $disk->{FILE_NAME};
+ undef $disk->{FILE_NAME};
+
+ my ($cyl_size) = 512 * 16 * 63;
+ extend_disk ($disk, ceil ($mb * 2) * $cyl_size);
+ } else {
+ # The file must exist and have nonzero size.
+ -e $disk->{FILE_NAME} or die "$disk->{FILE_NAME}: stat: $!\n";
+ -s _ or die "$disk->{FILE_NAME}: disk has zero size\n";
+ }
+ }
+
+ # Warn about (potentially) missing disks.
+ die "Cannot find OS disk\n" if !defined $disks{OS}{FILE_NAME};
+ if (my ($project) = `pwd` =~ /\b(threads|userprog|vm|filesys)\b/) {
+ if ((grep ($project eq $_, qw (userprog vm filesys)))
+ && !defined ($disks{FS}{FILE_NAME})) {
+ print STDERR "warning: it looks like you're running the $project ";
+ print STDERR "project, but no file system disk is present\n";
+ }
+ if ($project eq 'vm' && !defined $disks{SWAP}{FILE_NAME}) {
+ print STDERR "warning: it looks like you're running the $project ";
+ print STDERR "project, but no swap disk is present\n";
+ }
+ }
+}
+\f
+# Prepare the scratch disk for gets and puts.
+sub prepare_scratch_disk {
+ if (@puts) {
+ # Write ustar header and data for each file.
+ put_scratch_file ($_->[0],
+ defined $_->[1] ? $_->[1] : $_->[0])
+ foreach @puts;
+
+ # Write end-of-archive marker.
+ print { $disks{SCRATCH}{HANDLE} } "\0" x 1024;
+ }
+
+ # Make sure the scratch disk is big enough to get big files.
+ extend_disk ($disks{SCRATCH}, @gets * 1024 * 1024) if @gets;
+}
+
+# Read "get" files from the scratch disk.
+sub finish_scratch_disk {
+ # We need to start reading the scratch disk from the beginning again.
+ if (@gets) {
+ close ($disks{SCRATCH}{HANDLE});
+ undef ($disks{SCRATCH}{HANDLE});
+ }
+
+ # Read each file.
+ # If reading fails, delete that file and all subsequent files.
+ my ($ok) = 1;
+ foreach my $get (@gets) {
+ my ($name) = defined ($get->[1]) ? $get->[1] : $get->[0];
+ my ($error) = get_scratch_file ($name);
+ if ($error) {
+ print STDERR "getting $name failed ($error)\n";
+ die "$name: unlink: $!\n" if !unlink ($name) && !$!{ENOENT};
+ $ok = 0;
+ }
+ }
+}
+
+# mk_ustar_field($number, $size)
+#
+# Returns $number in a $size-byte numeric field in the format used by
+# the standard ustar archive header.
+sub mk_ustar_field {
+ my ($number, $size) = @_;
+ my ($len) = $size - 1;
+ my ($out) = sprintf ("%0${len}o", $number) . "\0";
+ die "$number: too large for $size-byte octal ustar field\n"
+ if length ($out) != $size;
+ return $out;
+}
+
+# calc_ustar_chksum($s)
+#
+# Calculates and returns the ustar checksum of 512-byte ustar archive
+# header $s.
+sub calc_ustar_chksum {
+ my ($s) = @_;
+ die if length ($s) != 512;
+ substr ($s, 148, 8, ' ' x 8);
+ return unpack ("%32a*", $s);
+}
+
+# put_scratch_file($src_file_name, $dst_file_name).
+#
+# Copies $src_file_name into the scratch disk for extraction as
+# $dst_file_name.
+sub put_scratch_file {
+ my ($src_file_name, $dst_file_name) = @_;
+ my ($disk_handle, $disk_file_name) = open_disk ($disks{SCRATCH});
+
+ print "Copying $src_file_name to scratch partition...\n";
+
+ # ustar format supports up to 100 characters for a file name, and
+ # even longer names given some common properties, but our code in
+ # the Pintos kernel only supports at most 99 characters.
+ die "$dst_file_name: name too long (max 99 characters)\n"
+ if length ($dst_file_name) > 99;
+
+ # Compose and write ustar header.
+ stat $src_file_name or die "$src_file_name: stat: $!\n";
+ my ($size) = -s _;
+ my ($header) = (pack ("a100", $dst_file_name) # name
+ . mk_ustar_field (0644, 8) # mode
+ . mk_ustar_field (0, 8) # uid
+ . mk_ustar_field (0, 8) # gid
+ . mk_ustar_field ($size, 12) # size
+ . mk_ustar_field (1136102400, 12) # mtime
+ . (' ' x 8) # chksum
+ . '0' # typeflag
+ . ("\0" x 100) # linkname
+ . "ustar\0" # magic
+ . "00" # version
+ . "root" . ("\0" x 28) # uname
+ . "root" . ("\0" x 28) # gname
+ . "\0" x 8 # devmajor
+ . "\0" x 8 # devminor
+ . ("\0" x 155)) # prefix
+ . "\0" x 12; # pad to 512 bytes
+ substr ($header, 148, 8) = mk_ustar_field (calc_ustar_chksum ($header), 8);
+ write_fully ($disk_handle, $disk_file_name, $header);
+
+ # Copy file data.
+ my ($put_handle);
+ sysopen ($put_handle, $src_file_name, O_RDONLY)
+ or die "$src_file_name: open: $!\n";
+ copy_file ($put_handle, $src_file_name, $disk_handle, $disk_file_name,
+ $size);
+ die "$src_file_name: changed size while being read\n"
+ if $size != -s $put_handle;
+ close ($put_handle);
+
+ # Round up disk data to beginning of next sector.
+ write_fully ($disk_handle, $disk_file_name, "\0" x (512 - $size % 512))
+ if $size % 512;
+}
+
+# get_scratch_file($file).
+#
+# Copies from the scratch disk to $file.
+# Returns 1 if successful, 0 on failure.
+sub get_scratch_file {
+ my ($get_file_name) = @_;
+ my ($disk_handle, $disk_file_name) = open_disk ($disks{SCRATCH});
+
+ print "Copying $get_file_name out of $disk_file_name...\n";
+
+ # Read ustar header sector.
+ my ($header) = read_fully ($disk_handle, $disk_file_name, 512);
+ return "scratch disk tar archive ends unexpectedly"
+ if $header eq ("\0" x 512);
+
+ # Verify magic numbers.
+ return "corrupt ustar signature" if substr ($header, 257, 6) ne "ustar\0";
+ return "invalid ustar version" if substr ($header, 263, 2) ne '00';
+
+ # Verify checksum.
+ my ($chksum) = oct (unpack ("Z*", substr ($header, 148, 8)));
+ my ($correct_chksum) = calc_ustar_chksum ($header);
+ return "checksum mismatch" if $chksum != $correct_chksum;
+
+ # Get type.
+ my ($typeflag) = substr ($header, 156, 1);
+ return "not a regular file" if $typeflag ne '0' && $typeflag ne "\0";
+
+ # Get size.
+ my ($size) = oct (unpack ("Z*", substr ($header, 124, 12)));
+ return "bad size $size\n" if $size < 0;
+
+ # Copy file data.
+ my ($get_handle);
+ sysopen ($get_handle, $get_file_name, O_WRONLY | O_CREAT, 0666)
+ or die "$get_file_name: create: $!\n";
+ copy_file ($disk_handle, $disk_file_name, $get_handle, $get_file_name,
+ $size);
+ close ($get_handle);
+
+ # Skip forward in disk up to beginning of next sector.
+ read_fully ($disk_handle, $disk_file_name, 512 - $size % 512)
+ if $size % 512;
+
+ return 0;
+}
+\f
+# Prepares the arguments to pass to the Pintos kernel,
+# and then write them into Pintos bootloader.
+sub prepare_arguments {
+ my (@args);
+ push (@args, shift (@kernel_args))
+ while @kernel_args && $kernel_args[0] =~ /^-/;
+ push (@args, 'extract') if @puts;
+ push (@args, @kernel_args);
+ push (@args, 'append', $_->[0]) foreach @gets;
+ write_cmd_line ($disks{OS}, @args);