diff options
Diffstat (limited to 'ubi-utils/src/mkpfi')
| -rw-r--r-- | ubi-utils/src/mkpfi/f128_nand_sample.cfg | 38 | ||||
| -rw-r--r-- | ubi-utils/src/mkpfi/f64_nor_sample.cfg | 39 | ||||
| -rwxr-xr-x | ubi-utils/src/mkpfi/mkpfi | 723 | 
3 files changed, 800 insertions, 0 deletions
| diff --git a/ubi-utils/src/mkpfi/f128_nand_sample.cfg b/ubi-utils/src/mkpfi/f128_nand_sample.cfg new file mode 100644 index 0000000..e468d9d --- /dev/null +++ b/ubi-utils/src/mkpfi/f128_nand_sample.cfg @@ -0,0 +1,38 @@ +[targets] +complete=ipl,spl,bootenv,kernel,rootfs +bootcode=spl,bootenv + +# Build sections +[ipl]  +image=ipl.bin +raw_starts=0x00000000 +raw_total_size=128kiB  + +[spl] +image=u-boot.bin +ubi_ids=2,3 +ubi_size=2MiB +ubi_type=static +ubi_names=spl_0,spl_1 + +[bootenv] +bootenv_file=bootenv_complete.txt +ubi_ids=4,5 +ubi_size=128kiB +ubi_type=static +ubi_names=bootenv_0,bootenv_1 + +[kernel] +image=vmlinux.bin +ubi_ids=6,7 +ubi_size=6MiB +ubi_type=static +ubi_names=kernel_0,kernel_1 + +[rootfs] +image=rootfs.bin +ubi_ids=8,9 +ubi_alignment=2kiB +ubi_size=16MiB  +ubi_type=dynamic +ubi_names=rootfs_0,rootfs_1 diff --git a/ubi-utils/src/mkpfi/f64_nor_sample.cfg b/ubi-utils/src/mkpfi/f64_nor_sample.cfg new file mode 100644 index 0000000..fd44e27 --- /dev/null +++ b/ubi-utils/src/mkpfi/f64_nor_sample.cfg @@ -0,0 +1,39 @@ +[targets] +complete=ipl,spl,bootenv,kernel,rootfs +bootcode=spl,bootenv +rootfs=rootfs + +# Build sections +[ipl]  +image=ipl.bin +raw_starts=0x02FE0000, 0x03FE0000 +raw_total_size=128kiB  + +[spl] +image=u-boot.bin +ubi_ids=2,3 +ubi_size=2MiB +ubi_type=static +ubi_names=spl_0,spl_1 + +[bootenv] +bootenv_file=bootenv_complete.txt +ubi_ids=4,5 +ubi_size=128kiB +ubi_type=static +ubi_names=bootenv_0,bootenv_1 + +[kernel] +image=vmlinux.bin +ubi_ids=6,7 +ubi_size=6MiB +ubi_type=static +ubi_names=kernel_0,kernel_1 + +[rootfs] +image=rootfs.bin +ubi_ids=8,9 +ubi_alignment=2kiB +ubi_size=16128kiB  +ubi_type=dynamic +ubi_names=rootfs_0,rootfs_1 diff --git a/ubi-utils/src/mkpfi/mkpfi b/ubi-utils/src/mkpfi/mkpfi new file mode 100755 index 0000000..2cce587 --- /dev/null +++ b/ubi-utils/src/mkpfi/mkpfi @@ -0,0 +1,723 @@ +#!/usr/bin/perl +# +# Copyright (c) International Business Machines Corp., 2006 +# +# 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., 675 Mass Ave, Cambridge, MA 02139, USA. +# + +# +# mkpfi +# +# This perl program is assembles PFI files from a config file. +# +# Author: Oliver Lohmann (oliloh@de.ibm.com) +# +use warnings; +use strict; +use lib "/usr/lib/perl5"; # Please change this path as you need it, or +			  # make a proposal how this could be done +			  # nicer. +use Getopt::Long; +use Pod::Usage; +use Config::IniFiles; +use File::Temp; + +# ---------------------------------------------------------------------------- +# Versions +our $version : unique = "0.1"; +our $pfi_version : unique = "0x1"; + +# ---------------------------------------------------------------------------- +# Globals +my $verbose = 0; +my $cfg; + +my %opts = (); +my %files = (config => ""); +my @tmp_files; + +my %tools = (ubicrc32 => "ubicrc32"); + +# ---------------------------------------------------------------------------- +# Processing the input sections +# +# The idea is to combine each section entry with a function +# in order to allow some kind of preprocessing for the values +# before they are written into the PFI file. +# This is especially useful to be more verbose and +# user-friendly in the layout file. +# +# All key-function hashes are applied after the general +# validation of the configuration file. +# If any mandatory key is missing in a section the user +# will be informed and the PFI creation process is aborted. +# +# Default keys will be checked for their presence inside the config +# file. If they are missing, they will be generated with appr. values. + +# Mandatory keys for UBI volumes. +my %ubi_keys = ("ubi_ids"       => \&check_id_list, +		"ubi_size"      => \&replace_num, +		"ubi_type"      => \&replace_type, +		"ubi_names"     => \&remove_spaces, +		"ubi_alignment" => \&replace_num); + +# Mandatory keys for RAW sections. +my %raw_keys = ("raw_starts"     => \&expand_starts, +		"raw_total_size" => \&replace_num); + +# Common default keys for documentation and control purposes. +my %common_keys = ("flags" => \&replace_num, +		   "label" => \&do_nothing); + +# Define any defaults here. Values which maintained in this default +# region need not to be specified by the user explicitly. +my %def_ubi_keys      = ("ubi_alignment" => [\&set_default, "0x1"]); +my %def_raw_keys      = (); +my %def_common_keys   = ("flags"	 => [\&set_default, "0x0"], +			 "label"	 => [\&generate_label, ""]); + +# ---------------------------------------------------------------------------- +# Input keys, actually the path to the input data. + +my %input_keys = ("image" => \&do_nothing); + +# Placeholder keys allow the replacement via a special +# purpose function. E.g. the bootenv_file key will be used +# to generate bootenv binary data from an text file and +# replace the bootenv_file key with an image key to handle it +# in the same way in the further creation process. +my %input_placeholder_keys = ("bootenv_file" => \&create_bootenv_image); + +# ---------------------------------------------------------------------------- +# Helper + +# @brief Get current time string. +sub get_date { +	my $tmp = scalar localtime; +	$tmp =~ s/ /_/g; +	return $tmp; +} + +# @brief Print an info message to stdout. +sub INFO($) { +	my $str = shift; + +	if (!$verbose) { +		return; +	} + +	print STDOUT $str; +} + +# @brief Print an error message to stderr. +sub ERR($) { +	my $str = shift; +	print STDERR $str; +} + +# @brief Print a warning message to stderr. +sub WARN($) { +	my $str = shift; +	print STDERR $str; +} + +sub parse_command_line($) { +	my $opt = shift; +	my $result = GetOptions( "help"	     => \$$opt{'help'}, +				 "man"	     => \$$opt{'man'}, +				 "config=s"  => \$$opt{'config'}, +				 "verbose"   => \$$opt{'verbose'}, +			       ) or pod2usage(2); +	pod2usage(1) if defined ($$opt{help}); +	pod2usage(-verbose => 2) if defined ($$opt{man}); + +	$verbose = $$opt{verbose} if defined $$opt{verbose}; + +	if (!defined $$opt{config}) { +		ERR("[ ERROR: No config file specified. Aborting...\n"); +		exit 1; +	} + +} + +# @brief Check if all needed tools are in PATH. +sub check_tools { +	my $err = 0; +	my $key; + +	foreach $key (keys %tools) { +		if (`which $tools{$key}` eq "") { +			ERR("\n") if ($err == 0); +			ERR("! Please add the tool \'$tools{$key}\' " . +				"to your path!\n"); +			$err = 1; +		} +	} +	die "[ ERROR: Did not find all needed tools!\n" if $err; +} + +sub open_cfg_file($) { +	my $fname = shift; +	my $res = new Config::IniFiles( -file => $fname ); + +	die "[ ERROR: Cannot load your config file!\n" if (!defined $res); +	return $res; +} + +sub set_default($$$$) { +	my ($cfg, $section, $parameter, $def_value) = @_; +	$cfg->newval($section, $parameter, $def_value); +	return; +} + +sub generate_label($$$$) { +	my ($cfg, $section, $parameter, $def_value) = @_; +	my $new_label = $def_value . $section; +	$new_label .= "_" . get_date; +	$cfg->newval($section, $parameter, $new_label); +	return; +} + +# @brief   Converts any num to a unified hex string, i.e the resulting value +#	   always starts with "0x" and is aligned to 8 hexdigits. +# @return  Returns 0 on success, otherwise an error occured. +# +sub any_num_to_hex($$) { +	my $val = shift; +	my $res = shift; + +	# M(iB) +	if ($val =~ m/([0-9]+)[Mm][i]?[Bb]?/g) { +		$$res = sprintf("0x%08x", $1 * 1024 * 1024); +	} +	# k(iB) +	elsif ($val =~ m/([0-9]+)[kK][i]?[Bb]?/g) { +		$$res = sprintf("0x%08x", $1 * 1024); +	} +	# hex +	elsif ($val =~ m/0x?([0-9a-fA-F]+)/g) { +		$$res = sprintf("0x%08x", hex $1); +	} +	# decimal +	elsif ($val =~ m/^([0-9]+)$/g) { +		$$res = sprintf("0x%08x", $1); +	} +	else { +		$$res = ""; +		return -1; +	} + +	return 0; +} + +sub remove_spaces($$$) { +	my ($cfg, $section, $parameter) = @_; +	my ($start, @starts, @new_starts); +	my $val = $cfg->val($section, $parameter); +	my $res; + +	$val =~ s/ //g; # spaces +	$cfg->newval($section, $parameter, $val); +} + +sub expand_starts($$$) { +	my ($cfg, $section, $parameter) = @_; +	my ($start, @starts, @new_starts); +	my $val = $cfg->val($section, $parameter); +	my $res; + +	$val =~ s/ //g; # spaces +	@starts = split(/,/, $val); + +	foreach $start (@starts) { +		if (any_num_to_hex($start, \$res) != 0) { +			ERR("[ ERROR: [$section]\n"); +			ERR("[        Expecting a list of numeric " . +			    "values for parameter: $parameter\n"); +			exit 1; +		} +		push (@new_starts, $res); +	} +	$res = join(',', @starts); + +	$cfg->newval($section, $parameter, $res); +} + +sub check_id_list($$$) { +	my ($cfg, $section, $parameter) = @_; +	my $val = $cfg->val($section, $parameter); +	my $res; + +	if (!($val =~ m/^[0-9]+[,0-9]*/)) { +		ERR("[ ERROR: Syntax error in 'ubi_ids' in " . +		    "section '$section': $val\n"); +			ERR("[ Aborting... "); +			exit 1; +	} +} + +sub replace_type($$$) { +	my ($cfg, $section, $parameter) = @_; +	my $val = $cfg->val($section, $parameter); +	my $res; + +	$res = lc($val); +	grep {$res eq $_} ('static', 'dynamic') +	    or die "[ ERROR: Unknown UBI Volume Type in " . +	    "section '$section': $val\n"; + +	$cfg->newval($section, $parameter, $res); +} + + +sub replace_num($$$) { +	my ($cfg, $section, $parameter) = @_; +	my $val = $cfg->val($section, $parameter); +	my $res = ""; + +	if (any_num_to_hex($val, \$res) != 0) { +		ERR("[ ERROR: [$section]\n"); +		ERR("[        Expecting a numeric value " . +		    "for parameter: $parameter\n"); +		exit 1; +	} +	$cfg->newval($section, $parameter, $res); +} + +sub do_nothing($$$) { +	my ($cfg, $section, $parameter) = @_; +	return; +} + +sub bootenv_sanity_check($) { +	my $env = shift;	# hash array containing bootenv +	my %pdd = (); + +	defined($$env{'pdd'}) or return "'pdd' not defined"; +	foreach (split /,/, $$env{'pdd'}) { +		defined($$env{$_}) or return "undefined '$_' in pdd"; +		$pdd{$_} = 1; +	} + +	defined $$env{'pdd_preserve'} or +		return ""; +	foreach (split /,/, $$env{'pdd_preserve'}) { +		defined($pdd{$_}) +			or return "pdd_preserve field '$_' not in pdd"; +	} +	return ""; +} + +sub create_bootenv_image($$$) { +	my ($cfg, $section, $parameter) = @_; +	my $txt_fn = $cfg->val($section, "bootenv_file"); +	my $in; + +	my %value = (); +	my @key = (); + +	open $in, "<", $txt_fn +		or die "[ ERROR: can't open bootenv file '$txt_fn'.\n"; +	while (<$in>) { +		next if (/^\s*(\#.*)?$/); # Skip comments/whitespace. + +		if (/^(\S+?)\+\=(.*)$/) { +			defined($value{$1}) or +				die "$txt_fn:$.: error: appending to" . +					" non-existent '$1'\n"; +			$value{$1} .= $2; +		} elsif (/^(\S+?)\=(.*)$/) { +			not defined($value{$1}) or +				die "$txt_fn:$.: error: trying to" . +					" redefine '$1'\n"; +			push @key, $1; +			$value{$1} = $2; +		} else { +			die "$txt_fn:$.: error: unrecognized syntax\n"; +		} +	} +	close $in; + +	$_ = &bootenv_sanity_check(\%value) +		and die "$txt_fn: error: $_\n"; + +	my $tmp_file = new File::Temp(); +	push (@tmp_files, $tmp_file); + +	foreach (@key) { +		print $tmp_file "$_=", $value{$_}, "\0"; +	} +	close $tmp_file; + +	$cfg->newval($section, "image", $tmp_file-> filename); +} + +sub process_keys($$$) { +	my ($cfg, $section, $keys) = @_; +	my @parameters = $cfg->Parameters($section); +	my $i; + +	for ($i = 0 ; $i < scalar(@parameters) ; $i++ ) { +		if (defined($$keys{$parameters[$i]})) { +			$$keys{$parameters[$i]}->($cfg, $section, +					$parameters[$i]); +		} +	} + +} + +sub is_in_keylist($$) { +	my ($key, $keys) = @_; +	my $i; + +	for ($i = 0; $i < scalar(@$keys); $i++) { +		if ($$keys[$i] eq $key) { +			return 1; +		} +	} + +	return 0; +} + +sub check_default_keys($$$) { +	my ($cfg, $section, $keys) = @_; +	my @parameters = $cfg->Parameters($section); +	my $key; + +	foreach $key (keys %$keys) { +		if (!is_in_keylist($key, \@parameters)) { +			$$keys{$key}[0]-> +				($cfg, $section, $key, $$keys{$key}[1]); +		} +	} + +} + + + +sub check_keys($$$) { +	my ($cfg, $section, $keys) = @_; +	my @parameters = $cfg->Parameters($section); +	my ($i, $key, $err); + +	$err = 0; +	for ($i = 0 ; $i < scalar(@$keys) ; $i++ ) { +		if (!is_in_keylist($$keys[$i], \@parameters)) { +			ERR("[ ERROR: [$section]\n") if $err == 0; +			$err = 1; +			ERR("[        Missing key '$$keys[$i]'\n"); +		} +	} + +	if ($err) { +		ERR("[ Aborting...\n"); +		exit 1; +	} +} + +sub push_pfi_data($$$$$) { +	my ($cfg, $section, $pfi_infos, $keys, $mode) = @_; +	my ($tmp, $i, $hdr); + +	my %pfi_info = (); +	$pfi_info{'mode'} = $mode; +	$pfi_info{'image'} = $cfg->val($section, "image"); + +	# Build the PFI header +	$hdr  = sprintf("PFI!\n"); +	$hdr .= sprintf("version=0x%08x\n", hex $pfi_version); +	$hdr .= sprintf("mode=$mode\n"); + +	# calculate the size of the binary data part +	$tmp = -s $cfg->val($section, "image"); +	if (!defined $tmp) { +		ERR("[ ERROR: [$section]\n"); +		ERR("[        Missing input image: " +				. $cfg->val($section, "image") . "\n"); +		exit 1; +	} +	# Check for the image to fit into the given space +	my $quota; +	if ($mode eq 'raw') { +		$quota = oct $cfg->val($section, "raw_total_size"); +	} elsif ($mode eq 'ubi') { +		$quota = oct $cfg->val($section, "ubi_size"); +	} +	$tmp <= $quota +		or die "[ERROR: image file too big: " . +		$cfg->val($section, "image") . "\n"; +	$pfi_info{'size'} = $tmp; + +	$hdr .= sprintf("size=0x%08x\n", $tmp); + +	my $img_file = $cfg->val($section, "image"); +	my $crc32 = `$tools{'ubicrc32'} $img_file 2>&1`; +	if (any_num_to_hex($crc32, \$tmp) != 0) { +		die "[ ERROR: $tools{'ubicrc32'} returned with errors"; +	} +	$hdr .= sprintf("crc=$tmp\n"); + + +	# Process all remaining keys +	for ($i = 0; $i < scalar (@$keys); $i++) { +		if ($$keys[$i] eq "image") { # special case image input file +			if (! -e ($tmp = $cfg->val($section, "image"))) { +				ERR("[ ERROR: [$section]\n"); +				ERR("[        Cannot find input file $tmp\n"); +				exit 1; +			} +			next; +		} +		$hdr .= sprintf("%s=%s\n", $$keys[$i], +				$cfg->val($section, $$keys[$i])); +	} + +	$hdr .= sprintf("\n"); # end marker for PFI-header + +	$pfi_info{'header'} = $hdr; + +	# store in the header list +	push @$pfi_infos, \%pfi_info; +} + +sub process_section($$$$$$) { +	my ($cfg, $section, $pfi_infos, $custom_keys, +			$def_custom_keys, $mode) = @_; +	my @keys = (keys %common_keys, keys %$custom_keys); +	my @complete_keys = (@keys, keys %input_keys); + +	# set defaults if necessary +	check_default_keys($cfg, $section, $def_custom_keys); +	check_default_keys($cfg, $section, \%def_common_keys); + +	# check for placeholders... +	process_keys($cfg, $section, \%input_placeholder_keys); + +	# VALIDATE layout.cfg entries +	check_keys($cfg, $section, \@complete_keys); + +	# execute linked functions (if any) +	process_keys($cfg, $section, \%common_keys); +	process_keys($cfg, $section, $custom_keys); + +	push_pfi_data($cfg, $section, $pfi_infos, \@keys, $mode); +} + +sub get_section_info($$) { +	my ($cfg, $section) = @_; +	my @parameters = $cfg->Parameters($section); +	my ($ubi, $raw, $i, @res); + +	$ubi = $raw = 0; +	for ($i = 0 ; $i < scalar(@parameters) ; $i++ ) { +		if ($parameters[$i] =~ m/ubi_/gi) { +			$ubi = 1; +			@res = (\%ubi_keys, \%def_ubi_keys, "ubi"); +		} +		if ($parameters[$i] =~ m/raw_/gi) { +			$raw = 1; +			@res = (\%raw_keys, \%def_raw_keys, "raw"); +		} +	} + +	if (($ubi + $raw) != 1)	{ # double definition in section +		ERR("[ ERROR: Layout error in section '$section'\n"); +		exit 1; +	} + +	return @res; +} + +sub mk_target_list($$) { +	my $val = shift; +	my $tmp = shift; +	my $complete = 0; + +	if ($val =~ m/\((.*)\)/g) { +		$val = $1; +		$complete = 1; +	} +	$val =~ s/ //g; # spaces + +	@$tmp = split(/,/, $val); + +	return $complete; +} + +sub copy_bytes($$$) { +	my ($in, $out, $to_copy) = @_; + +	while ($to_copy) { +		my $buf; +		my $bufsize = 1024*1024; + +		$bufsize < $to_copy or $bufsize = $to_copy; +		read($in, $buf, $bufsize) == $bufsize +			or die "[ ERROR: Image file shrunk during operation\n"; +		print $out $buf; +		$to_copy -= $bufsize; +	} +} + +sub write_target($$) { +	my ($pfi_infos, $target) = @_; +	my ($pfi_info); + +	INFO("[ Writting target pfi file: '$target.pfi'...\n"); +	if (-e "$target.pfi") { +		WARN("! Replaced old pfi...\n"); +		`rm -f $target.pfi`; +	} +	open(FILE, ">", "$target.pfi") +		or die "[ ERROR: Cannot create output file: $target.pfi\n"; +	binmode(FILE); + +	# @FIXME sort by mode (first raw, then ubi) +	# Currently this ordering is based on a string comparism. :-) +	@$pfi_infos = sort {(lc $$a{'mode'}) cmp (lc $$b{'mode'})} @$pfi_infos; + +	# Print all headers first +	foreach $pfi_info (@$pfi_infos) { +		print FILE $$pfi_info{'header'}; + +	} +	# Print the linked data sections +	print FILE "DATA\n"; +	foreach $pfi_info (@$pfi_infos) { +		open(IMAGE, "<", $$pfi_info{'image'}) +				or die "[ ERROR: Cannot open input image: " . +				"$$pfi_info{'image'}" . "\n"; +		binmode(IMAGE); +		©_bytes(\*IMAGE, \*FILE, $$pfi_info{'size'}); +		close(IMAGE) or die "[ ERROR: Cannot close input image: " . +				"$$pfi_info{'image'}" . "\n"; +	} +	close(FILE) or die "[ ERROR: Cannot close output file: $target.pfi\n"; +} + +sub process_config($) { +	my $cfg = shift; +	my @sections = $cfg->Sections; +	my ($i, $j, $keylist, $def_keylist, $mode, $tmp, +			@tlist, $complete,@pfi_infos); + +	my @parameters = $cfg->Parameters("targets") or +		die "[ ERROR: Config file has no 'targets' section!\n"; + +	for ($i = 0 ; $i < scalar(@parameters) ; $i++ ) { +		INFO("[ Processing target '$parameters[$i]'...\n"); +		@pfi_infos = (); + +		# get a list of subtargets +		$complete = mk_target_list($cfg->val("targets", +					$parameters[$i]), \@tlist); +		# build all subtargets +		for ($j = 0 ; $j < scalar(@tlist) ; $j++ ) { +			($keylist, $def_keylist, $mode) +				= get_section_info($cfg, $tlist[$j]); +			process_section($cfg, $tlist[$j], +					\@pfi_infos, +					$keylist, $def_keylist, $mode); +		} + +		write_target(\@pfi_infos, $parameters[$i]); +	} + +	INFO("[ Success.\n"); + + +} + +sub clear_files() { +	# @FIXME: +	# Works implicitly and Fedora seems to have removed +	# the cleanup call. Thus for now, inactive. +	# File::Temp::cleanup(); +} + +require 5.008_000;		# Tested with version 5.8.0. +select STDOUT; $| = 1;		# make STDOUT output unbuffered +select STDERR; $| = 1;		# make STDERR output unbuffered + +parse_command_line(\%opts); +check_tools; +$cfg = open_cfg_file($opts{config}); +process_config($cfg); +clear_files; + +__END__ + + +=head1 NAME + +mkpfi - Using GetOpt::Long, Pod::Usage, Config::IniFiles + + +=head1 SYNOPSIS + +mkpfi  [OPTIONS ...] + + +	OPTION + +	[--config] [--help] [--man] + + +=head1 ABSTRACT + +Perl script for generating pdd pfi files from given config files. + +=head1 OPTIONS + +=over + +=item B<--help> + +Print out brief help message. + +=item B<--usage> + +Print usage. + +=item B<--config> + +Config input file. + +=item B<--man> + +Print manual page, same as 'perldoc mkpfi'. + +=item B<--verbose> + +Be verbose! + +=back + +=head1 BUGS + +Report via MTD mailing list + + +=head1 SEE ALSO + +http://www.linux-mtd.infradead.org/ + + +=head1 AUTHOR + +Oliver Lohmann (oliloh@de.ibm.com) + +=cut | 
