diff options
| author | manuel <manuel@mausz.at> | 2012-03-27 11:51:08 +0200 |
|---|---|---|
| committer | manuel <manuel@mausz.at> | 2012-03-27 11:51:08 +0200 |
| commit | 4f670845ff9ab6c48bcb5f7bf4d4ef6dc3c3064b (patch) | |
| tree | 868c52e06f207b5ec8a3cc141f4b8b2bdfcc165c /utils/Pintos.pm | |
| parent | eae0bd57f0a26314a94785061888d193d186944a (diff) | |
| download | progos-4f670845ff9ab6c48bcb5f7bf4d4ef6dc3c3064b.tar.gz progos-4f670845ff9ab6c48bcb5f7bf4d4ef6dc3c3064b.tar.bz2 progos-4f670845ff9ab6c48bcb5f7bf4d4ef6dc3c3064b.zip | |
reorganize file structure to match the upstream requirements
Diffstat (limited to 'utils/Pintos.pm')
| -rw-r--r-- | utils/Pintos.pm | 491 |
1 files changed, 491 insertions, 0 deletions
diff --git a/utils/Pintos.pm b/utils/Pintos.pm new file mode 100644 index 0000000..70df40d --- /dev/null +++ b/utils/Pintos.pm | |||
| @@ -0,0 +1,491 @@ | |||
| 1 | # Pintos helper subroutines. | ||
| 2 | |||
| 3 | # Number of bytes available for the loader at the beginning of the MBR. | ||
| 4 | # Kernel command-line arguments follow the loader. | ||
| 5 | our $LOADER_SIZE = 314; | ||
| 6 | |||
| 7 | # Partition types. | ||
| 8 | my (%role2type) = (KERNEL => 0x20, | ||
| 9 | FILESYS => 0x21, | ||
| 10 | SCRATCH => 0x22, | ||
| 11 | SWAP => 0x23); | ||
| 12 | my (%type2role) = reverse %role2type; | ||
| 13 | |||
| 14 | # Order of roles within a given disk. | ||
| 15 | our (@role_order) = qw (KERNEL FILESYS SCRATCH SWAP); | ||
| 16 | |||
| 17 | # Partitions. | ||
| 18 | # | ||
| 19 | # Valid keys are KERNEL, FILESYS, SCRATCH, SWAP. Only those | ||
| 20 | # partitions which are in use are included. | ||
| 21 | # | ||
| 22 | # Each value is a reference to a hash. If the partition's contents | ||
| 23 | # are to be obtained from a file (that will be copied into a new | ||
| 24 | # virtual disk), then the hash contains: | ||
| 25 | # | ||
| 26 | # FILE => name of file from which the partition's contents are copied | ||
| 27 | # (perhaps "/dev/zero"), | ||
| 28 | # OFFSET => offset in bytes in FILE, | ||
| 29 | # BYTES => size in bytes of contents from FILE, | ||
| 30 | # | ||
| 31 | # If the partition is taken from a virtual disk directly, then it | ||
| 32 | # contains the following. The same keys are also filled in once a | ||
| 33 | # file-based partition has been copied into a new virtual disk: | ||
| 34 | # | ||
| 35 | # DISK => name of virtual disk file, | ||
| 36 | # START => sector offset of start of partition within DISK, | ||
| 37 | # SECTORS => number of sectors of partition within DISK, which is usually | ||
| 38 | # greater than round_up (BYTES, 512) due to padding. | ||
| 39 | our (%parts); | ||
| 40 | |||
| 41 | # set_part($opt, $arg) | ||
| 42 | # | ||
| 43 | # For use as a helper function for Getopt::Long::GetOptions to set | ||
| 44 | # disk sources. | ||
| 45 | sub set_part { | ||
| 46 | my ($opt, $arg) = @_; | ||
| 47 | my ($role, $source) = $opt =~ /^([a-z]+)(?:-([a-z]+))?/ or die; | ||
| 48 | |||
| 49 | $role = uc $role; | ||
| 50 | $source = 'FILE' if $source eq ''; | ||
| 51 | |||
| 52 | die "can't have two sources for \L$role\E partition" | ||
| 53 | if exists $parts{$role}; | ||
| 54 | |||
| 55 | do_set_part ($role, $source, $arg); | ||
| 56 | } | ||
| 57 | |||
| 58 | # do_set_part($role, $source, $arg) | ||
| 59 | # | ||
| 60 | # Sets partition $role as coming from $source (one of 'file', 'from', | ||
| 61 | # or 'size'). $arg is a file name for 'file' or 'from', a size in | ||
| 62 | # megabytes for 'size'. | ||
| 63 | sub do_set_part { | ||
| 64 | my ($role, $source, $arg) = @_; | ||
| 65 | |||
| 66 | my ($p) = $parts{$role} = {}; | ||
| 67 | if ($source eq 'file') { | ||
| 68 | if (read_mbr ($arg)) { | ||
| 69 | print STDERR "warning: $arg looks like a partitioned disk "; | ||
| 70 | print STDERR "(did you want --$role-from=$arg or --disk=$arg?)\n" | ||
| 71 | } | ||
| 72 | |||
| 73 | $p->{FILE} = $arg; | ||
| 74 | $p->{OFFSET} = 0; | ||
| 75 | $p->{BYTES} = -s $arg; | ||
| 76 | } elsif ($source eq 'from') { | ||
| 77 | my (%pt) = read_partition_table ($arg); | ||
| 78 | my ($sp) = $pt{$role}; | ||
| 79 | die "$arg: does not contain \L$role\E partition\n" if !defined $sp; | ||
| 80 | |||
| 81 | $p->{FILE} = $arg; | ||
| 82 | $p->{OFFSET} = $sp->{START} * 512; | ||
| 83 | $p->{BYTES} = $sp->{SECTORS} * 512; | ||
| 84 | } elsif ($source eq 'size') { | ||
| 85 | $arg =~ /^\d+(\.\d+)?|\.\d+$/ or die "$arg: not a valid size in MB\n"; | ||
| 86 | |||
| 87 | $p->{FILE} = "/dev/zero"; | ||
| 88 | $p->{OFFSET} = 0; | ||
| 89 | $p->{BYTES} = ceil ($arg * 1024 * 1024); | ||
| 90 | } else { | ||
| 91 | die; | ||
| 92 | } | ||
| 93 | } | ||
| 94 | |||
| 95 | # set_geometry('HEADS,SPT') | ||
| 96 | # set_geometry('zip') | ||
| 97 | # | ||
| 98 | # For use as a helper function for Getopt::Long::GetOptions to set | ||
| 99 | # disk geometry. | ||
| 100 | sub set_geometry { | ||
| 101 | local ($_) = $_[1]; | ||
| 102 | if ($_ eq 'zip') { | ||
| 103 | @geometry{'H', 'S'} = (64, 32); | ||
| 104 | } else { | ||
| 105 | @geometry{'H', 'S'} = /^(\d+)[,\s]+(\d+)$/ | ||
| 106 | or die "bad syntax for geometry\n"; | ||
| 107 | $geometry{H} <= 255 or die "heads limited to 255\n"; | ||
| 108 | $geometry{S} <= 63 or die "sectors per track limited to 63\n"; | ||
| 109 | } | ||
| 110 | } | ||
| 111 | |||
| 112 | # set_align('bochs|full|none') | ||
| 113 | # | ||
| 114 | # For use as a helper function for Getopt::Long::GetOptions to set | ||
| 115 | # partition alignment. | ||
| 116 | sub set_align { | ||
| 117 | $align = $_[1]; | ||
| 118 | die "unknown alignment type \"$align\"\n" | ||
| 119 | if $align ne 'bochs' && $align ne 'full' && $align ne 'none'; | ||
| 120 | } | ||
| 121 | |||
| 122 | # assemble_disk(%args) | ||
| 123 | # | ||
| 124 | # Creates a virtual disk $args{DISK} containing the partitions | ||
| 125 | # described by @args{KERNEL, FILESYS, SCRATCH, SWAP}. | ||
| 126 | # | ||
| 127 | # Required arguments: | ||
| 128 | # DISK => output disk file name | ||
| 129 | # HANDLE => output file handle (will be closed) | ||
| 130 | # | ||
| 131 | # Normally at least one of the following is included: | ||
| 132 | # KERNEL, FILESYS, SCRATCH, SWAP => {input: | ||
| 133 | # FILE => file to read, | ||
| 134 | # OFFSET => byte offset in file, | ||
| 135 | # BYTES => byte count from file, | ||
| 136 | # | ||
| 137 | # output: | ||
| 138 | # DISK => output disk file name, | ||
| 139 | # START => sector offset in DISK, | ||
| 140 | # SECTORS => sector count in DISK}, | ||
| 141 | # | ||
| 142 | # Optional arguments: | ||
| 143 | # ALIGN => 'bochs' (default), 'full', or 'none' | ||
| 144 | # GEOMETRY => {H => heads, S => sectors per track} (default 16, 63) | ||
| 145 | # FORMAT => 'partitioned' (default) or 'raw' | ||
| 146 | # LOADER => $LOADER_SIZE-byte string containing the loader binary | ||
| 147 | # ARGS => ['arg 1', 'arg 2', ...] | ||
| 148 | sub assemble_disk { | ||
| 149 | my (%args) = @_; | ||
| 150 | |||
| 151 | my (%geometry) = $args{GEOMETRY} || (H => 16, S => 63); | ||
| 152 | |||
| 153 | my ($align); # Align partition start, end to cylinder boundary? | ||
| 154 | my ($pad); # Pad end of disk out to cylinder boundary? | ||
| 155 | if (!defined ($args{ALIGN}) || $args{ALIGN} eq 'bochs') { | ||
| 156 | $align = 0; | ||
| 157 | $pad = 1; | ||
| 158 | } elsif ($args{ALIGN} eq 'full') { | ||
| 159 | $align = 1; | ||
| 160 | $pad = 0; | ||
| 161 | } elsif ($args{ALIGN} eq 'none') { | ||
| 162 | $align = $pad = 0; | ||
| 163 | } else { | ||
| 164 | die; | ||
| 165 | } | ||
| 166 | |||
| 167 | my ($format) = $args{FORMAT} || 'partitioned'; | ||
| 168 | die if $format ne 'partitioned' && $format ne 'raw'; | ||
| 169 | |||
| 170 | # Check that we have apartitions to copy in. | ||
| 171 | my $part_cnt = grep (defined ($args{$_}), keys %role2type); | ||
| 172 | die "must have exactly one partition for raw output\n" | ||
| 173 | if $format eq 'raw' && $part_cnt != 1; | ||
| 174 | |||
| 175 | # Calculate the disk size. | ||
| 176 | my ($total_sectors) = 0; | ||
| 177 | if ($format eq 'partitioned') { | ||
| 178 | $total_sectors += $align ? $geometry{S} : 1; | ||
| 179 | } | ||
| 180 | for my $role (@role_order) { | ||
| 181 | my ($p) = $args{$role}; | ||
| 182 | next if !defined $p; | ||
| 183 | |||
| 184 | die if $p->{DISK}; | ||
| 185 | |||
| 186 | my ($bytes) = $p->{BYTES}; | ||
| 187 | my ($start) = $total_sectors; | ||
| 188 | my ($end) = $start + div_round_up ($bytes, 512); | ||
| 189 | $end = round_up ($end, cyl_sectors (%geometry)) if $align; | ||
| 190 | |||
| 191 | $p->{DISK} = $args{DISK}; | ||
| 192 | $p->{START} = $start; | ||
| 193 | $p->{SECTORS} = $end - $start; | ||
| 194 | $total_sectors = $end; | ||
| 195 | } | ||
| 196 | |||
| 197 | # Write the disk. | ||
| 198 | my ($disk_fn) = $args{DISK}; | ||
| 199 | my ($disk) = $args{HANDLE}; | ||
| 200 | if ($format eq 'partitioned') { | ||
| 201 | # Pack loader into MBR. | ||
| 202 | my ($loader) = $args{LOADER} || "\xcd\x18"; | ||
| 203 | my ($mbr) = pack ("a$LOADER_SIZE", $loader); | ||
| 204 | |||
| 205 | $mbr .= make_kernel_command_line (@{$args{ARGS}}); | ||
| 206 | |||
| 207 | # Pack partition table into MBR. | ||
| 208 | $mbr .= make_partition_table (\%geometry, \%args); | ||
| 209 | |||
| 210 | # Add signature to MBR. | ||
| 211 | $mbr .= pack ("v", 0xaa55); | ||
| 212 | |||
| 213 | die if length ($mbr) != 512; | ||
| 214 | write_fully ($disk, $disk_fn, $mbr); | ||
| 215 | write_zeros ($disk, $disk_fn, 512 * ($geometry{S} - 1)) if $align; | ||
| 216 | } | ||
| 217 | for my $role (@role_order) { | ||
| 218 | my ($p) = $args{$role}; | ||
| 219 | next if !defined $p; | ||
| 220 | |||
| 221 | my ($source); | ||
| 222 | my ($fn) = $p->{FILE}; | ||
| 223 | open ($source, '<', $fn) or die "$fn: open: $!\n"; | ||
| 224 | if ($p->{OFFSET}) { | ||
| 225 | sysseek ($source, $p->{OFFSET}, 0) == $p->{OFFSET} | ||
| 226 | or die "$fn: seek: $!\n"; | ||
| 227 | } | ||
| 228 | copy_file ($source, $fn, $disk, $disk_fn, $p->{BYTES}); | ||
| 229 | close ($source) or die "$fn: close: $!\n"; | ||
| 230 | |||
| 231 | write_zeros ($disk, $disk_fn, $p->{SECTORS} * 512 - $p->{BYTES}); | ||
| 232 | } | ||
| 233 | if ($pad) { | ||
| 234 | my ($pad_sectors) = round_up ($total_sectors, cyl_sectors (%geometry)); | ||
| 235 | write_zeros ($disk, $disk_fn, ($pad_sectors - $total_sectors) * 512); | ||
| 236 | } | ||
| 237 | close ($disk) or die "$disk: close: $!\n"; | ||
| 238 | } | ||
| 239 | |||
| 240 | # make_partition_table({H => heads, S => sectors}, {KERNEL => ..., ...}) | ||
| 241 | # | ||
| 242 | # Creates and returns a partition table for the given partitions and | ||
| 243 | # disk geometry. | ||
| 244 | sub make_partition_table { | ||
| 245 | my ($geometry, $partitions) = @_; | ||
| 246 | my ($table) = ''; | ||
| 247 | for my $role (@role_order) { | ||
| 248 | defined (my $p = $partitions->{$role}) or next; | ||
| 249 | |||
| 250 | my $end = $p->{START} + $p->{SECTORS} - 1; | ||
| 251 | my $bootable = $role eq 'KERNEL'; | ||
| 252 | |||
| 253 | $table .= pack ("C", $bootable ? 0x80 : 0); # Bootable? | ||
| 254 | $table .= pack_chs ($p->{START}, $geometry); # CHS of partition start | ||
| 255 | $table .= pack ("C", $role2type{$role}); # Partition type | ||
| 256 | $table .= pack_chs($end, $geometry); # CHS of partition end | ||
| 257 | $table .= pack ("V", $p->{START}); # LBA of partition start | ||
| 258 | $table .= pack ("V", $p->{SECTORS}); # Length in sectors | ||
| 259 | die if length ($table) % 16; | ||
| 260 | } | ||
| 261 | return pack ("a64", $table); | ||
| 262 | } | ||
| 263 | |||
| 264 | # make_kernel_command_line(@args) | ||
| 265 | # | ||
| 266 | # Returns the raw bytes to write to an MBR at offset $LOADER_SIZE to | ||
| 267 | # set a Pintos kernel command line. | ||
| 268 | sub make_kernel_command_line { | ||
| 269 | my (@args) = @_; | ||
| 270 | my ($args) = join ('', map ("$_\0", @args)); | ||
| 271 | die "command line exceeds 128 bytes" if length ($args) > 128; | ||
| 272 | return pack ("V a128", scalar (@args), $args); | ||
| 273 | } | ||
| 274 | |||
| 275 | # copy_file($from_handle, $from_file_name, $to_handle, $to_file_name, $size) | ||
| 276 | # | ||
| 277 | # Copies $size bytes from $from_handle to $to_handle. | ||
| 278 | # $from_file_name and $to_file_name are used in error messages. | ||
| 279 | sub copy_file { | ||
| 280 | my ($from_handle, $from_file_name, $to_handle, $to_file_name, $size) = @_; | ||
| 281 | |||
| 282 | while ($size > 0) { | ||
| 283 | my ($chunk_size) = 4096; | ||
| 284 | $chunk_size = $size if $chunk_size > $size; | ||
| 285 | $size -= $chunk_size; | ||
| 286 | |||
| 287 | my ($data) = read_fully ($from_handle, $from_file_name, $chunk_size); | ||
| 288 | write_fully ($to_handle, $to_file_name, $data); | ||
| 289 | } | ||
| 290 | } | ||
| 291 | |||
| 292 | # read_fully($handle, $file_name, $bytes) | ||
| 293 | # | ||
| 294 | # Reads exactly $bytes bytes from $handle and returns the data read. | ||
| 295 | # $file_name is used in error messages. | ||
| 296 | sub read_fully { | ||
| 297 | my ($handle, $file_name, $bytes) = @_; | ||
| 298 | my ($data); | ||
| 299 | my ($read_bytes) = sysread ($handle, $data, $bytes); | ||
| 300 | die "$file_name: read: $!\n" if !defined $read_bytes; | ||
| 301 | die "$file_name: unexpected end of file\n" if $read_bytes != $bytes; | ||
| 302 | return $data; | ||
| 303 | } | ||
| 304 | |||
| 305 | # write_fully($handle, $file_name, $data) | ||
| 306 | # | ||
| 307 | # Write $data to $handle. | ||
| 308 | # $file_name is used in error messages. | ||
| 309 | sub write_fully { | ||
| 310 | my ($handle, $file_name, $data) = @_; | ||
| 311 | my ($written_bytes) = syswrite ($handle, $data); | ||
| 312 | die "$file_name: write: $!\n" if !defined $written_bytes; | ||
| 313 | die "$file_name: short write\n" if $written_bytes != length $data; | ||
| 314 | } | ||
| 315 | |||
| 316 | sub write_zeros { | ||
| 317 | my ($handle, $file_name, $size) = @_; | ||
| 318 | |||
| 319 | while ($size > 0) { | ||
| 320 | my ($chunk_size) = 4096; | ||
| 321 | $chunk_size = $size if $chunk_size > $size; | ||
| 322 | $size -= $chunk_size; | ||
| 323 | |||
| 324 | write_fully ($handle, $file_name, "\0" x $chunk_size); | ||
| 325 | } | ||
| 326 | } | ||
| 327 | |||
| 328 | # div_round_up($x,$y) | ||
| 329 | # | ||
| 330 | # Returns $x / $y, rounded up to the nearest integer. | ||
| 331 | # $y must be an integer. | ||
| 332 | sub div_round_up { | ||
| 333 | my ($x, $y) = @_; | ||
| 334 | return int ((ceil ($x) + $y - 1) / $y); | ||
| 335 | } | ||
| 336 | |||
| 337 | # round_up($x, $y) | ||
| 338 | # | ||
| 339 | # Returns $x rounded up to the nearest multiple of $y. | ||
| 340 | # $y must be an integer. | ||
| 341 | sub round_up { | ||
| 342 | my ($x, $y) = @_; | ||
| 343 | return div_round_up ($x, $y) * $y; | ||
| 344 | } | ||
| 345 | |||
| 346 | # cyl_sectors(H => heads, S => sectors) | ||
| 347 | # | ||
| 348 | # Returns the number of sectors in a cylinder of a disk with the given | ||
| 349 | # geometry. | ||
| 350 | sub cyl_sectors { | ||
| 351 | my (%geometry) = @_; | ||
| 352 | return $geometry{H} * $geometry{S}; | ||
| 353 | } | ||
| 354 | |||
| 355 | # read_loader($file_name) | ||
| 356 | # | ||
| 357 | # Reads and returns the first $LOADER_SIZE bytes in $file_name. | ||
| 358 | # If $file_name is undefined, tries to find the default loader. | ||
| 359 | # Makes sure that the loader is a reasonable size. | ||
| 360 | sub read_loader { | ||
| 361 | my ($name) = @_; | ||
| 362 | $name = find_file ("loader.bin") if !defined $name; | ||
| 363 | die "Cannot find loader\n" if !defined $name; | ||
| 364 | |||
| 365 | my ($handle); | ||
| 366 | open ($handle, '<', $name) or die "$name: open: $!\n"; | ||
| 367 | -s $handle == $LOADER_SIZE || -s $handle == 512 | ||
| 368 | or die "$name: must be exactly $LOADER_SIZE or 512 bytes long\n"; | ||
| 369 | $loader = read_fully ($handle, $name, $LOADER_SIZE); | ||
| 370 | close ($handle) or die "$name: close: $!\n"; | ||
| 371 | return $loader; | ||
| 372 | } | ||
| 373 | |||
| 374 | # pack_chs($lba, {H => heads, S => sectors}) | ||
| 375 | # | ||
| 376 | # Converts logical sector $lba to a 3-byte packed geometrical sector | ||
| 377 | # in the format used in PC partition tables (see [Partitions]) and | ||
| 378 | # returns the geometrical sector as a 3-byte string. | ||
| 379 | sub pack_chs { | ||
| 380 | my ($lba, $geometry) = @_; | ||
| 381 | my ($cyl, $head, $sect) = lba_to_chs ($lba, $geometry); | ||
| 382 | return pack ("CCC", $head, $sect | (($cyl >> 2) & 0xc0), $cyl & 0xff); | ||
| 383 | } | ||
| 384 | |||
| 385 | # lba_to_chs($lba, {H => heads, S => sectors}) | ||
| 386 | # | ||
| 387 | # Returns the geometrical sector corresponding to logical sector $lba | ||
| 388 | # given the specified geometry. | ||
| 389 | sub lba_to_chs { | ||
| 390 | my ($lba, $geometry) = @_; | ||
| 391 | my ($hpc) = $geometry->{H}; | ||
| 392 | my ($spt) = $geometry->{S}; | ||
| 393 | |||
| 394 | # Source: | ||
| 395 | # http://en.wikipedia.org/wiki/CHS_conversion | ||
| 396 | use integer; | ||
| 397 | my $cyl = $lba / ($hpc * $spt); | ||
| 398 | my $temp = $lba % ($hpc * $spt); | ||
| 399 | my $head = $temp / $spt; | ||
| 400 | my $sect = $temp % $spt + 1; | ||
| 401 | |||
| 402 | # Source: | ||
| 403 | # http://www.cgsecurity.org/wiki/Intel_Partition_Table | ||
| 404 | if ($cyl <= 1023) { | ||
| 405 | return ($cyl, $head, $sect); | ||
| 406 | } else { | ||
| 407 | return (1023, 254, 63); ## or should this be (1023, $hpc, $spt)? | ||
| 408 | } | ||
| 409 | } | ||
| 410 | |||
| 411 | # read_mbr($file) | ||
| 412 | # | ||
| 413 | # Tries to read an MBR from $file. Returns the 512-byte MBR if | ||
| 414 | # successful, otherwise numeric 0. | ||
| 415 | sub read_mbr { | ||
| 416 | my ($file) = @_; | ||
| 417 | my ($retval) = 0; | ||
| 418 | open (FILE, '<', $file) or die "$file: open: $!\n"; | ||
| 419 | if (-s FILE == 0) { | ||
| 420 | die "$file: file has zero size\n"; | ||
| 421 | } elsif (-s FILE >= 512) { | ||
| 422 | my ($mbr); | ||
| 423 | sysread (FILE, $mbr, 512) == 512 or die "$file: read: $!\n"; | ||
| 424 | $retval = $mbr if unpack ("v", substr ($mbr, 510)) == 0xaa55; | ||
| 425 | } | ||
| 426 | close (FILE); | ||
| 427 | return $retval; | ||
| 428 | } | ||
| 429 | |||
| 430 | # interpret_partition_table($mbr, $disk) | ||
| 431 | # | ||
| 432 | # Parses the partition-table in the specified 512-byte $mbr and | ||
| 433 | # returns the partitions. $disk is used for error messages. | ||
| 434 | sub interpret_partition_table { | ||
| 435 | my ($mbr, $disk) = @_; | ||
| 436 | my (%parts); | ||
| 437 | for my $i (0...3) { | ||
| 438 | my ($bootable, $valid, $type, $lba_start, $lba_length) | ||
| 439 | = unpack ("C X V C x3 V V", substr ($mbr, 446 + 16 * $i, 16)); | ||
| 440 | next if !$valid; | ||
| 441 | |||
| 442 | (print STDERR "warning: invalid partition entry $i in $disk\n"), | ||
| 443 | next if $bootable != 0 && $bootable != 0x80; | ||
| 444 | |||
| 445 | my ($role) = $type2role{$type}; | ||
| 446 | (printf STDERR "warning: non-Pintos partition type 0x%02x in %s\n", | ||
| 447 | $type, $disk), | ||
| 448 | next if !defined $role; | ||
| 449 | |||
| 450 | (print STDERR "warning: duplicate \L$role\E partition in $disk\n"), | ||
| 451 | next if exists $parts{$role}; | ||
| 452 | |||
| 453 | $parts{$role} = {START => $lba_start, | ||
| 454 | SECTORS => $lba_length}; | ||
| 455 | } | ||
| 456 | return %parts; | ||
| 457 | } | ||
| 458 | |||
| 459 | # find_file($base_name) | ||
| 460 | # | ||
| 461 | # Looks for a file named $base_name in a couple of likely spots. If | ||
| 462 | # found, returns the name; otherwise, returns undef. | ||
| 463 | sub find_file { | ||
| 464 | my ($base_name) = @_; | ||
| 465 | -e && return $_ foreach $base_name, "build/$base_name"; | ||
| 466 | return undef; | ||
| 467 | } | ||
| 468 | |||
| 469 | # read_partition_table($file) | ||
| 470 | # | ||
| 471 | # Reads a partition table from $file and returns the parsed | ||
| 472 | # partitions. Dies if partitions can't be read. | ||
| 473 | sub read_partition_table { | ||
| 474 | my ($file) = @_; | ||
| 475 | my ($mbr) = read_mbr ($file); | ||
| 476 | die "$file: not a partitioned disk\n" if !$mbr; | ||
| 477 | return interpret_partition_table ($mbr, $file); | ||
| 478 | } | ||
| 479 | |||
| 480 | # max(@args) | ||
| 481 | # | ||
| 482 | # Returns the numerically largest value in @args. | ||
| 483 | sub max { | ||
| 484 | my ($max) = $_[0]; | ||
| 485 | foreach (@_[1..$#_]) { | ||
| 486 | $max = $_ if $_ > $max; | ||
| 487 | } | ||
| 488 | return $max; | ||
| 489 | } | ||
| 490 | |||
| 491 | 1; | ||
