Merge tag 'v4.1.0-rc2'
[qemu/ar7.git] / scripts / mkfirm.rb
blobee3927835bdf8c04673229a286c14195077f5bb1
1 #!/usr/bin/ruby -w
3 =begin
5 $Id$
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
30 4 MiB
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)
39 8 MiB
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 ===========================
51 Files:
52 0xb0000000.bin  flash image
53 0xb0020000.bin  configuration
54 0xb0040000.bin  web
55 0xb0110000.bin  code            917504
56 0xb01f0000.bin  params
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                                 |}.....|
68 -000a8ad6
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.Ë<`-|
73 +000e0000
75         data (zipped soho.img)
76         (n) 0xff fill bytes
77         (4) length of data (little endian)
78         (4) magic number 0x12345678 (little endian)
79         (4) CRC-32 of data
80         (10) signature
82 =end
84 require 'zlib'  # Zlib.crc32
86 KiB = 1024
87 MiB = (KiB * KiB)
89 FLASHIMAGE = 'boot/flashimage.bin'
90 FLASHSIZE = 2 * MiB
92 FILE = 'file'
93 START = 'start'
94 SIZE = 'size'
95 LABEL = 'label'
97 # Sinus 154 DSL Basic SE
98 # Sinus 154 DSL Basic 3
99 PARTITIONS = {
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'}
107 # SX541
108 #~ PARTITIONS = {
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'}
114 #~ }
116 # AVM Fritz!Box Fon WLAN
117 #~ PARTITIONS = {
118         #~ 'code' 0x10000
119         #~ 'filesystem' 0x9d100, 0xc0000
120 #~ }
122 class Partition
123         BASEADDRESS = PARTITIONS['boot'][START]
125         def initialize(name)
126                 @name = name
127                 @partition = PARTITIONS[@name]
128                 erase
129         end
130         def data
131                 return @data
132         end
133         def erase
134                 @data = "\xff" * size
135         end
136         def range
137                 offset = start - Partition::BASEADDRESS
138                 return offset ... offset + size
139         end
140         def size
141                 return @partition[SIZE]
142         end
143         def start
144                 return @partition[START]
145         end
146         def update(firmware)
147                 raise if firmware.size > size
148                 erase
149                 @data[0 ... firmware.size] = firmware
150                 @data[size - 3 * 4 ... size] = [firmware.size, 0x12345678, Zlib.crc32(firmware)].pack('V3')
151         end
154 class Flashimage
155   def initialize(filename = FLASHIMAGE)
156     @filename = filename
157     @flashimage = Flashimage.read(filename)
158   end
160   def update(partition)
161     @flashimage[partition.range] = partition.data
162     #~ puts("#{__FILE__}:#{__LINE__} #{@flashimage.size}")
163   end
165   def read(filename = @filename)
166     @flashimage = Flashimage.read(filename)
167     return @flashimage
168   end
170   def write
171     Flashimage.write(@flashimage, @filename)
172   end
174   def data
175     return @flashimage
176   end
178   def self.read(filename = FLASHIMAGE)
179     flashdata = nil
180     File.open(filename, 'rb') { |f|
181       flashdata = f.read
182     }
183     return flashdata
184   end
186   def self.write(flashdata, filename = FLASHIMAGE)
187     File.open(filename, 'wb') { |f|
188       f.write(flashdata)
189     }
190   end
193 # Create an erased flash.
194 def createflashimage
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)
212         }
215 # Make flash image from partitions.
216 def mergepartitions
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)
224                 size = partdata.size
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
228         }
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)
248         puts
249         puts("Get #{text} part...")
250         offset -= 4 * 3
251         length, magic, crc32 = firmware.unpack("x#{offset}V3")
252         while firmware[offset - 1] == 0xff
253                 offset -= 1
254         end
255         block_end = offset
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")
261         else
262                 puts("magic:     #{'0x%08x' % magic}, bad")
263         end
264         if crc32 == Zlib.crc32(firmware[block_start ... block_start + length])
265                 puts("crc32:     #{'0x%08x' % crc32}, ok")
266         else
267                 puts("crc32:     #{'0x%08x' % crc32}, bad")
268         end
269         if firmware[block_start ... block_start + 2] != 'PK'
270                 puts("zipdata:   missing 'PK' at offset 0")
271         end
272         if firmware[block_start + 2] != 3
273                 puts("zipdata:   missing 0x03 at offset 2")
274         end
275         if firmware[block_start + 3] != 4
276                 puts("zipdata:   missing 0x04 at offset 3")
277         end
278         if (firmware[block_start + 7] & 1) == 1
279                 puts("zipdata:   wrong bit 0 at offset 7")
280         end
281         if firmware[block_start + 8 .. block_start + 9] != "\x08\x00"
282                 puts("zipdata:   missing 0x08,0x00 at offset 8")
283         end
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)
291         size = firmware.size
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)
299         flashimage.write
302 # Load firmware file (code and web).
303 def loadfw(filename)
304         flashimage = Flashimage.new
305         firmware = Flashimage.read(filename)
306         size = firmware.size
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])
313         puts code.size
314         length, magic, crc32, offset_web = getblock('web', firmware, offset_code)
315         web = Partition.new('web')
316         puts web.size
317         web.update(firmware[offset_web ... offset_web + length])
318         flashimage.update(code)
319         flashimage.update(web)
320         flashimage.write
323 module Adam2
324   #~ FLASHIMAGE = "boot/adam2-flashimage.bin"
325   FLASHIMAGE = "boot/test/flashimage.bin"
326   FLASHSIZE = 4 * MiB
327   if FLASHSIZE == 2 * MiB
328     MTD0start = 0x00000000
329     MTD1start = 0x00010000
330     MTD2start = 0x00000000
331     MTD3start = 0x00780000
332     MTD4start = 0x007c0000
333     MTD0end = MTD0start
334     #~ MTD1end = MTD3start
335     MTD1end = 0x00800000
336     MTD0size = MTD0end - MTD0start
337     MTD1size = MTD1end - MTD1start
338   elsif FLASHSIZE == 4 * MiB
339     # 4 MiB flash
340     MTD0start = 0x000c0000
341     MTD1start = 0x00010000
342     MTD2start = 0x00000000
343     MTD3start = 0x003c0000
344     MTD4start = 0x003e0000
345     MTD0end = MTD3start
346     MTD1end = MTD0start
347     MTD2end = MTD1start
348     MTD0size = MTD0end - MTD0start
349     MTD1size = MTD1end - MTD1start
350     MTD2size = MTD2end - MTD2start
351   elsif FLASHSIZE == 8 * MiB
352     raise
353     # 8 MiB flash
354     #~ mtd0    0x90000000,0x90000000    # filesystem
355     #~ mtd1    0x90010000,0x90780000    # kernel
356     #~ mtd2    0x90000000,0x90010000    # ADAM2
357     #~ mtd3    0x90780000,0x907C0000
358     #~ mtd4    0x907C0000,0x90800000
359   else
360     raise
361   end
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]
372     flashimage.write
373   end
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]
384     flashimage.write
385   end
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]
396     flashimage.write
397   end
398 end # Adam2
400 # Extract bootloader from binary file.
401 def extract(filename, destination)
402     data = Flashimage.read(filename)
403     offset = data.index("\x00\x90\x80\x40")
404     while offset
405         data = data[offset .. -1]
406         puts("Bootloader at offset #{offset}")
407         if destination
408           Flashimage.write(data[0 ... 0x10000], "#{destination}")
409         end
410         data = data[4 .. -1]
411         offset = data.index("\x00\x90\x80\x40")
412     end
415 # Extract squashfs from binary file.
416 def getsquashfs(filename, destination)
417     data = Flashimage.read(filename)
418     n = 0
419     offset = data.index('hsqs')
420     while offset
421         data = data[offset .. -1]
422         puts("SQUASH filesystem at offset #{offset}")
423         if destination
424           n += 1
425           Flashimage.write(data, "#{destination}#{n}.squashfs")
426         end
427         data = data[4 .. -1]
428         offset = data.index('hsqs')
429     end
432 def test
433         PARTITIONS.each { |label, partition|
434                 puts label.inspect
435                 puts partition.inspect
436         }
439 command = ARGV[0]
441 if command == 'create'
442         createflashimage
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.
450         mergepartitions
451 elsif command == 'load-fw'
452         # Load firmware file (code and web).
453         loadfw(ARGV[1])
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'
485         test
486 else
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.")
490 end     
492 # eof