-#!/usr/bin/perl
+#!/usr/bin/env perl
#
# SlugImage : Manipulate NSLU2 firmware images
# Dwayne Fontenot (jacques)
my($filename, $partitions_ref) = @_;
my($firmware_buf);
- my($total_length) = 0x800000;
open FILE,$filename or die "Can't find firmware image \"$filename\": $!\n";
- read FILE,$firmware_buf,$total_length or die "Can't read $total_length bytes from \"$filename\": $!\n";
+ read FILE,$firmware_buf,$flash_len or die "Can't read $flash_len bytes from \"$filename\": $!\n";
close FILE or die "Can't close \"$filename\": $!\n";
$debug and printf("Read 0x%08X bytes from \"%s\"\n", length($firmware_buf), $filename);
}
else {
- # Slurp up the data, based on whether a header is present or not
+ # Slurp up the data, based on whether a header and/or data is present or not
if ($_->{'header'}) {
# Read the length, and grab the data based on the length.
$_->{'data'} = substr($firmware_buf, $_->{'offset'} + $_->{'header'}, $data_len);
}
}
+ elsif ($_->{'pseudo'} and not defined $_->{'file'} and
+ (substr($firmware_buf, $_->{'offset'}, $_->{'size'}) eq
+ (pack("C", 0xff) x $_->{'size'}))) {
+ $debug and printf("Skipping empty pseudo partition <%s>\n", $_->{'name'});
+ }
else {
# Grab the whole partition, using the maximum size.
sub layoutPartitions {
my(@partitions) = @_;
+ # Find the kernel partition, and save a pointer to it for later use
+ my $kernel;
+ map { ($_->{'name'} eq "Kernel") && ($kernel = $_); } @partitions;
+ $kernel or die "Couldn't find the kernel partition\n";
+
# Find the last variable size partition, and save a pointer to it for later use
my $lastdisk;
my $directory_offset;
$debug and printf("Pointer is 0x%08X\n", $pointer);
- # If this is the last variable size partition, then fill the rest of the space.
- if ($_->{'name'} eq $lastdisk->{'name'}) {
- $_->{'size'} = paddedSize($directory_offset + $flash_start - $pointer);
- $debug and printf("Padding last variable partition <%s> to 0x%08X bytes\n", $_->{'name'}, $_->{'size'});
- }
-
- # Handle requests for partition creation first.
- if (defined $_->{'size'} and not defined $_->{'data'} and ($_->{'name'} ne "FIS directory")) {
-
- # A zero size is a request to fill all available space.
- if ($_->{'size'} == 0) {
- # Grab the start of the FIS directory, and use all the space up to there.
- $_->{'size'} = paddedSize($directory_offset + $flash_start - $pointer);
-
- # Create an empty partition of the requested size.
- $_->{'data'} = padBytes("", $_->{'size'});
-
- $debug and printf("Creating empty partition <%s> of 0x%08X bytes\n", $_->{'name'}, $_->{'size'});
- }
-
- if (not defined $_->{'offset'}) {
- # Check to make sure that the requested size is not too large.
- if (($pointer + $_->{'size'}) > ($flash_start + $directory_offset)) {
- die "Ran out of flash space in <", $_->{'name'}, ">\n";
- }
- }
- else {
- # Check to make sure that the requested size is not too large.
- if (($_->{'offset'} + $_->{'size'}) > ($flash_start + $directory_offset)) {
- die "Ran out of flash space in <", $_->{'name'}, ">\n";
- }
+ # Determine the start and offset of the current partition.
+ if (defined $_->{'offset'}) {
+ $_->{'start'} = $flash_start + $_->{'offset'};
+ # Check for running past the defined start of the partition.
+ if (($pointer > $_->{'start'}) and not $_->{'pseudo'}) {
+ die sprintf("Ran out of flash space before <%s> - %s too large.\n", $_->{'name'},
+ sprintf("0x%05X bytes", ($pointer - $_->{'start'})));
}
}
- # Then handle known partitions, and allocate them.
- if (defined $_->{'size'}) {
-
- # Determine the start and offset of the current partition.
- if (defined $_->{'offset'}) {
- $_->{'start'} = $flash_start + $_->{'offset'};
- }
-
- # If offset is not defined, then calculate it.
- else {
- $_->{'start'} = $pointer;
- $_->{'offset'} = $_->{'start'} - $flash_start;
- }
+ # If offset is not defined, then calculate it.
+ else {
+ $_->{'start'} = $pointer;
+ $_->{'offset'} = $_->{'start'} - $flash_start;
+ }
- my $size = defined $_->{'data'} ? length($_->{'data'}) : 0;
+ my $size = defined $_->{'data'} ? length($_->{'data'}) : 0;
- # Add skip regions for the partitions with headers.
- if ($_->{'header'} > 0) {
- # Define the skip region for the initial Sercomm header.
- push(@{$_->{'skips'}},
- { 'offset' => 0, 'size' => $_->{'header'}, 'data' => undef });
- # Allow for the Sercomm header to be prepended to the data.
- $size += $_->{'header'};
- }
+ # Add skip regions for the partitions with headers.
+ if ($_->{'header'} > 0) {
+ # Define the skip region for the initial Sercomm header.
+ push(@{$_->{'skips'}},
+ { 'offset' => 0, 'size' => $_->{'header'}, 'data' => undef });
+ # Allow for the Sercomm header to be prepended to the data.
+ $size += $_->{'header'};
- # Determine if the partition requires a Sercomm skip region.
+ # Determine if the partition overlaps the ramdisk boundary.
if (($_->{'offset'} < $ramdisk_offset) and
(($_->{'offset'} + $size) > $ramdisk_offset)) {
# Define the skip region for the inline Sercomm header.
# Allow for the Sercomm header to be inserted in the data.
$size += 16;
}
+ }
- # Extend to another block if required.
- if ($size > $_->{'size'}) {
- $_->{'size'} = $size;
- printf("Extending partition <%s> to 0x%08X bytes\n", $_->{'name'}, $_->{'size'});
- }
+ # Partitions without headers cannot have skip regions.
+ elsif (($_->{'offset'} <= $ramdisk_offset) and
+ (($_->{'offset'} + $size) > $ramdisk_offset)) {
+ # Pad the kernel until it extends past the ramdisk offset.
+ push(@{$kernel->{'skips'}},
+ { 'offset' => ($ramdisk_offset - $kernel->{'offset'}), 'size' => 16,
+ 'data' => pack("N4", $block_size) });
+ $kernel->{'size'} = $ramdisk_offset - $kernel->{'offset'} + $block_size;
+ $kernel->{'data'} = padBytes($kernel->{'data'},
+ $kernel->{'size'} - $kernel->{'header'} - 16);
+ $_->{'offset'} = $ramdisk_offset + $block_size;
+ $_->{'start'} = $flash_start + $_->{'offset'};
+ $pointer = $_->{'start'};
+ $debug and printf("Extending kernel partition past ramdisk offset.\n");
+ }
- # Keep the user appraised ...
- $debug and printf("Allocated <%s> from 0x%08X to 0x%08X (%s / %s)\n",
- $_->{'name'}, $_->{'start'}, $_->{'start'} + $_->{'size'},
- ($size >= $block_size ?
- sprintf("%d blocks", numBlocks($size)) :
- sprintf("0x%05X bytes", $size)),
- ($_->{'size'} >= $block_size ?
- sprintf("%d blocks", numBlocks($_->{'size'})) :
- sprintf("0x%05X bytes", $_->{'size'})));
+ # If this is the last variable size partition, then fill the rest of the space.
+ if ($_->{'name'} eq $lastdisk->{'name'}) {
+ $_->{'size'} = paddedSize($directory_offset + $flash_start - $pointer);
+ $debug and printf("Padding last variable partition <%s> to 0x%08X bytes\n", $_->{'name'}, $_->{'size'});
+ }
+
+ die sprintf("Partition size not defined in <%s>.\n", $_->{'name'})
+ unless defined $_->{'size'};
- # Check to make sure we have not run out of room.
- if (($_->{'start'} + $_->{'size'}) > ($flash_start + $flash_len)) {
- die "Ran out of flash space in <", $_->{'name'}, ">\n";
+ # Extend to another block if required.
+ if ($size > $_->{'size'}) {
+ if ($_->{'name'} eq $lastdisk->{'name'}) {
+ die sprintf("Ran out of flash space in <%s> - %s too large.\n", $_->{'name'},
+ sprintf("0x%05X bytes", ($size - $_->{'size'})));
}
+ $_->{'size'} = $size;
+ printf("Extending partition <%s> to 0x%08X bytes\n", $_->{'name'}, $_->{'size'});
+ }
- $debug and printf("Moving pointer from 0x%08X to 0x%08X (0x%08X + 0x%08X)\n",
- $pointer, paddedSize($_->{'start'} + $_->{'size'}),
- $_->{'start'}, $_->{'size'});
+ # Keep the user appraised ...
+ $debug and printf("Allocated <%s> from 0x%08X to 0x%08X (%s / %s)\n",
+ $_->{'name'}, $_->{'start'}, $_->{'start'} + $_->{'size'},
+ ($size >= $block_size ?
+ sprintf("%d blocks", numBlocks($size)) :
+ sprintf("0x%05X bytes", $size)),
+ ($_->{'size'} >= $block_size ?
+ sprintf("%d blocks", numBlocks($_->{'size'})) :
+ sprintf("0x%05X bytes", $_->{'size'})));
+
+ # Check to make sure we have not run out of room.
+ if (($_->{'start'} + $_->{'size'}) > ($flash_start + $flash_len)) {
+ die "Ran out of flash space in <", $_->{'name'}, ">\n";
+ }
- # Move the pointer up, in preparation for the next partition.
- $pointer = paddedSize($_->{'start'} + $_->{'size'});
+ $debug and printf("Moving pointer from 0x%08X to 0x%08X (0x%08X + 0x%08X)\n",
+ $pointer, paddedSize($_->{'start'} + $_->{'size'}),
+ $_->{'start'}, $_->{'size'});
- }
+ # Move the pointer up, in preparation for the next partition.
+ $pointer = paddedSize($_->{'start'} + $_->{'size'});
} @partitions;
'offset'=>0x007f8000, 'size'=>0x00004000,
'variable'=>0, 'header'=>0, 'pseudo'=>1, 'data'=>undef, 'byteswap'=>0},
{'name'=>'Microcode', 'file'=>'NPE-B',
- 'offset'=>0x007fc000, 'size'=>0x00003ff0,
+ 'offset'=>0x007fc000, 'size'=>0x00003fe0,
'variable'=>0, 'header'=>16, 'pseudo'=>1, 'data'=>undef, 'byteswap'=>0},
{'name'=>'Trailer', 'file'=>'Trailer',
'offset'=>0x007ffff0, 'size'=>0x00000010,
# Main routine starts here ...
-my($unpack, $pack, $little, $input, $output, $redboot);
+my($unpack, $pack, $little, $fatflash, $input, $output, $redboot);
my($kernel, $sysconf, $ramdisk, $fisdir);
my($microcode, $trailer, $ethaddr, $loader);
"u|unpack" => \$unpack,
"p|pack" => \$pack,
"l|little" => \$little,
+ "F|fatflash" => \$fatflash,
"i|input=s" => \$input,
"o|output=s" => \$output,
"b|redboot=s" => \$redboot,
print " [-u|--unpack] Unpack a firmware image\n";
print " [-p|--pack] Pack a firmware image\n";
print " [-l|--little] Convert Kernel and Ramdisk to little-endian\n";
+ print " [-F|--fatflash] Generate an image for 16MB flash\n";
print " [-i|--input] <file> Input firmware image filename\n";
print " [-o|--output] <file> Output firmware image filename\n";
print " [-b|--redboot] <file> Input/Output RedBoot filename\n";
} @partitions;
}
+if (defined $fatflash) {
+ $flash_len = 0x01000000;
+ map {
+ if (($_->{'name'} eq 'FIS directory') or
+ ($_->{'name'} eq 'Loader config') or
+ ($_->{'name'} eq 'Microcode') or
+ ($_->{'name'} eq 'Trailer')) {
+ $_->{'offset'} += 0x00800000;
+ }
+ } @partitions;
+}
+
if (defined $ethaddr) {
map {
if ($_->{'name'} eq 'EthAddr') {
if (defined $size) {
$entry{'size'} = $size * $block_size;
# Create an empty partition of the requested size.
- $entry{'data'} = padBytes("", $entry{'size'});
- if ($entry{'header'}) {
- $entry{'data'} = padBytes("", $entry{'size'} - $entry{'header'});
- }
+ $entry{'data'} = padBytes("", $entry{'size'} - $entry{'header'});
}
\%entry;