7 mkfirm - create and change firmware and flash images
9 Copyright (c) 2006 Stefan Weil
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
25 ADAM2 bootloader flash layout
26 =============================
28 See http://wiki.ip-phone-forum.de/software:ds-mod:development:flash
32 flash: mtd[0] 0x900C0000 - 0x903C0000 (filesystem)
33 flash: mtd[1] 0x90010000 - 0x900C0000 (kernel) 720896
34 flash: mtd[2] 0x90000000 - 0x90010000 (urlader)
35 flash: mtd[3] 0x903C0000 - 0x903E0000 (tffs)
36 flash: mtd[4] 0x903E0000 - 0x90400000 (tffs)
37 flash: mtd[5] 0x900a1b00 - 0x900c0000 (hidden filesystem)
41 mtd0 0x90000000,0x90000000
42 mtd1 0x90010000,0x90780000
43 mtd2 0x90000000,0x90010000
44 mtd3 0x90780000,0x907C0000
45 mtd4 0x907C0000,0x90800000
47 These addresses are only examples. They differ for different flash sizes.
49 BRN bootloader flash layout
50 ===========================
52 0xb0000000.bin flash image
53 0xb0020000.bin configuration
55 0xb0110000.bin code 917504
59 Vergleich zip code und code partition:
61 --- 1 2006-04-22 11:52:11.000000000 +0200
62 +++ 2 2006-04-22 11:52:21.000000000 +0200
63 @@ -43176,5 +43176,8 @@
64 000a8aa0 00 00 00 00 00 a4 81 00 00 00 00 73 6f 68 6f 2e |.....¤.....soho.|
65 000a8ab0 62 69 6e 55 54 05 00 03 1f 35 79 41 55 78 00 00 |binUT....5yAUx..|
66 000a8ac0 50 4b 05 06 00 00 00 00 01 00 01 00 43 00 00 00 |PK..........C...|
67 -000a8ad0 7d 8a 0a 00 00 00 |}.....|
69 +000a8ad0 7d 8a 0a 00 00 00 ff ff ff ff ff ff ff ff ff ff |}.....ÿÿÿÿÿÿÿÿÿÿ|
70 +000a8ae0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ|
72 +000dfff0 ff ff ff ff d6 8a 0a 00 78 56 34 12 cb 3c 60 2d |ÿÿÿÿÖ...xV4.Ë<`-|
75 data (zipped soho.img)
77 (4) length of data (little endian)
78 (4) magic number 0x12345678 (little endian)
84 require 'zlib' # Zlib.crc32
89 FLASHIMAGE = 'boot/flashimage.bin'
97 # Sinus 154 DSL Basic SE
98 # Sinus 154 DSL Basic 3
100 'boot' => { FILE => '0xb0000000.bin', START => 0xb0000000, SIZE => 0x20000, LABEL => 'boot'},
101 'configuration' => { FILE => '0xb0020000.bin', START => 0xb0020000, SIZE => 0x20000, LABEL => 'configuration'},
102 'web' => { FILE => '0xb0040000.bin', START => 0xb0040000, SIZE => 0xd0000, LABEL => 'web'},
103 'code' => { FILE => '0xb0110000.bin', START => 0xb0110000, SIZE => 0xe0000, LABEL => 'code'},
104 'params' => { FILE => '0xb01f0000.bin', START => 0xb01f0000, SIZE => 0x10000, LABEL => 'params'}
109 #~ 'boot' => { FILE => '0xb0000000.bin', START => 0xb0000000, SIZE => 0x20000, LABEL => 'boot'},
110 #~ 'configuration' => { FILE => '0xb0020000.bin', START => 0xb0020000, SIZE => 0x20000, LABEL => 'configuration'},
111 #~ 'web' => { FILE => '0xb0040000.bin', START => 0xb0040000, SIZE => 0x50000, LABEL => 'web'},
112 #~ 'code' => { FILE => '0xb0110000.bin', START => 0xb0090000, SIZE => 0x150000, LABEL => 'code'},
113 #~ 'params' => { FILE => '0xb01f0000.bin', START => 0xb01f0000, SIZE => 0x10000, LABEL => 'params'}
116 # AVM Fritz!Box Fon WLAN
119 #~ 'filesystem' 0x9d100, 0xc0000
123 BASEADDRESS = PARTITIONS['boot'][START]
127 @partition = PARTITIONS[@name]
134 @data = "\xff" * size
137 offset = start - Partition::BASEADDRESS
138 return offset ... offset + size
141 return @partition[SIZE]
144 return @partition[START]
147 raise if firmware.size > size
149 @data[0 ... firmware.size] = firmware
150 @data[size - 3 * 4 ... size] = [firmware.size, 0x12345678, Zlib.crc32(firmware)].pack('V3')
155 def initialize(filename = FLASHIMAGE)
157 @flashimage = Flashimage.read(filename)
160 def update(partition)
161 @flashimage[partition.range] = partition.data
162 #~ puts("#{__FILE__}:#{__LINE__} #{@flashimage.size}")
165 def read(filename = @filename)
166 @flashimage = Flashimage.read(filename)
171 Flashimage.write(@flashimage, @filename)
178 def self.read(filename = FLASHIMAGE)
180 File.open(filename, 'rb') { |f|
186 def self.write(flashdata, filename = FLASHIMAGE)
187 File.open(filename, 'wb') { |f|
193 # Create an erased flash.
195 flashdata = "\xff" * FLASHSIZE
196 Flashimage.write(flashdata)
199 # Split flash image in partitions.
200 def splitflashimage(filename)
201 filename = FLASHIMAGE if filename.nil?
202 flashdata = Flashimage.read(filename)
203 size = flashdata.size
204 puts("Flash image size: #{size / MiB} MiB = #{size} B.")
205 raise('Flash size is unexpected.') if (size != FLASHSIZE)
206 baseaddress = PARTITIONS['boot'][START]
207 PARTITIONS.each { |label, partition|
208 first = partition[START] - baseaddress
209 last = first + partition[SIZE]
210 filename = "#{File.dirname(FLASHIMAGE)}/#{partition[FILE]}"
211 Flashimage.write(flashdata[first ... last], filename)
215 # Make flash image from partitions.
217 flashdata = "\xff" * FLASHSIZE
218 baseaddress = PARTITIONS['boot'][START]
219 PARTITIONS.each { |label, partition|
220 first = partition[START] - baseaddress
221 last = first + partition[SIZE]
222 filename = "#{File.dirname(FLASHIMAGE)}/#{partition[FILE]}"
223 partdata = Flashimage.read(filename)
225 puts("Partition size: #{size / KiB} KiB = #{size} B (#{label}).")
226 raise('Partition size is unexpected.') if (size != partition[SIZE])
227 flashdata[first ... last] = partdata
229 Flashimage.write(flashdata)
232 # Change partition (configuration, web or code).
233 def changepartition(partition, filename)
234 baseaddress = PARTITIONS['boot'][START]
235 size = partition[SIZE]
236 partdata = Flashimage.read(filename)
237 length = partdata.size
238 last = partition[SIZE]
239 raise('Input file too large.') if length + 12 > last
240 crc32 = Zlib.crc32(partdata)
241 partdata[length ... last - 12] = "\xff" * (last - length - 12)
242 partdata[last - 12 ... last] = [length, 0x12345678, crc32].pack('V3')
243 filename = "#{File.dirname(FLASHIMAGE)}/#{partition[FILE]}"
244 Flashimage.write(partdata, filename)
247 def getblock(text, firmware, offset)
249 puts("Get #{text} part...")
251 length, magic, crc32 = firmware.unpack("x#{offset}V3")
252 while firmware[offset - 1] == 0xff
256 block_start = offset - length
257 puts("offset: #{'0x%08x' % block_start}")
258 puts("length: #{'0x%08x' % length} = #{length}")
259 if magic == 0x12345678
260 puts("magic: #{'0x%08x' % magic}, ok")
262 puts("magic: #{'0x%08x' % magic}, bad")
264 if crc32 == Zlib.crc32(firmware[block_start ... block_start + length])
265 puts("crc32: #{'0x%08x' % crc32}, ok")
267 puts("crc32: #{'0x%08x' % crc32}, bad")
269 if firmware[block_start ... block_start + 2] != 'PK'
270 puts("zipdata: missing 'PK' at offset 0")
272 if firmware[block_start + 2] != 3
273 puts("zipdata: missing 0x03 at offset 2")
275 if firmware[block_start + 3] != 4
276 puts("zipdata: missing 0x04 at offset 3")
278 if (firmware[block_start + 7] & 1) == 1
279 puts("zipdata: wrong bit 0 at offset 7")
281 if firmware[block_start + 8 .. block_start + 9] != "\x08\x00"
282 puts("zipdata: missing 0x08,0x00 at offset 8")
284 return length, magic, crc32, block_start
287 # Load partition file (code, web or configuration).
288 def loadpart(partname, filename)
289 flashimage = Flashimage.new
290 firmware = Flashimage.read(filename)
292 offset_signature = size - 10
293 signature = firmware[offset_signature .. -1]
294 puts("signature: #{signature.inspect}")
295 length, magic, crc32, offset = getblock(partname, firmware, offset_signature)
296 partition = Partition.new(partname)
297 partition.update(firmware[offset ... offset + length])
298 flashimage.update(partition)
302 # Load firmware file (code and web).
304 flashimage = Flashimage.new
305 firmware = Flashimage.read(filename)
307 offset_signature = size - 10
308 signature = firmware[offset_signature .. -1]
309 puts("signature: #{signature.inspect}")
310 length, magic, crc32, offset_code = getblock('code', firmware, offset_signature)
311 code = Partition.new('code')
312 code.update(firmware[offset_code ... offset_code + length])
314 length, magic, crc32, offset_web = getblock('web', firmware, offset_code)
315 web = Partition.new('web')
317 web.update(firmware[offset_web ... offset_web + length])
318 flashimage.update(code)
319 flashimage.update(web)
324 #~ FLASHIMAGE = "boot/adam2-flashimage.bin"
325 FLASHIMAGE = "boot/test/flashimage.bin"
327 if FLASHSIZE == 2 * MiB
328 MTD0start = 0x00000000
329 MTD1start = 0x00010000
330 MTD2start = 0x00000000
331 MTD3start = 0x00780000
332 MTD4start = 0x007c0000
334 #~ MTD1end = MTD3start
336 MTD0size = MTD0end - MTD0start
337 MTD1size = MTD1end - MTD1start
338 elsif FLASHSIZE == 4 * MiB
340 MTD0start = 0x000c0000
341 MTD1start = 0x00010000
342 MTD2start = 0x00000000
343 MTD3start = 0x003c0000
344 MTD4start = 0x003e0000
348 MTD0size = MTD0end - MTD0start
349 MTD1size = MTD1end - MTD1start
350 MTD2size = MTD2end - MTD2start
351 elsif FLASHSIZE == 8 * MiB
354 #~ mtd0 0x90000000,0x90000000 # filesystem
355 #~ mtd1 0x90010000,0x90780000 # kernel
356 #~ mtd2 0x90000000,0x90010000 # ADAM2
357 #~ mtd3 0x90780000,0x907C0000
358 #~ mtd4 0x907C0000,0x90800000
363 def self.loadadam2(filename, imagename)
364 imagename = FLASHIMAGE if imagename.nil?
365 flashimage = Flashimage.new(imagename)
366 flashimage.data[MTD2start ... MTD2end] = "\xff" * MTD2size
367 data = Flashimage.read(filename)
368 puts("Bootloader size: #{'0x%08x' % data.size} byte (max #{'0x%08x' % MTD2size}")
369 datasize = data.size - 8
370 raise if datasize > MTD2size
371 flashimage.data[MTD2start ... MTD2start + datasize] = data[0 ... datasize]
375 def self.loadfilesystem(filename, imagename)
376 imagename = FLASHIMAGE if imagename.nil?
377 flashimage = Flashimage.new(imagename)
378 flashimage.data[MTD0start ... MTD0end] = "\xff" * MTD0size
379 data = Flashimage.read(filename)
380 puts("Filesystem size: #{'0x%08x' % data.size} byte (max #{'0x%08x' % MTD0size}")
381 datasize = data.size - 8
382 raise if datasize > MTD0size
383 flashimage.data[MTD0start ... MTD0start + datasize] = data[0 ... datasize]
387 def self.loadkernel(filename, imagename)
388 imagename = FLASHIMAGE if imagename.nil?
389 flashimage = Flashimage.new(imagename)
390 flashimage.data[MTD1start ... MTD1end] = "\xff" * MTD1size
391 data = Flashimage.read(filename)
392 puts("Kernel size: #{'0x%08x' % data.size} byte (max #{'0x%08x' % MTD1size}")
393 datasize = data.size - 8
394 #~ raise if datasize > MTD1size
395 flashimage.data[MTD1start ... MTD1start + datasize] = data[0 ... datasize]
400 # Extract bootloader from binary file.
401 def extract(filename, destination)
402 data = Flashimage.read(filename)
403 offset = data.index("\x00\x90\x80\x40")
405 data = data[offset .. -1]
406 puts("Bootloader at offset #{offset}")
408 Flashimage.write(data[0 ... 0x10000], "#{destination}")
411 offset = data.index("\x00\x90\x80\x40")
415 # Extract squashfs from binary file.
416 def getsquashfs(filename, destination)
417 data = Flashimage.read(filename)
419 offset = data.index('hsqs')
421 data = data[offset .. -1]
422 puts("SQUASH filesystem at offset #{offset}")
425 Flashimage.write(data, "#{destination}#{n}.squashfs")
428 offset = data.index('hsqs')
433 PARTITIONS.each { |label, partition|
435 puts partition.inspect
441 if command == 'create'
443 elsif command == 'extract-bootloader'
444 extract(ARGV[1], ARGV[2])
445 elsif command == 'split'
446 # Split flash image in partitions.
447 splitflashimage(ARGV[1])
448 elsif command == 'merge'
449 # Make flash image from partitions.
451 elsif command == 'load-fw'
452 # Load firmware file (code and web).
454 elsif command == 'load-configuration'
455 # Load configuration partition from file into flash.
456 loadpart('configuration', ARGV[1])
457 elsif command == 'load-web'
458 # Load web partition from file into flash.
459 loadpart('web', ARGV[1])
460 elsif command == 'load-code'
461 # Load code partition from file into flash.
462 loadpart('code', ARGV[1])
463 elsif command == 'load-bootloader'
464 # Load kernel from file into flash (AVM).
465 Adam2.loadadam2(ARGV[1], ARGV[2])
466 elsif command == 'load-kernel'
467 # Load kernel from file into flash (AVM).
468 Adam2.loadkernel(ARGV[1], ARGV[2])
469 elsif command == 'load-filesystem'
470 # Load filesystem from file into flash (AVM).
471 Adam2.loadfilesystem(ARGV[1], ARGV[2])
472 elsif command == 'configuration'
473 # Modify web partition.
474 changepartition(PARTITIONS['configuration'], ARGV[1])
475 elsif command == 'web'
476 # Modify web partition.
477 changepartition(PARTITIONS['web'], ARGV[1])
478 elsif command == 'code'
479 # Modify code partition.
480 changepartition(PARTITIONS['code'], ARGV[1])
481 elsif command == 'getsquashfs'
482 # Extract squashfs from binary file.
483 getsquashfs(ARGV[1], ARGV[2])
484 elsif command == 'test'
487 puts("Unknown command #{command.inspect}.")
488 puts("Supported commands: create, split, merge, load-fw, load-web, load-code,")
489 puts("configuration, code, web, getsquashfs, test.")