#!/usr/bin/ruby # Create a firmware file suitable for upload to a DNS-323 or CH3SNAS # device. # # Copyright (C) 2008 Matt Palmer # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # # Script based on information provided by Leschinsky Oleg. # # To construct a firmware image, you need six things: a uBoot-packaged # kernel, a uBoot-packaged initrd, the product/custom/model IDs for the # particular device you want to target, and the firmware signature type. # Using the wrong values can result in the device rejecting the firmware. # # Known values for these the IDs and firmware signature type are: # # prod custom model fwtype # DNS-323 rev B1: 7 1 1 FrodoII # CH3SNAS: 7 2 1 FrodoII # DNS-343: 9 1 1 Gandolf # DNS-321: 10 1 1 Chopper # require 'optparse' def main opts = parse_cmdline validate_command_line(opts) k_size = File.stat(opts[:kernel]).size i_size = File.stat(opts[:initrd]).size d_size = opts[:defaults].nil? ? 0 : File.stat(opts[:defaults]).size ctl_header = [ 64, k_size, 64 + k_size, i_size, 64 + k_size + i_size, d_size, checksum(opts[:kernel]), checksum(opts[:initrd]), d_size == 0 ? 0 : checksum(opts[:defaults]), "\x55\xAA#{opts[:signature]}\x00\x55\xAA", opts[:prod_id], opts[:custom_id], opts[:model_id], 1, 3, "\x00" * 7, 0 ].pack("V9a12c5a7V") File.open(opts[:output], 'w') do |fd| fd.write ctl_header fd.write File.read(opts[:kernel]) fd.write File.read(opts[:initrd]) fd.write File.read(opts[:defaults]) unless d_size == 0 end puts "Firmware generation completed successfully." end def validate_command_line(opts) %w{kernel initrd}.each do |k| if !File.exist?(opts[k.to_sym]) $stderr.puts "#{k} file (#{opts[k.to_sym]}) doesn't exist!" exit 1 end if !is_uboot(opts[k.to_sym]) $stderr.puts "#{k} file #{opts[k.to_sym]} is not a uboot file" exit 1 end end if opts[:defaults] and !File.exist?(opts[:defaults]) $stderr.puts "default file (#{opts[:defaults]}) doesn't exist!" exit 1 end if opts[:signature] unless %w{FrodoII Chopper Gandolf}.include? opts[:signature] $stderr.puts "Unknown signature type #{opts[:signature]}" exit 1 end else opts[:signature] = "FrodoII" end end def is_uboot(file) File.read(file, 4) == "\x27\x05\x19\x56" end def checksum(file) sum = 0 File.read(file).unpack("V*").each { |v| sum ^= v } sum end def parse_cmdline opts = OptionParser.new optargs = {} opts.on('-h', '--help', "Print this help") { optargs[:help] = true } opts.on('-k KERNEL', '--kernel KERNEL', "Specify the kernel to include in the firmware image", String) { |optargs[:kernel]| } opts.on('-i INITRD', '--initrd INITRD', "Specify the initrd to include in the firmware image", String) { |optargs[:initrd]| } opts.on('-d DEFAULTS', '--defaults DEFAULTS', "Specify the defaults.tar.gz to include in the firmware image (optional)", String) { |optargs[:defaults]| } opts.on('-o OUTPUT', '--output OUTPUT', "Specify where to put the resulting firmware image", String) { |optargs[:output]| } opts.on('-p PROD_ID', '--product-id PROD_ID', "The product ID to embed in the firmware image", Integer) { |optargs[:prod_id]| } opts.on('-c CUSTOM_ID', '--custom-id CUSTOM_ID', "The custom ID to embed in the firmware image", Integer) { |optargs[:custom_id]| } opts.on('-m MODEL_ID', '--model-id MODEL_ID', "The model ID to embed in the firmware image", Integer) { |optargs[:model_id]| } opts.on('-s SIGNATURE', '--signature SIGNATURE', "The firmware signature type (either FrodoII, Chopper or Gandolf)", String) { |optargs[:signature]| } opts.parse(ARGV) if optargs[:help] $stderr.puts opts.to_s exit 0 end %w{kernel initrd output prod_id custom_id model_id}.each do |k| if optargs[k.to_sym].nil? $stderr.puts "Missing required argument #{k}" exit 1 end end optargs end main