Change -nv to -v, -ns to -s, for consistency.
[pintos-anon] / src / utils / pintos
1 #! /usr/bin/perl
2
3 $mem = 4;
4 $serial_out = 1;
5 while (@ARGV) {
6     my ($arg) = shift (@ARGV);
7     if ($arg =~ /--(bochs|qemu|gsx)$/) {
8         die "$arg conflicts with --$sim\n" if defined $sim;
9         $sim = $1;
10     } elsif ($arg =~ /--(no-debug|monitor|gdb)$/) {
11         die "$debug conflicts with --$debug" if defined $debug;
12         $debug = $1;
13     } elsif ($arg eq 'run') {
14         run_vm (@ARGV);
15         exit 0;
16     } elsif ($arg =~ /^--mem(?:ory)?=(\d+)/) {
17         $mem = $1;
18     } elsif ($arg eq '-m') {
19         die "-m needs integer argument\n" if !@ARGV || $ARGV[0] !~ /^-?\d+$/;
20         $mem = shift (@ARGV);
21     } elsif ($arg eq '-j') {
22         die "-j need random seed argument\n" if !@ARGV;
23         die "-j need integer argument\n" if $ARGV[0] !~ /^-?\d+$/;
24         $jitter = shift (@ARGV);
25     } elsif ($arg =~ /--jitter=(-?\d+)$/) {
26         $jitter = $1;
27     } elsif ($arg eq '--no-vga' || $arg eq '-v') {
28         print "warning: --no-vga conflicts with --terminal\n"
29             if $vga eq 'terminal';
30         $vga = 'none';
31     } elsif ($arg eq '--no-serial' || $arg eq '-s') {
32         $serial_out = 0;
33     } elsif ($arg eq '--terminal' || $arg eq '-t') {
34         print "warning: --terminal conflicts with --no-vga\n"
35             if $vga eq 'none';
36         $vga = 'terminal';
37     } elsif ($arg eq 'make-disk') {
38         usage () if @ARGV != 2;
39         my ($file, $mb) = @ARGV;
40         usage () if $mb !~ /^\d+(\.\d+)?|\.\d+$/;
41         die "$file: already exists\n" if -e $file;
42
43         create_disk ($file, int ($mb * 1008));
44         exit 0;
45     } elsif ($arg eq 'put') {
46         usage () if @ARGV != 1 && @ARGV != 2;
47         my ($hostfn, $guestfn) = @ARGV;
48         $guestfn = $hostfn if !defined $guestfn;
49
50         # Create scratch disk from file.
51         die "$hostfn: $!\n" if ! -e $hostfn;
52         my ($size) = -s _;
53         copy_pad ($hostfn, "scratch.dsk", 512);
54
55         # Do copy.
56         run_vm ("-ci", $guestfn, $size, "-q");
57         exit 0;
58     } elsif ($arg eq 'get') {
59         usage () if @ARGV != 1 && @ARGV != 2;
60         my ($guestfn, $hostfn) = @ARGV;
61         $hostfn = $guestfn if !defined $hostfn;
62         die "$hostfn: already exists\n" if -e $file;
63
64         # Create scratch disk big enough for any file in the filesystem
65         # (modulo sparse files).
66         die "fs.dsk: $!\n" if ! -e "fs.dsk";
67         my ($fs_size) = -s _;
68         my ($scratch_size) = -s "scratch.dsk";
69         $scratch_size = 0 if !defined $scratch_size;
70         create_disk ("scratch.dsk", $fs_size / 1024 + 16)
71             if $scratch_size < $fs_size + 16384;
72
73         # Do copy.
74         run_vm ("-co", $guestfn, "-q");
75
76         # Read out scratch disk.
77         print "copying $guestfn from scratch.dsk to $hostfn...\n";
78         open (SRC, "<scratch.dsk") or die "scratch.dsk: open: $!\n";
79         open (DST, ">$hostfn") or die "$hostfn: create: $!\n";
80         my ($input);
81         read (SRC, $input, 512) == 512 or die "scratch.dsk: read error\n";
82         my ($size) = unpack ("%V", $input);
83         $size != 0xffffffff or die "$guestfn: too big for scratch.dsk?";
84         read (SRC, $src, $size) == $size or die "scratch.dsk: read error\n";
85         print DST $src or die "$hostfn: write error\n";
86         close (DST);
87         close (SRC);
88
89         exit 0;
90     } elsif ($arg eq 'help' || $arg eq '--help') {
91         usage (0);
92     } else {
93         die "unknown option `$arg'\n";
94     }
95 }
96 usage ();
97
98 sub usage {
99     my ($exitcode) = @_;
100     $exitcode = 1 unless defined $exitcode;
101     print "pintos, a utility for invoking Pintos in a simulator\n";
102     print "Usage: pintos [OPTION...] COMMAND [ARG...]\n";
103     print "where COMMAND is one of the following:\n";
104     print "  run [CMDLINE...]        run a VM in the simulator\n";
105     print "  make-disk FILE.DSK SIZE create FILE.DSK as empty SIZE MB disk\n";
106     print "  put HOSTFN [GUESTFN]    copy HOSTFN into VM (as GUESTFN)\n";
107     print "  get GUESTFN [HOSTFN]    copy GUESTFN out of VM (to HOSTFN)\n";
108     print "  help                    print this help message and exit\n";
109     print "Simulator options:\n";
110     print "  --bochs          (default) Use Bochs as simulator\n";
111     print "  --qemu           Use qemu as simulator\n";
112     print "  --gsx            Use VMware GSX Server 3.x as simulator\n";
113     print "Debugger options:\n";
114     print "  --no-debug       (default) No debugger\n";
115     print "  --monitor        Debug with simulator's monitor\n";
116     print "  --gdb            Debug with gdb\n";
117     print "Display options: (default is VGA + serial)\n";
118     print "  -v, --no-vga     No VGA display\n";
119     print "  -s, --no-serial  No serial output\n";
120     print "  -t, --terminal   Display VGA in terminal (Bochs only)\n";
121     print "VM options:\n";
122     print "  -j SEED          Randomize timer interrupts (Bochs only)\n";
123     print "  -m, --mem=MB     Run VM with MB megabytes of physical memory\n";
124     exit $exitcode;
125 }
126
127 sub copy_pad {
128     my ($src, $dst, $blocksize) = @_;
129     run_command ("dd", "if=$src", "of=$dst", "bs=$blocksize", "conv=sync");
130 }
131
132 sub create_disk {
133     my ($disk, $kb) = @_;
134     run_command ("dd", "if=/dev/zero", "of=$disk", "bs=1024", "count=$kb");
135 }
136
137 sub run_vm {
138     my (@disks) = (undef, undef, undef, undef);
139
140     $sim = "bochs" if !defined $sim;
141     $debug = "no-debug" if !defined $debug;
142     $vga = "window" if !defined $vga;
143
144     $disks[0] = "os.dsk";
145     $disks[1] = "fs.dsk" if -e "fs.dsk";
146     $disks[2] = "scratch.dsk" if -e "scratch.dsk";
147     $disks[3] = "swap.dsk" if -e "swap.dsk";
148
149     die "$disks[0]: can't find OS disk\n" if ! -e $disks[0];
150     write_cmd_line ($disks[0], @_);
151
152     if ($sim eq 'bochs') {
153         if ($debug eq 'no-debug') {
154             $bin = 'bochs';
155         } elsif ($debug eq 'monitor') {
156             $bin = 'bochs-dbg';
157         } elsif ($debug eq 'gdb') {
158             $bin = 'bochs-gdb';
159         }
160         $bochsbin = search_path ($bin);
161         $bochsshare = "$bochsbin/../share/bochs";
162         $romimage = "$bochsshare/BIOS-bochs-latest";
163         $vgaromimage = "$bochsshare/VGABIOS-lgpl-latest";
164
165         open (BOCHSRC, ">bochsrc.txt") or die "bochsrc.txt: create: $!\n";
166         print BOCHSRC "romimage: file=$romimage, address=0xf0000\n";
167         print BOCHSRC "vgaromimage: $vgaromimage\n";
168         print BOCHSRC bochs_disk_line ("ata0-master", $disks[0]);
169         print BOCHSRC bochs_disk_line ("ata0-slave", $disks[1]);
170         print BOCHSRC "ata1: enabled=1, ioaddr1=0x170, ioaddr2=0x370, irq=15\n"
171             if defined ($disks[2]) || defined ($disks[3]);
172         print BOCHSRC bochs_disk_line ("ata1-master", $disks[2]);
173         print BOCHSRC bochs_disk_line ("ata1-slave", $disks[3]);
174         print BOCHSRC "boot: c\n";
175         print BOCHSRC "ips: 1000000\n";
176         print BOCHSRC "clock: sync=none, time0=0\n";
177         print BOCHSRC "megs: $mem\n";
178         print BOCHSRC "log: bochsout.txt\n";
179         if ($vga ne 'terminal') {
180             print BOCHSRC "com1: enabled=1, dev=/dev/stdout\n"
181                 if $serial_out;
182             print BOCHSRC "display_library: nogui\n"
183                 if $vga eq 'none';
184         } else {
185             print BOCHSRC "display_library: term\n";
186         }
187         close (BOCHSRC);
188         @cmd = ($bin, '-q');
189         push (@cmd, '-j', $jitter) if defined $jitter;
190         run_command_no_die (@cmd);
191     } elsif ($sim eq 'qemu') {
192         print "warning: qemu doesn't support --terminal\n"
193             if $vga eq 'terminal';
194         print "warning: qemu doesn't support jitter\n"
195             if defined $jitter;
196         my (@cmd) = ('qemu');
197         push (@cmd, '-hda', $disks[0]) if defined $disks[0];
198         push (@cmd, '-hdb', $disks[1]) if defined $disks[1];
199         push (@cmd, '-hdc', $disks[2]) if defined $disks[2];
200         push (@cmd, '-hdd', $disks[3]) if defined $disks[3];
201         push (@cmd, '-m', $mem);
202         push (@cmd, '-nographic') if $vga eq 'none';
203         push (@cmd, '-serial', 'stdio') if $serial_out && $vga ne 'none';
204         push (@cmd, '-S') if $debug eq 'monitor';
205         push (@cmd, '-s') if $debug eq 'gdb';
206         run_command (@cmd);
207     } elsif ($sim eq 'gsx') {
208         print "warning: VMware GSX Server doesn't support --$debug\n"
209             if $debug ne 'no-debug';
210         print "warning: VMware GSX Server doesn't support --no-vga\n"
211             if $vga eq 'none';
212         print "warning: VMware GSX Server doesn't support --terminal\n"
213             if $vga eq 'terminal';
214         print "warning: VMware GSX Server doesn't support jitter\n"
215             if defined $jitter;
216
217         open (VMX, ">pintos.vmx") or die "pintos.vmx: create: $!\n";
218         chmod 0777 & ~umask, "pintos.vmx";
219         print VMX "#! /usr/bin/vmware -G\n";
220         print VMX "config.version = 6\n";
221         print VMX "guestOS = \"linux\"\n";
222         print VMX "floppy0.present = FALSE\n";
223
224         if (! -e 'null.bin') {
225             open (NULL, ">null.bin") or die "null.bin: create: $!\n";
226             close (NULL);
227         }
228
229         for (my ($i) = 0; $i < 4; $i++) {
230             my ($dsk) = $disks[$i];
231             next if !defined $dsk;
232             $device = "ide" . int ($i / 2) . ":" . ($i % 2);
233
234             my ($pln) = $dsk;
235             $pln =~ s/\.dsk//;
236             $pln .= ".pln";
237
238             print VMX "\n$device.present = TRUE\n";
239             print VMX "$device.deviceType = \"plainDisk\"\n";
240             print VMX "$device.fileName = \"$pln\"\n";
241
242             my (%geom) = disk_geometry ($dsk);
243             open (PLN, ">$pln") or die "$pln: create: $!\n";
244             print PLN "DRIVETYPE        ide\n";
245             print PLN "#vm|VERSION      2\n";
246             print PLN "#vm|TOOLSVERSION 2\n";
247             print PLN "CYLINDERS        $geom{C}\n";
248             print PLN "HEADS            $geom{H}\n";
249             print PLN "SECTORS          $geom{S}\n";
250             print PLN "#vm|CAPACITY     $geom{CAPACITY}\n";
251             print PLN "ACCESS \"$dsk\" 0 $geom{CAPACITY}\n";
252             close (PLN);
253         }
254         close (VMX);
255
256         use Cwd;
257         $vmx = getcwd () . "/pintos.vmx";
258         system ("vmware-cmd -s register $vmx >&/dev/null");
259         system ("vmware-cmd $vmx stop hard >&/dev/null");
260         system ("vmware -l -G -x -q $vmx");
261         system ("vmware-cmd $vmx stop hard >&/dev/null");
262     }
263 }
264
265 sub write_cmd_line {
266     my ($disk, @args) = @_;
267
268     die "command line includes empty string" if grep (/^$/, @args);
269     $args = join ("\0", @args) . "\0\0";
270     die "command line exceeds 128 bytes" if length ($args) > 128;
271     $args .= "\0" x (128 - length ($args));
272
273     print "writing command line to $disk...\n";
274     open (DISK, "+<$disk") or die "$disk: open: $!\n";
275     seek (DISK, 0x17e, 0) or die "$disk: seek: $!\n";
276     syswrite (DISK, $args) or die "$disk: write: $!\n";
277     close (DISK) or die "$disk: close: $!\n";
278 }
279
280 sub run_command {
281     print join (' ', @_), "\n";
282     die "command failed\n" if system (@_);
283 }
284
285 sub run_command_no_die {
286     print join (' ', @_), "\n";
287     system (@_);
288 }
289
290 sub search_path {
291     my ($target) = @_;
292     for $dir (split (':', $ENV{PATH})) {
293         return $dir if -e "$dir/$target";
294     }
295     die "$target not in PATH\n";
296 }
297
298 sub bochs_disk_line {
299     my ($device, $file) = @_;
300     return "" if !defined $file;
301     my (%geom) = disk_geometry ($file);
302     return "$device: type=disk, path=$file, mode=flat, "
303         . "cylinders=$geom{C}, heads=$geom{H}, spt=$geom{S}, "
304         . "translation=none\n";
305 }
306
307 sub disk_geometry {
308     my ($file) = @_;
309     my ($size) = -s $file;
310     die "$file: stat: $!\n" if !defined $size;
311     die "$file: size not a multiple of 512 bytes\n" if $size % 512;
312     $cylinders = int ($size / (512 * 16 * 63));
313     $cylinders++ if $size % (512 * 16 * 63);
314
315     return (CAPACITY => $size / 512,
316             C => $cylinders,
317             H => 16,
318             S => 63);
319 }