mpc85xx: drop 3.14 support
[openwrt/openwrt.git] / scripts / slugimage.pl
index 541325dd694e79b5fd49aa1938751e58eeea11e9..deac6ac6dd33f66e2d9bb3520363136a15cd8c33 100755 (executable)
@@ -1,4 +1,4 @@
-#!/usr/bin/perl
+#!/usr/bin/env perl
 # 
 # SlugImage : Manipulate NSLU2 firmware images
 #             Dwayne Fontenot (jacques)
@@ -391,10 +391,9 @@ sub readInFirmware {
     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);
@@ -410,7 +409,7 @@ sub readInFirmware {
            }
            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.
@@ -422,6 +421,11 @@ sub readInFirmware {
                        $_->{'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.
@@ -557,6 +561,11 @@ sub readInFirmwareParts {
 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;
@@ -588,66 +597,33 @@ sub layoutPartitions {
 
        $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.
@@ -657,36 +633,64 @@ sub layoutPartitions {
                # 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;
 
@@ -916,7 +920,7 @@ sub defaultPartitions {
             '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,
@@ -925,7 +929,7 @@ sub defaultPartitions {
 
 # 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);
 
@@ -941,6 +945,7 @@ if (!GetOptions("d|debug"       => \$debug,
                "u|unpack"      => \$unpack,
                "p|pack"        => \$pack,
                "l|little"      => \$little,
+               "F|fatflash"    => \$fatflash,
                "i|input=s"     => \$input,
                "o|output=s"    => \$output,
                "b|redboot=s"   => \$redboot,
@@ -960,6 +965,7 @@ if (!GetOptions("d|debug"       => \$debug,
     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";
@@ -1042,6 +1048,18 @@ if (defined $little)  {
     } @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') {
@@ -1110,10 +1128,7 @@ if (defined $ramdisk) {
            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;