| 1 | #!/usr/bin/perl |
|---|
| 2 | ###################################################################### |
|---|
| 3 | # |
|---|
| 4 | # File : split_bootimg.pl |
|---|
| 5 | # Author(s) : William Enck <enck@cse.psu.edu> |
|---|
| 6 | # Description : Split appart an Android boot image created |
|---|
| 7 | # with mkbootimg. The format can be found in |
|---|
| 8 | # android-src/system/core/mkbootimg/bootimg.h |
|---|
| 9 | # |
|---|
| 10 | # Thanks to alansj on xda-developers.com for |
|---|
| 11 | # identifying the format in bootimg.h and |
|---|
| 12 | # describing initial instructions for splitting |
|---|
| 13 | # the boot.img file. |
|---|
| 14 | # |
|---|
| 15 | # Last Modified : Tue Dec 2 23:36:25 EST 2008 |
|---|
| 16 | # By : William Enck <enck@cse.psu.edu> |
|---|
| 17 | # |
|---|
| 18 | # Copyright (c) 2008 William Enck |
|---|
| 19 | # |
|---|
| 20 | ###################################################################### |
|---|
| 21 | |
|---|
| 22 | use strict; |
|---|
| 23 | use warnings; |
|---|
| 24 | |
|---|
| 25 | # Turn on print flushing |
|---|
| 26 | $|++; |
|---|
| 27 | |
|---|
| 28 | ###################################################################### |
|---|
| 29 | ## Global Variables and Constants |
|---|
| 30 | |
|---|
| 31 | my $SCRIPT = __FILE__; |
|---|
| 32 | my $IMAGE_FN = undef; |
|---|
| 33 | |
|---|
| 34 | # Constants (from bootimg.h) |
|---|
| 35 | use constant BOOT_MAGIC => 'ANDROID!'; |
|---|
| 36 | use constant BOOT_MAGIC_SIZE => 8; |
|---|
| 37 | use constant BOOT_NAME_SIZE => 16; |
|---|
| 38 | use constant BOOT_ARGS_SIZE => 512; |
|---|
| 39 | |
|---|
| 40 | # Unsigned integers are 4 bytes |
|---|
| 41 | use constant UNSIGNED_SIZE => 4; |
|---|
| 42 | |
|---|
| 43 | # Parsed Values |
|---|
| 44 | my $PAGE_SIZE = undef; |
|---|
| 45 | my $KERNEL_SIZE = undef; |
|---|
| 46 | my $RAMDISK_SIZE = undef; |
|---|
| 47 | my $SECOND_SIZE = undef; |
|---|
| 48 | |
|---|
| 49 | ###################################################################### |
|---|
| 50 | ## Main Code |
|---|
| 51 | |
|---|
| 52 | &parse_cmdline(); |
|---|
| 53 | &parse_header($IMAGE_FN); |
|---|
| 54 | |
|---|
| 55 | =format (from bootimg.h) |
|---|
| 56 | ** +-----------------+ |
|---|
| 57 | ** | boot header | 1 page |
|---|
| 58 | ** +-----------------+ |
|---|
| 59 | ** | kernel | n pages |
|---|
| 60 | ** +-----------------+ |
|---|
| 61 | ** | ramdisk | m pages |
|---|
| 62 | ** +-----------------+ |
|---|
| 63 | ** | second stage | o pages |
|---|
| 64 | ** +-----------------+ |
|---|
| 65 | ** |
|---|
| 66 | ** n = (kernel_size + page_size - 1) / page_size |
|---|
| 67 | ** m = (ramdisk_size + page_size - 1) / page_size |
|---|
| 68 | ** o = (second_size + page_size - 1) / page_size |
|---|
| 69 | =cut |
|---|
| 70 | |
|---|
| 71 | my $n = int(($KERNEL_SIZE + $PAGE_SIZE - 1) / $PAGE_SIZE); |
|---|
| 72 | my $m = int(($RAMDISK_SIZE + $PAGE_SIZE - 1) / $PAGE_SIZE); |
|---|
| 73 | my $o = int(($SECOND_SIZE + $PAGE_SIZE - 1) / $PAGE_SIZE); |
|---|
| 74 | |
|---|
| 75 | my $k_offset = $PAGE_SIZE; |
|---|
| 76 | my $r_offset = $k_offset + ($n * $PAGE_SIZE); |
|---|
| 77 | my $s_offset = $r_offset + ($m * $PAGE_SIZE); |
|---|
| 78 | |
|---|
| 79 | (my $base = $IMAGE_FN) =~ s/.*\/(.*)$/$1/; |
|---|
| 80 | my $k_file = $base . "-kernel"; |
|---|
| 81 | my $r_file = $base . "-ramdisk.gz"; |
|---|
| 82 | my $s_file = $base . "-second.gz"; |
|---|
| 83 | |
|---|
| 84 | # The kernel is always there |
|---|
| 85 | print "Writing $k_file ..."; |
|---|
| 86 | &dump_file($IMAGE_FN, $k_file, $k_offset, $KERNEL_SIZE); |
|---|
| 87 | print " complete.\n"; |
|---|
| 88 | |
|---|
| 89 | # The ramdisk is always there |
|---|
| 90 | print "Writing $r_file ..."; |
|---|
| 91 | &dump_file($IMAGE_FN, $r_file, $r_offset, $RAMDISK_SIZE); |
|---|
| 92 | print " complete.\n"; |
|---|
| 93 | |
|---|
| 94 | # The Second stage bootloader is optional |
|---|
| 95 | unless ($SECOND_SIZE == 0) { |
|---|
| 96 | print "Writing $s_file ..."; |
|---|
| 97 | &dump_file($IMAGE_FN, $s_file, $s_offset, $SECOND_SIZE); |
|---|
| 98 | print " complete.\n"; |
|---|
| 99 | } |
|---|
| 100 | |
|---|
| 101 | ###################################################################### |
|---|
| 102 | ## Supporting Subroutines |
|---|
| 103 | |
|---|
| 104 | =header_format (from bootimg.h) |
|---|
| 105 | struct boot_img_hdr |
|---|
| 106 | { |
|---|
| 107 | unsigned char magic[BOOT_MAGIC_SIZE]; |
|---|
| 108 | |
|---|
| 109 | unsigned kernel_size; /* size in bytes */ |
|---|
| 110 | unsigned kernel_addr; /* physical load addr */ |
|---|
| 111 | |
|---|
| 112 | unsigned ramdisk_size; /* size in bytes */ |
|---|
| 113 | unsigned ramdisk_addr; /* physical load addr */ |
|---|
| 114 | |
|---|
| 115 | unsigned second_size; /* size in bytes */ |
|---|
| 116 | unsigned second_addr; /* physical load addr */ |
|---|
| 117 | |
|---|
| 118 | unsigned tags_addr; /* physical addr for kernel tags */ |
|---|
| 119 | unsigned page_size; /* flash page size we assume */ |
|---|
| 120 | unsigned unused[2]; /* future expansion: should be 0 */ |
|---|
| 121 | |
|---|
| 122 | unsigned char name[BOOT_NAME_SIZE]; /* asciiz product name */ |
|---|
| 123 | |
|---|
| 124 | unsigned char cmdline[BOOT_ARGS_SIZE]; |
|---|
| 125 | |
|---|
| 126 | unsigned id[8]; /* timestamp / checksum / sha1 / etc */ |
|---|
| 127 | }; |
|---|
| 128 | =cut |
|---|
| 129 | sub parse_header { |
|---|
| 130 | my ($fn) = @_; |
|---|
| 131 | my $buf = undef; |
|---|
| 132 | |
|---|
| 133 | open INF, $fn or die "Could not open $fn: $!\n"; |
|---|
| 134 | binmode INF; |
|---|
| 135 | |
|---|
| 136 | # Read the Magic |
|---|
| 137 | read(INF, $buf, BOOT_MAGIC_SIZE); |
|---|
| 138 | unless ($buf eq BOOT_MAGIC) { |
|---|
| 139 | die "Android Magic not found in $fn. Giving up.\n"; |
|---|
| 140 | } |
|---|
| 141 | |
|---|
| 142 | # Read kernel size and address (assume little-endian) |
|---|
| 143 | read(INF, $buf, UNSIGNED_SIZE * 2); |
|---|
| 144 | my ($k_size, $k_addr) = unpack("VV", $buf); |
|---|
| 145 | |
|---|
| 146 | # Read ramdisk size and address (assume little-endian) |
|---|
| 147 | read(INF, $buf, UNSIGNED_SIZE * 2); |
|---|
| 148 | my ($r_size, $r_addr) = unpack("VV", $buf); |
|---|
| 149 | |
|---|
| 150 | # Read second size and address (assume little-endian) |
|---|
| 151 | read(INF, $buf, UNSIGNED_SIZE * 2); |
|---|
| 152 | my ($s_size, $s_addr) = unpack("VV", $buf); |
|---|
| 153 | |
|---|
| 154 | # Ignore tags_addr |
|---|
| 155 | read(INF, $buf, UNSIGNED_SIZE); |
|---|
| 156 | |
|---|
| 157 | # get the page size (assume little-endian) |
|---|
| 158 | read(INF, $buf, UNSIGNED_SIZE); |
|---|
| 159 | my ($p_size) = unpack("V", $buf); |
|---|
| 160 | |
|---|
| 161 | # Ignore unused |
|---|
| 162 | read(INF, $buf, UNSIGNED_SIZE * 2); |
|---|
| 163 | |
|---|
| 164 | # Read the name (board name) |
|---|
| 165 | read(INF, $buf, BOOT_NAME_SIZE); |
|---|
| 166 | my $name = $buf; |
|---|
| 167 | |
|---|
| 168 | # Read the command line |
|---|
| 169 | read(INF, $buf, BOOT_ARGS_SIZE); |
|---|
| 170 | my $cmdline = $buf; |
|---|
| 171 | |
|---|
| 172 | # Ignore the id |
|---|
| 173 | read(INF, $buf, UNSIGNED_SIZE * 8); |
|---|
| 174 | |
|---|
| 175 | # Close the file |
|---|
| 176 | close INF; |
|---|
| 177 | |
|---|
| 178 | # Print important values |
|---|
| 179 | printf "Page size: %d (0x%08x)\n", $p_size, $p_size; |
|---|
| 180 | printf "Kernel size: %d (0x%08x)\n", $k_size, $k_size; |
|---|
| 181 | printf "Kernel addr: %d (0x%08x)\n", $k_addr, $k_addr; |
|---|
| 182 | printf "Ramdisk size: %d (0x%08x)\n", $r_size, $r_size; |
|---|
| 183 | printf "Ramdisk addr: %d (0x%08x)\n", $r_addr, $r_addr; |
|---|
| 184 | printf "Second size: %d (0x%08x)\n", $s_size, $s_size; |
|---|
| 185 | printf "Second addr: %d (0x%08x)\n", $s_addr, $s_addr; |
|---|
| 186 | printf "Board name: $name\n"; |
|---|
| 187 | printf "Command line: $cmdline\n"; |
|---|
| 188 | |
|---|
| 189 | # Save the values |
|---|
| 190 | $PAGE_SIZE = $p_size; |
|---|
| 191 | $KERNEL_SIZE = $k_size; |
|---|
| 192 | $RAMDISK_SIZE = $r_size; |
|---|
| 193 | $SECOND_SIZE = $s_size; |
|---|
| 194 | } |
|---|
| 195 | |
|---|
| 196 | sub dump_file { |
|---|
| 197 | my ($infn, $outfn, $offset, $size) = @_; |
|---|
| 198 | my $buf = undef; |
|---|
| 199 | |
|---|
| 200 | open INF, $infn or die "Could not open $infn: $!\n"; |
|---|
| 201 | open OUTF, ">$outfn" or die "Could not open $outfn: $!\n"; |
|---|
| 202 | |
|---|
| 203 | binmode INF; |
|---|
| 204 | binmode OUTF; |
|---|
| 205 | |
|---|
| 206 | seek(INF, $offset, 0) or die "Could not seek in $infn: $!\n"; |
|---|
| 207 | read(INF, $buf, $size) or die "Could not read $infn: $!\n"; |
|---|
| 208 | print OUTF $buf or die "Could not write $outfn: $!\n"; |
|---|
| 209 | |
|---|
| 210 | close INF; |
|---|
| 211 | close OUTF; |
|---|
| 212 | } |
|---|
| 213 | |
|---|
| 214 | ###################################################################### |
|---|
| 215 | ## Configuration Subroutines |
|---|
| 216 | |
|---|
| 217 | sub parse_cmdline { |
|---|
| 218 | unless ($#ARGV == 0) { |
|---|
| 219 | die "Usage: $SCRIPT boot.img\n"; |
|---|
| 220 | } |
|---|
| 221 | $IMAGE_FN = $ARGV[0]; |
|---|
| 222 | } |
|---|
| 223 | |
|---|
| 224 | |
|---|