summaryrefslogtreecommitdiff
path: root/ubi-utils
diff options
context:
space:
mode:
authorArtem Bityutskiy <Artem.Bityutskiy@nokia.com>2007-05-04 21:00:46 +0300
committerArtem Bityutskiy <Artem.Bityutskiy@nokia.com>2007-05-04 21:00:46 +0300
commit80cecea79cf13075d136e73067aa40439539bb0f (patch)
treef28acc5b3a124c3a7355f77ccdbad48753f55872 /ubi-utils
parent726ac243f051f0daee6149db66ac21ba621fb454 (diff)
parent22f90673165489fd50c893a91051a79c1b143d2a (diff)
Merge branch 'ubi'
Diffstat (limited to 'ubi-utils')
-rw-r--r--ubi-utils/Makefile93
-rw-r--r--ubi-utils/README236
-rw-r--r--ubi-utils/UBI.TXT108
-rw-r--r--ubi-utils/doc/unubi.roff123
-rw-r--r--ubi-utils/inc/libubi.h268
-rw-r--r--ubi-utils/inc/libubiold.h310
-rw-r--r--ubi-utils/lib/Makefile.am58
-rw-r--r--ubi-utils/perl/f128_nand_sample.cfg38
-rw-r--r--ubi-utils/perl/f64_nor_sample.cfg39
-rwxr-xr-xubi-utils/perl/mkpfi723
-rwxr-xr-xubi-utils/perl/ubicrc32.pl74
-rw-r--r--ubi-utils/scripts/Makefile75
-rw-r--r--ubi-utils/scripts/README11
-rw-r--r--ubi-utils/scripts/TODO5
-rwxr-xr-xubi-utils/scripts/jffs2_test.sh91
-rw-r--r--ubi-utils/scripts/pdd.txt16
-rwxr-xr-xubi-utils/scripts/run_all.sh101
-rw-r--r--ubi-utils/scripts/test.cfg23
-rwxr-xr-xubi-utils/scripts/ubi_jffs2_test.sh411
-rwxr-xr-xubi-utils/scripts/ubi_test.sh328
-rwxr-xr-xubi-utils/scripts/ubi_tools_test.sh252
-rw-r--r--ubi-utils/src/bin2nand.c346
-rw-r--r--ubi-utils/src/bootenv.c984
-rw-r--r--ubi-utils/src/bootenv.h424
-rw-r--r--ubi-utils/src/config.h28
-rw-r--r--ubi-utils/src/crc32.c83
-rw-r--r--ubi-utils/src/crc32.h36
-rw-r--r--ubi-utils/src/eb_chain.c287
-rw-r--r--ubi-utils/src/eb_chain.h73
-rw-r--r--ubi-utils/src/error.c240
-rw-r--r--ubi-utils/src/error.h84
-rw-r--r--ubi-utils/src/example_ubi.h28
-rw-r--r--ubi-utils/src/hashmap.c412
-rw-r--r--ubi-utils/src/hashmap.h49
-rw-r--r--ubi-utils/src/libpfiflash.c1076
-rw-r--r--ubi-utils/src/libubi.c917
-rw-r--r--ubi-utils/src/libubi_int.h129
-rw-r--r--ubi-utils/src/libubigen.c486
-rw-r--r--ubi-utils/src/libubimirror.c217
-rw-r--r--ubi-utils/src/libubiold.c768
-rw-r--r--ubi-utils/src/libubiold_int.h119
-rw-r--r--ubi-utils/src/libubiold_sysfs.c232
-rw-r--r--ubi-utils/src/libubiold_sysfs.h109
-rw-r--r--ubi-utils/src/list.c149
-rw-r--r--ubi-utils/src/list.h56
-rw-r--r--ubi-utils/src/mkbootenv.c170
-rw-r--r--ubi-utils/src/nand2bin.c312
-rw-r--r--ubi-utils/src/nandcorr.c85
-rw-r--r--ubi-utils/src/nandecc.c159
-rw-r--r--ubi-utils/src/nandecc.h28
-rw-r--r--ubi-utils/src/pddcustomize.c500
-rw-r--r--ubi-utils/src/peb.c116
-rw-r--r--ubi-utils/src/peb.h41
-rw-r--r--ubi-utils/src/pfi.c458
-rw-r--r--ubi-utils/src/pfi.h244
-rw-r--r--ubi-utils/src/pfi2bin.c682
-rw-r--r--ubi-utils/src/pfiflash.c263
-rw-r--r--ubi-utils/src/pfiflash.h75
-rw-r--r--ubi-utils/src/pfiflash_error.h71
-rw-r--r--ubi-utils/src/reader.c482
-rw-r--r--ubi-utils/src/reader.h87
-rw-r--r--ubi-utils/src/ubicrc32.c143
-rw-r--r--ubi-utils/src/ubigen.c361
-rw-r--r--ubi-utils/src/ubigen.h149
-rw-r--r--ubi-utils/src/ubimirror.c214
-rw-r--r--ubi-utils/src/ubimirror.h66
-rw-r--r--ubi-utils/src/ubimkvol.c322
-rw-r--r--ubi-utils/src/ubirmvol.c230
-rw-r--r--ubi-utils/src/ubiupdatevol.c337
-rw-r--r--ubi-utils/src/unubi.c837
-rw-r--r--ubi-utils/src/unubi_analyze.c435
-rw-r--r--ubi-utils/src/unubi_analyze.h26
-rw-r--r--ubi-utils/testcases.txt9
-rw-r--r--ubi-utils/tests/Makefile40
-rw-r--r--ubi-utils/tests/common.c336
-rw-r--r--ubi-utils/tests/common.h103
-rw-r--r--ubi-utils/tests/integ.c783
-rw-r--r--ubi-utils/tests/io_basic.c182
-rw-r--r--ubi-utils/tests/io_paral.c251
-rw-r--r--ubi-utils/tests/io_read.c398
-rw-r--r--ubi-utils/tests/io_update.c370
-rw-r--r--ubi-utils/tests/mkvol_bad.c311
-rw-r--r--ubi-utils/tests/mkvol_basic.c249
-rw-r--r--ubi-utils/tests/mkvol_paral.c112
-rw-r--r--ubi-utils/tests/rsvol.c310
-rwxr-xr-xubi-utils/tests/runtests.pl30
-rwxr-xr-xubi-utils/tests/runtests.sh41
87 files changed, 21133 insertions, 0 deletions
diff --git a/ubi-utils/Makefile b/ubi-utils/Makefile
new file mode 100644
index 0000000..797807d
--- /dev/null
+++ b/ubi-utils/Makefile
@@ -0,0 +1,93 @@
+#
+# Makefile for ubi-utils
+#
+
+HOST_OS_NAME := $(shell uname -s)
+HOST_VERSION_NAME := $(shell uname -r)
+BUILD_CPU := $(shell uname -m)
+BUILD_OS := $(shell uname -o)
+
+KERNELHDR := ../include
+DESTDIR := /usr/local
+SBINDIR := bin
+
+CC := $(CROSS)gcc
+CFLAGS := -I./inc -I./src -I$(KERNELHDR) -O2 -g -Wall -Werror \
+ -Wwrite-strings -W -std=gnu99 \
+ -DHOST_OS_NAME=\"$(HOST_OS_NAME)\" \
+ -DHOST_VERSION_NAME=\"$(HOST_VERSION_NAME)\" \
+ -DBUILD_CPU=\"$(BUILD_CPU)\" -DBUILD_OS=\"$(BUILD_OS)\" \
+ -DPACKAGE_VERSION=\"1.0\"
+
+PERLPROGS = mkpfi ubicrc32.pl
+TARGETS = ubiupdatevol ubimkvol ubirmvol pfiflash pddcustomize ubimirror \
+ bin2nand nand2bin ubigen mkbootenv unubi pfi2bin ubicrc32
+
+vpath %.c ./src
+
+%: %.o
+ $(CC) $(LDFLAGS) -g -o $@ $^
+
+%.o: %.c
+ $(CC) $(CFLAGS) -g -c -o $@ $< -g -Wp,-MD,.$(shell basename $<).dep
+
+all: $(TARGETS)
+
+IGNORE=${wildcard .*.c.dep}
+-include ${IGNORE}
+
+clean:
+ rm -rf *.o $(TARGETS) .*.c.dep
+
+ubiupdatevol: ubiupdatevol.o error.o libubi.o
+ $(CC) $(LDFLAGS) -o $@ $^
+
+ubimkvol: ubimkvol.o error.o libubi.o
+ $(CC) $(LDFLAGS) -o $@ $^
+
+ubirmvol: ubirmvol.o error.o libubi.o
+ $(CC) $(LDFLAGS) -o $@ $^
+
+pddcustomize: pddcustomize.o error.o libubimirror.o bootenv.o hashmap.o \
+ libubiold.o libubiold_sysfs.o crc32.o
+ $(CC) $(LDFLAGS) -o $@ $^
+
+pfiflash: pfiflash.o libpfiflash.o list.o reader.o error.o libubimirror.o \
+ bootenv.o hashmap.o pfi.o libubiold.o libubiold_sysfs.o crc32.o
+ $(CC) $(LDFLAGS) -o $@ $^
+
+ubimirror: ubimirror.o error.o libubimirror.o bootenv.o hashmap.o \
+ libubiold.o libubiold_sysfs.o crc32.o
+ $(CC) $(LDFLAGS) -o $@ $^
+
+nand2bin: nand2bin.o nandecc.o nandcorr.o
+ $(CC) $(LDFLAGS) -o $@ $^
+
+bin2nand: bin2nand.o error.o nandecc.o
+ $(CC) $(LDFLAGS) -o $@ $^
+
+ubigen: ubigen.o libubigen.o crc32.o
+ $(CC) $(LDFLAGS) -o $@ $^
+
+mkbootenv: mkbootenv.o bootenv.o hashmap.o error.o crc32.o
+ $(CC) $(LDFLAGS) -o $@ $^
+
+unubi: unubi.o crc32.o unubi_analyze.o eb_chain.o
+ $(CC) $(LDFLAGS) -o $@ $^
+
+pfi2bin: pfi2bin.o peb.o error.o list.o crc32.o libubigen.o bootenv.o \
+ hashmap.o reader.o pfi.o
+ $(CC) $(LDFLAGS) -o $@ $^
+
+ubicrc32: ubicrc32.o crc32.o
+ $(CC) $(LDFLAGS) -o $@ $^
+
+install: ${TARGETS}
+ mkdir -p ${DESTDIR}/${SBINDIR}
+ install -m0755 ${TARGETS} ${DESTDIR}/${SBINDIR}/
+ (cd perl && install ${PERLPROGS} ${DESTDIR}/${SBINDIR}/)
+
+uninstall:
+ for file in ${TARGETS} ${PERLPROGS}; do \
+ $(RM) ${DESTDIR}/${SBINDIR}/$$file; \
+ done
diff --git a/ubi-utils/README b/ubi-utils/README
new file mode 100644
index 0000000..d976a76
--- /dev/null
+++ b/ubi-utils/README
@@ -0,0 +1,236 @@
+README
+======
+
+The programs and libraries in this directory provide a tool-chain to
+generate binary data for embedded systems which can be flashed either
+by a hardware flash programmer, e.g. JTAG debugger, or on the target
+system directly using pfiflash, or ubimkvol, ubirmvol, ubiwritevol.
+
+The latter is the case when there is already Linux running which has
+build in UBI support.
+
+Authors: Oliver Lohmann
+ Frank Haverkamp
+ Andreas Arnez
+
+mkpfi - tool for flash content generation in PFI
+ format
+pfi2bin - conversion tool to transfer a PFI file into a
+ binary image
+pfiflash - tool to update the embedded systems flash using
+ pfi files created by mkpfi
+libbootenv - library for boot-parameter processing
+libpfi - library for partial flash image (PFI) creation
+ and handling
+ubigen - tool to create binary UBI images e.g. for a
+ jtag flashing tool
+nandimg - tool to add OOB data to binary images intended
+ for NAND flash systems
+ubilib - UBI library
+
+!!! NOTICE !!!
+If you execute ./configure in the top_level directory the helper Makefile
+gets overwritten. Thats actually no problem, but be aware of that.
+
+1. Build Process
+
+1.1 Build, install and forget
+ o Build all and everything
+ $make all (takes a while, builds ppc and x86 binaries/libs)
+ o Installation:
+ $make install
+ o Uninstallation:
+ $make uninstall
+
+ o x86 only would be:
+ $make x86 && make install_x86
+
+1.2 Usage for a developer
+
+ 1.2.1 The build process in detail
+
+ o If you've checked out the sources from the CVS repository you'll find a
+ directory setup like this:
+
+ flashutils/
+ -rw-r--r-- 1 olli olli 1.3K Mar 14 11:53 Makefile
+ -rw-r--r-- 1 olli olli 1.9K Mar 14 10:50 Makefile.am
+ -rwxr-xr-x 1 olli olli 265 Mar 9 00:47 bootstrap
+ -rw-r--r-- 1 olli olli 1.1K Mar 9 16:55 configure.ac
+ drwxr-xr-x 2 olli olli 4.0K Mar 9 00:28 doc
+ drwxr-xr-x 2 olli olli 4.0K Mar 14 11:56 inc
+ drwxr-xr-x 2 olli olli 4.0K Mar 14 11:56 lib
+ drwxr-xr-x 17 olli olli 4.0K Mar 13 16:50 src
+
+ o To generate the initial build templates you have to call the bootstrap
+ script:
+ $ ./bootstrap
+ o Create a directory for the target platform
+ $ mkdir build_x86
+ o Descend into the directory and call the top-level configure script
+ with the desired options.
+ $ cd build_x86
+ $ ../configure --prefix=/usr/local [...]
+ o Now you'll find a directory structure like this:
+
+ flashutils/build_x86/
+ -rw-r--r-- 1 olli olli 47K Mar 14 13:33 Makefile
+ -rw-r--r-- 1 olli olli 33K Mar 14 13:33 config.log
+ -rwxr-xr-x 1 olli olli 38K Mar 14 13:33 config.status
+ drwxr-xr-x 2 olli olli 4.0K Mar 14 13:33 inc
+ drwxr-xr-x 3 olli olli 4.0K Mar 14 13:33 lib
+ -rwxr-xr-x 1 olli olli 202K Mar 14 13:33 libtool
+
+ o The config.guess script can be used to update the Makefiles in the
+ target directory after a change of the top-level template files
+ (i.e. the Makefile.in files).
+ $ ./config.guess
+ o To compile everything for this platform just invoke make in
+ flashutils/build_x86:
+ $ make
+ or from toplevel:
+ $ make -C ./build_x86
+ o The build process creates a new directory "bin":
+ flashutils/build_x86/
+ [...]
+ drwxr-xr-x 3 olli olli 4.0K Mar 14 13:41 bin
+ [...]
+
+ This directory contains all binary files which will be installed
+ by make install, e.g.:
+
+ flashutils/build_x86/bin/
+ -rwxr-xr-x 1 olli olli 7.2K Mar 14 13:41 bin2nand
+ -rwxr-xr-x 1 olli olli 15K Mar 14 13:41 mkbootenv
+ -rwxr-xr-x 1 olli olli 16K Mar 14 13:41 pddcustomize
+ -rwxr-xr-x 1 olli olli 36K Mar 14 13:41 pfi2bin
+ -rwxr-xr-x 1 olli olli 6.8K Mar 14 13:41 pfiflash
+ -rwxr-xr-x 1 olli olli 5.0K Mar 14 13:41 ubicrc32
+ -rwxr-xr-x 1 olli olli 13K Mar 14 13:41 ubigen
+ -rwxr-xr-x 1 olli olli 6.3K Mar 14 13:41 ubimirror
+
+
+ 1.2.2 Modifying and Adding Sources
+
+ o There is a dedicated directory which contains all source code
+ of the flashutils package, e.g.:
+
+ flashutils/src/
+ drwxr-xr-x 2 olli olli 4.0K Mar 13 11:42 libbootenv
+ drwxr-xr-x 2 olli olli 4.0K Mar 13 11:42 liberror
+ drwxr-xr-x 2 olli olli 4.0K Mar 13 16:48 mkpfi
+ drwxr-xr-x 2 olli olli 4.0K Mar 13 16:12 pddcustomize
+
+
+
+ The prefix "lib" is used to mark directories as part of a convenience
+ library. Binaries have no special prefix.
+
+ o How to add sources?
+
+ Just create a new directory at flashutils/src/, e.g.:
+
+ For a binary:
+ $ mkdir rider
+ $ cd rider
+ $ vi rider.c
+ /* do sth with that file... */
+
+ For a convenience library (as well as for "normal libs")
+ $ mkdir libworld
+ $ cd libworld
+ $ vi world.c
+ /* do sth with that file... */
+
+ o How to register sources in the build process (for binaries)?
+
+ You have to register your sources at the top-level automake Makefile:
+
+ In directory flashutils/
+ $ vi Makefile.am
+
+ Binaries have to be registered at "bin_PROGRAMS", e.g.:
+ bin_PROGRAMS = bin/pddcustomize \
+ bin/rider
+
+ Add the rule how the binary is assembled, e.g.:
+ bin_pddcustomize_SOURCES = \
+ $(top_srcdir)/src/pddcustomize/pddcustomize.c
+ bin_pddcustomize_LDADD = \
+ $(top_builddir)/lib/libbootenv.la \
+ $(top_builddir)/lib/liberror.la
+
+ bin_rider_SOURCES = \
+ $(top_srcdir)/src/rider/rider.c
+
+ This example reflects a simple build process for "rider". "rider"
+ is built without any other dependencies or convenience libraries.
+ The example for pddcustomize is a bit more complicated.
+ "_LDADD" adds some convenience libraris into the link process of
+ "pddcustomize". Imagine, that your "rider" has common code
+ with "dragon_bin" which is held in a library called "libworld".
+ The build rules would like like the following:
+
+ bin_rider_SOURCES = \
+ $(top_srcdir)/src/rider/rider.c
+ bin_rider_LDADD = \
+ $(top_builddir)/lib/libworld.la
+
+ bin_dragon_SOURCES = \
+ $(top_srcdir)/src/dragon_bin/dragon_bin.c
+ bin_dragon_LDADD = \
+ $(top_builddir)/lib/libworld.la
+
+ Don't forget to add "dragon" to "bin_PROGRAMS"!
+ Don't forget to set the build rule for the "libworld" itself!
+ This is documented in the next section.
+
+
+ o How to register sources in the build process (for libraries)?
+
+ Until now we didn't care about the build process of "libworld".
+ Libraries are handled special in this build process because
+ they are handled as "modules", i.e. they are able to be built
+ without building the binaries in the same step. Additionally,
+ it is possible to assemble complex libraries out of simple ones.
+ That especially makes sense if you want to export (install) a
+ library on a system which uses some common code and makes
+ some adoptions for usability and presents a comfortable interface to
+ the user (see libpfiflash in the sources for an example).
+
+ o Registering "libworld" as convenience library.
+
+ Instead of editing the "Makefile.am" in "flashtools/", we have to
+ edit now the "Makefile.am" in "flashtools/lib/":
+
+ noinst_LTLIBRARIES = libworld.la
+
+ libworld_la_SOURCES = $(top_srcdir)/src/libworld/world.c
+
+ o Registering "libworld" as library which gets installed.
+
+ lib_LTLIBRARIES = libworld.la
+ libworld_la_SOURCES = $(top_srcdir)/src/libworld/world.c
+ libworld_la_LDFLAGS = -no-undefined -version-info 0:0:0
+
+ o Header files
+
+ All header files are stored at "flashutils/inc", regardless
+ if convenience library or not.
+
+ If you want to export headers you have to specify this in the Makefile.am
+ located at "flashutils/inc", e.g. (this should not be done
+ for convenience libraries):
+
+ nobase_include_HEADERS = world.h
+
+
+
+Appendix
+
+A.1. FAQ
+
+ Q How to call configure to setup a cross-platform build?
+ A $ ./configure --build=i686-pc-linux-gnu --host=ppc-linux \
+ --prefix=/opt/.../ppcnf/crossroot/ \
+ --exec-prefix=/opt/..../ppcnf/crossroot/usr
diff --git a/ubi-utils/UBI.TXT b/ubi-utils/UBI.TXT
new file mode 100644
index 0000000..9a1c3c7
--- /dev/null
+++ b/ubi-utils/UBI.TXT
@@ -0,0 +1,108 @@
+UBI - Unsorted Block Images
+
+UBI (Latin: "where?") manages multiple logical volumes on a single
+flash device, specifically supporting NAND flash devices. UBI provides
+a flexible partitioning concept which still allows for wear-levelling
+across the whole flash device.
+
+In a sense, UBI may be compared to the Logical Volume Manager
+(LVM). Whereas LVM maps logical sector numbers to physical HDD sector
+numbers, UBI maps logical eraseblocks to physical eraseblocks.
+
+More information may be found in the UBI design documentation:
+ubidesign.pdf. Which can be found here:
+http://www.linux-mtd.infradead.org/doc/ubi.html
+
+Partitioning/Re-partitioning
+
+ An UBI volume occupies a certain number of erase blocks. This is
+ limited by a configured maximum volume size, which could also be
+ viewed as the partition size. Each individual UBI volume's size can
+ be changed independently of the other UBI volumes, provided that the
+ sum of all volume sizes doesn't exceed a certain limit.
+
+ UBI supports dynamic volumes and static volumes. Static volumes are
+ read-only and their contents are protected by CRC check sums.
+
+Bad eraseblocks handling
+
+ UBI transparently handles bad eraseblocks. When a physical
+ eraseblock becomes bad, it is substituted by a good physical
+ eraseblock, and the user does not even notice this.
+
+Scrubbing
+
+ On a NAND flash bit flips can occur on any write operation,
+ sometimes also on read. If bit flips persist on the device, at first
+ they can still be corrected by ECC, but once they accumulate,
+ correction will become impossible. Thus it is best to actively scrub
+ the affected eraseblock, by first copying it to a free eraseblock
+ and then erasing the original. The UBI layer performs this type of
+ scrubbing under the covers, transparently to the UBI volume users.
+
+Erase Counts
+
+ UBI maintains an erase count header per eraseblock. This frees
+ higher-level layers (like file systems) from doing this and allows
+ for centralized erase count management instead. The erase counts are
+ used by the wear-levelling algorithm in the UBI layer. The algorithm
+ itself is exchangeable.
+
+Booting from NAND
+
+ For booting directly from NAND flash the hardware must at least be
+ capable of fetching and executing a small portion of the NAND
+ flash. Some NAND flash controllers have this kind of support. They
+ usually limit the window to a few kilobytes in erase block 0. This
+ "initial program loader" (IPL) must then contain sufficient logic to
+ load and execute the next boot phase.
+
+ Due to bad eraseblocks, which may be randomly scattered over the
+ flash device, it is problematic to store the "secondary program
+ loader" (SPL) statically. Also, due to bit-flips it may become
+ corrupted over time. UBI allows to solve this problem gracefully by
+ storing the SPL in a small static UBI volume.
+
+UBI volumes vs. static partitions
+
+ UBI volumes are still very similar to static MTD partitions:
+
+ * both consist of eraseblocks (logical eraseblocks in case of UBI
+ volumes, and physical eraseblocks in case of static partitions;
+ * both support three basic operations - read, write, erase.
+
+ But UBI volumes have the following advantages over traditional
+ static MTD partitions:
+
+ * there are no eraseblock wear-leveling constraints in case of UBI
+ volumes, so the user should not care about this;
+ * there are no bit-flips and bad eraseblocks in case of UBI volumes.
+
+ So, UBI volumes may be considered as flash devices with relaxed
+ restrictions.
+
+Where can it be found?
+
+ Documentation, kernel code and applications can be found in the MTD
+ gits.
+
+What are the applications for?
+
+ The applications help to create binary flash images for two
+ purposes: pfi files (partial flash images) for in-system update of
+ UBI volumes, and plain binary images, with or without OOB data in
+ case of NAND, for a manufacturing step. Furthermore some tools
+ are/and will be created that allow flash content analysis after a
+ system has crashed.
+
+Who did UBI?
+
+ The original ideas, where UBI is based on, were developed by Andreas
+ Arnez, Frank Haverkamp and Thomas Gleixner. Josh W. Boyer and
+ some others were involved too. The implementation of the kernel
+ layer was done by Artem B. Bityutskiy. The user-space applications
+ and tools were written by Oliver Lohmann with contributions from
+ Frank Haverkamp, Andreas Arnez, and Artem. Joern Engel contributed a
+ patch which modifies JFFS2 so that it can be run on a UBI
+ volume. Thomas Gleixner did modifications to the NAND layer and also
+ some to JFFS2 to make it work.
diff --git a/ubi-utils/doc/unubi.roff b/ubi-utils/doc/unubi.roff
new file mode 100644
index 0000000..6cebc46
--- /dev/null
+++ b/ubi-utils/doc/unubi.roff
@@ -0,0 +1,123 @@
+.TH UNUBI 1 "NOVEMBER 2006" FSP "FSP Flashutils"
+.SH NAME
+unubi \- extract volumes/eraseblocks from a raw\-UBI image
+.SH SYNOPSIS
+\fBunubi [\-aevEV] [\-d \fIout\-dir\fB] [\-r \fIvolume\-id\fB]
+[\-b \fIblock\-size\fB] \fIimage\-file
+.SH DESCRIPTION
+.PP
+\fBunubi\fR reads an image file containing blocks of UBI headers and data
+(such as produced from \fBnand2bin\fR) and rebuilds the volumes within.
+The default operation (when no flags are given) is to rebuild all valid
+volumes found in the image. \fBunubi\fR can also read straight from the
+onboard MTD device (ex. /dev/mtdblock/NAND).
+.SH OPTIONS
+.IP "\-a, \-\-analyze"
+When flagged, analysis files are generated within the output directory. These
+may include tables and or graphs detailing statistics gathered from the
+eraseblock data. Files are prefixed `analysis_'.
+
+See \fBANALYSIS\fR.
+.IP "\-b, \-\-blocksize \fIblock\-size\fR"
+Specify in bytes the \fIimage\-file\fR eraseblock size. Sizes may be
+postfixed with `KiB' or `MiB' to indicate mebibytes or kibibytes
+respectively. Default is 128KiB.
+.IP "\-d, \-\-dir \fIoutput\-dir\fR"
+Specify the output directory. If no directory is specified, the default
+is `unubi_\fIimage\-file\fR' within the curent working directory. If the
+attempt to create the output directory fails,
+.B unubi
+will try to create it in /tmp before aborting.
+.IP "\-e, \-\-eb\-split"
+When flagged, images are created for each eraseblock in \fIimage\-file\fR
+regardless of its validity. Each image is the complete eraseblock, including
+headers and any space to the end of the eraseblock after where the data may
+end.
+
+Invalid images are named `ebEEEE', where EEEE is the physical index of the
+eraseblock in the image. Valid images are named `ebEEEE_VVV_NNN_RRR' where
+VVV is the known volume ID, NNN is the logical number and RRR is the version
+of the eraseblock data. Note that the version number is in hexadecimal.
+
+Invalid images may also contain this postfix, if the data in the header
+could be valid (ie. the header contains a resonable volume ID, but the
+header and/or data CRCs are not valid). If this is the case, images are named
+`ebEEEE_VVV_NNN_RRR.reason', so as to distinguish known values from
+non\-definite ones.
+
+See \fBREASON SUFFIXES\fR.
+.IP "\-r, \-\-rebuild \fIvolume\-id\fR"
+Specify a volume to rebuild. Can be used successively to specify
+several volumes to be rebuilt.
+
+Images are named `volumeVVV' where VVV is the volume ID. For each missing
+eraseblock, an error message will be printed.
+.IP "\-v, \-\-vol\-split"
+When flagged, images are created for each valid eraseblock in
+\fIimage\-file\fR. Since a vaild eraseblock will have a defined data start and
+data length, only this range will make up the image.
+
+Images are named `volVVV_NNN_RRR_EEEE', where, for the data in the eraseblock,
+VVV is the volume ID, NNN is the logical number, RRR is the version and EEEE
+is the phyisical index of the eraseblock in the image.
+.IP "\-V, \-\-vol\-split!"
+Same as above, only all images are the complete eraseblock (including headers,
+and raw data, even past the point where the data is supposed to end).
+Overrides \-v when both \-v and \-V are flagged.
+.SH ANALYSIS
+The following files will be generated during the analysis:
+.IP "analysis_ec_hdr.data"
+A space delimited table with these two columns for each eraseblock: the
+eraseblock's index or physical position in the image, and the eraseblock's
+erase count. The third column contains the erase count data sorted.
+.IP "analysis_vid_hdr.data"
+A space delimited table with these four colums for each eraseblock: the
+volume ID, the volume logical number, the leb version, and the data size.
+In addition there are a normalized column representing the volume ID and
+volume logical number, a normalized column representing the leb version, and
+a normalized column representing the data_size. These normalized columns are
+used to better draw the the gnuplot image.
+.IP "analysis_ec_hdr.plot"
+A gnuplot script for quickly viewing a sample output from the respective .data
+file.
+.IP "analysis_vid_hdr.plot"
+A gnuplot script for quickly viewing a sample output from the respective .data
+file.
+.SH REASONS SUFFIXES
+When \-\-eb\-split produces possibly invalid, though usable, eraseblocks, the
+known reason suffixes are:
+.IP ".ec_magic"
+The erase counter header did not contain a valid magic field.
+.IP ".ec_hdr_crc"
+The erase counter header did not contain a vaild header CRC field.
+.IP ".vid_magic"
+The volume ID header did not contain a valid magic field.
+.IP ".vid_hdr_crc"
+The volume ID header did not contain a valid header CRC field.
+.IP ".data_crc"
+The volume ID header did not contain a valid data CRC field.
+.SH EXAMPLES
+To extract and rebuild all valid volumes from demo.img (note the output
+directory will be /home/user/unubi_demo.img):
+.sp 1
+.RS
+.B /home/user# unubi demo.img
+.sp 1
+.RE
+To analyze demo.img as well as extract and rebuild volume 7:
+.sp 1
+.RS
+.B /home/user# unubi \-a \-r 7 demo.img
+.sp 1
+.RE
+To split demo.img into raw images for each eraseblock into the folder
+/var/eraseblocks:
+.sp 1
+.RS
+.B /home/user# unubi \-e \-d /var/eraseblocks demo.img
+.SH AUTHORS
+Frank Haverkamp <haver@vnet.ibm.com>
+.sp 0
+Drake Dowsett <dowsett@de.ibm.com>
+.SH CONTACT
+Andreas Arnez <arnez@de.ibm.com>
diff --git a/ubi-utils/inc/libubi.h b/ubi-utils/inc/libubi.h
new file mode 100644
index 0000000..d39c1b9
--- /dev/null
+++ b/ubi-utils/inc/libubi.h
@@ -0,0 +1,268 @@
+/*
+ * 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.
+ *
+ * Author: Artem B. Bityutskiy
+ *
+ * UBI (Unsorted Block Images) library.
+ */
+
+#ifndef __LIBUBI_H__
+#define __LIBUBI_H__
+
+#include <stdint.h>
+#include <mtd/ubi-user.h>
+#include <ctype.h>
+#include <mtd/ubi-header.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* UBI version libubi is made for */
+#define LIBUBI_UBI_VERSION 1
+
+/* UBI library descriptor */
+typedef void * libubi_t;
+
+/**
+ * struct ubi_mkvol_request - volume creation request.
+ * */
+struct ubi_mkvol_request
+{
+ int vol_id;
+ int alignment;
+ long long bytes;
+ int vol_type;
+ const char *name;
+};
+
+/**
+ * struct ubi_info - general UBI information.
+ *
+ * @dev_count count of UBI devices in system
+ * @lowest_dev_num lowest UBI device number
+ * @highest_dev_num highest UBI device number
+ * @version UBI version
+ */
+struct ubi_info
+{
+ int dev_count;
+ int lowest_dev_num;
+ int highest_dev_num;
+ int version;
+};
+
+/**
+ * struct ubi_dev_info - UBI device information.
+ *
+ * @vol_count count of volumes on this UBI device
+ * @lowest_vol_num lowest volume number
+ * @highest_vol_num highest volume number
+ * @total_ebs total number of eraseblocks on this UBI device
+ * @avail_ebs how many eraseblocks are not used and available for new
+ * volumes
+ * @total_bytes @total_ebs * @eb_size
+ * @avail_bytes @avail_ebs * @eb_size
+ * @bad_count count of bad eraseblocks
+ * @eb_size size of UBI eraseblock
+ * @max_ec current highest erase counter value
+ * @bad_rsvd how many physical eraseblocks of the underlying flash
+ * device are reserved for bad eraseblocks handling
+ * @max_vol_count maximum count of volumes on this UBI device
+ * @min_io_size minimum input/output size of the UBI device
+ */
+struct ubi_dev_info
+{
+ int dev_num;
+ int vol_count;
+ int lowest_vol_num;
+ int highest_vol_num;
+ int total_ebs;
+ int avail_ebs;
+ long long total_bytes;
+ long long avail_bytes;
+ int bad_count;
+ int eb_size;
+ long long max_ec;
+ int bad_rsvd;
+ int max_vol_count;
+ int min_io_size;
+};
+
+/**
+ * struct ubi_vol_info - UBI volume information.
+ *
+ * @dev_num UBI device number the volume resides on
+ * @vol_id ID of this volume
+ * @type volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME)
+ * @alignment alignemnt of this volume
+ * @data_bytes how many data bytes are stored on this volume (equivalent to
+ * @rsvd_bytes for dynamic volumes)
+ * @rsvd_bytes how many bytes are reserved for this volume
+ * @rsvd_ebs how many eraseblocks are reserved for this volume
+ * @eb_size logical eraseblock size of this volume (may be less then
+ * device's logical eraseblock size due to alignment)
+ * @corrupted the volume is corrupted if this flag is not zero
+ * @name volume name (null-terminated)
+ */
+struct ubi_vol_info
+{
+ int dev_num;
+ int vol_id;
+ int type;
+ int alignment;
+ long long data_bytes;
+ long long rsvd_bytes;
+ int rsvd_ebs;
+ int eb_size;
+ int corrupted;
+ char name[UBI_VOL_NAME_MAX + 1];
+};
+
+/**
+ * libubi_open - open UBI library.
+ *
+ * This function initializes and opens the UBI library and returns UBI library
+ * descriptor in case of success and %NULL in case of failure.
+ */
+libubi_t libubi_open(void);
+
+/**
+ * libubi_close - close UBI library
+ *
+ * @desc UBI library descriptor
+ */
+void libubi_close(libubi_t desc);
+
+/**
+ * ubi_get_info - get general UBI information.
+ *
+ * @info pointer to the &struct ubi_info object to fill
+ * @desc UBI library descriptor
+ *
+ * This function fills the passed @info object with general UBI information and
+ * returns %0 in case of success and %-1 in case of failure.
+ */
+int ubi_get_info(libubi_t desc, struct ubi_info *info);
+
+/**
+ * ubi_mkvol - create an UBI volume.
+ *
+ * @desc UBI library descriptor
+ * @node name of the UBI character device to create a volume at
+ * @req UBI volume creation request (defined at <mtd/ubi-user.h>)
+ *
+ * This function creates a UBI volume as described at @req and returns %0 in
+ * case of success and %-1 in case of failure. The assigned volume ID is
+ * returned in @req->vol_id.
+ */
+int ubi_mkvol(libubi_t desc, const char *node, struct ubi_mkvol_request *req);
+
+/**
+ * ubi_rmvol - remove a UBI volume.
+ *
+ * @desc UBI library descriptor
+ * @node name of the UBI character device to remove a volume from
+ * @vol_id ID of the volume to remove
+ *
+ * This function removes volume @vol_id from UBI device @node and returns %0 in
+ * case of success and %-1 in case of failure.
+ */
+int ubi_rmvol(libubi_t desc, const char *node, int vol_id);
+
+/**
+ * ubi_rsvol - re-size UBI volume.
+ *
+ * @desc UBI library descriptor
+ * @node name of the UBI character device owning the volume which should be
+ * re-sized
+ * @vol_id volume ID to re-size
+ * @bytes new volume size in bytes
+ *
+ * This function returns %0 in case of success and %-1 in case of error.
+ */
+int ubi_rsvol(libubi_t desc, const char *node, int vol_id, long long bytes);
+
+/**
+ * ubi_get_dev_info - get UBI device information.
+ *
+ * @desc UBI library descriptor
+ * @node name of the UBI character device to fetch information about
+ * @info pointer to the &struct ubi_dev_info object to fill
+ *
+ * This function fills the passed @info object with UBI device information and
+ * returns %0 in case of success and %-1 in case of failure.
+ */
+int ubi_get_dev_info(libubi_t desc, const char *node,
+ struct ubi_dev_info *info);
+
+/**
+ * ubi_get_dev_info1 - get UBI device information.
+ *
+ * @desc UBI library descriptor
+ * @dev_num UBI device number to fetch information about
+ * @info pointer to the &struct ubi_dev_info object to fill
+ *
+ * This function is identical to 'ubi_get_dev_info()' except that it accepts UBI
+ * device number, not UBI character device.
+ */
+int ubi_get_dev_info1(libubi_t desc, int dev_num, struct ubi_dev_info *info);
+
+/**
+ * ubi_get_vol_info - get UBI volume information.
+ *
+ * @desc UBI library descriptor
+ * @node name of the UBI volume character device to fetch information about
+ * @info pointer to the &struct ubi_vol_info object to fill
+ *
+ * This function fills the passed @info object with UBI volume information and
+ * returns %0 in case of success and %-1 in case of failure.
+ */
+int ubi_get_vol_info(libubi_t desc, const char *node,
+ struct ubi_vol_info *info);
+
+/**
+ * ubi_get_vol_info1 - get UBI volume information.
+ *
+ * @desc UBI library descriptor
+ * @dev_num UBI device number
+ * @vol_id ID of the UBI volume to fetch information about
+ * @info pointer to the &struct ubi_vol_info object to fill
+ *
+ * This function is identical to 'ubi_get_vol_info()' except that it accepts UBI
+ * volume number, not UBI volume character device.
+ */
+int ubi_get_vol_info1(libubi_t desc, int dev_num, int vol_id,
+ struct ubi_vol_info *info);
+
+/**
+ * ubi_update_start - start UBI volume update.
+ *
+ * @desc UBI library descriptor
+ * @fd volume character devie file descriptor
+ * @bytes how many bytes will be written to the volume
+ *
+ * This function initiates UBI volume update and returns %0 in case of success
+ * and %-1 in case of error.
+ */
+int ubi_update_start(libubi_t desc, int fd, long long bytes);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !__LIBUBI_H__ */
diff --git a/ubi-utils/inc/libubiold.h b/ubi-utils/inc/libubiold.h
new file mode 100644
index 0000000..f13d812
--- /dev/null
+++ b/ubi-utils/inc/libubiold.h
@@ -0,0 +1,310 @@
+#ifndef __UBI_H__
+#define __UBI_H__
+/*
+ * 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.
+ */
+
+/*
+ * UBI (Unsorted Block Images) library.
+ * @file libubi.h
+ * @author Artem B. Bityutskiy
+ * @author Additions: Oliver Lohmann
+ * @version 1.0
+ */
+
+#include <stdint.h>
+#include <mtd/ubi-user.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @section eh Error Handling
+ * The following error indication policy is used: in case of success, all
+ * library functions return 0, in case of failure they either return UBI error
+ * codes, or -1 if a system error occured; in the latter case the exact error
+ * code has to be in the errno variable.
+ *
+ * @def UBI_ENOTFOUND
+ * @brief UBI was not found in the system.
+ * @def UBI_EBUG
+ * @brief An error due to bug in kernel part of UBI in UBI library.
+ * @def UBI_EINVAL
+ * @brief Invalid argument.
+ * @def UBI_EMACS
+ * @brief Highest error value.
+ */
+#define UBI_ENOTFOUND 1
+#define UBI_EBUG 2
+#define UBI_EINVAL 3
+#define UBI_EMAX 4
+
+
+/**
+ * UBI library descriptor, vague for library users.
+ */
+typedef struct ubi_lib *ubi_lib_t;
+
+/**
+ * struct ubi_info - general information about UBI.
+ *
+ * @version UBI version
+ * @nlen_max maximum length of names of volumes
+ * @dev_count count UBI devices in the system
+ */
+struct ubi_info
+{
+ unsigned int version;
+ unsigned int nlen_max;
+ unsigned int dev_count;
+};
+
+/**
+ * struct ubi_dev_info - information about an UBI device
+ *
+ * @wear average number of erasures of flash erasable blocks
+ * @major major number of the corresponding character device
+ * @minor minor number of the corresponding character device
+ * @eb_size size of eraseblocks
+ * @total_ebs total count of eraseblocks
+ * @avail_ebs count of unused eraseblock available for new volumes
+ * @vol_count total count of volumes in this UBI device
+ */
+struct ubi_dev_info
+{
+ unsigned long long wear;
+ unsigned int major;
+ unsigned int minor;
+ unsigned int eb_size;
+ unsigned int total_ebs;
+ unsigned int avail_ebs;
+ unsigned int vol_count;
+};
+
+/**
+ * struct ubi_vol_info - information about an UBI volume
+ *
+ * @bytes volume size in bytes
+ * @eraseblocks volume size in eraseblocks
+ * @major major number of the corresponding character device
+ * @minor minor number of the corresponding character device
+ * @type volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME)
+ * @dev_path device path to volume
+ * @name volume name
+ */
+struct ubi_vol_info
+{
+ unsigned long long bytes;
+ unsigned int eraseblocks;
+ unsigned int major;
+ unsigned int minor;
+ int type;
+ char *dev_path;
+ char *name;
+};
+
+/**
+ * ubi_mkvol - create a dynamic UBI volume.
+ *
+ * @desc UBI library descriptor
+ * @devn Number of UBI device to create new volume on
+ * @vol_id volume ID to assign to the new volume
+ * @vol_type volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME)
+ * @bytes volume size in bytes
+ * @alignment volume alignment
+ * @name volume name
+ *
+ * This function creates new UBI volume. If @vol_id is %UBI_VOLN_AUTO, then
+ * volume number is assigned automatically. This function returns positive
+ * volume number of the new volume in case of success or %-1 in case of
+ * failure.
+ */
+int ubi_mkvol(ubi_lib_t desc, int devn, int vol_id, int vol_type,
+ long long bytes, int alignment, const char *name);
+
+/**
+ * ubi_rmvol - remove a volume.
+ *
+ * @desc UBI library descriptor
+ * @devn Number of UBI device to remove volume from
+ * @vol_id volume ID to remove
+ *
+ * This function returns zero in case of success or %-1 in case of failure.
+ */
+int ubi_rmvol(ubi_lib_t desc, int devn, int vol_id);
+
+/**
+ * ubi_get_info - get UBI information.
+ *
+ * @desc UBI library descriptor
+ * @ubi UBI information is returned here
+ *
+ * This function retrieves information about UBI and puts it to @ubi. Returns
+ * zero in case of success and %-1 in case of failure.
+ */
+int ubi_get_info(ubi_lib_t desc, struct ubi_info *ubi);
+
+/**
+ * ubi_vol_open - open a UBI volume
+ *
+ * @desc UBI library descriptor
+ * @devn Number of UBI device on which to open the volume
+ * @vol_id Number of UBI device on which to open the volume
+ * @flags Flags to pass to open()
+ *
+ * This function opens a UBI volume on a given UBI device. It returns
+ * the file descriptor of the opened volume device. In case of an
+ * error %-1 is returned and errno is set appropriately.
+ */
+int ubi_vol_open(ubi_lib_t desc, int devn, int vol_id, int flags);
+
+/**
+ * ubi_vol_close - close a UBI volume
+ *
+ * @vol_fd file descriptor of UBI volume to close
+ *
+ * This function closes the given UBI device.
+ */
+int ubi_vol_close(int vol_fd);
+
+/**
+ * ubi_vol_update - initiate volume update on a UBI volume
+ * @vol_fd File descriptor of UBI volume to update
+ * @bytes No. of bytes which shall be written.
+ *
+ * Initiates a volume update on a given volume. The caller must then
+ * actually write the appropriate number of bytes to the volume by
+ * calling write(). Returns 0 on success, else error.
+ */
+int ubi_vol_update(int vol_fd, unsigned long long bytes);
+
+/**
+ * ubi_vol_fopen_read - open a volume for reading, returning a FILE *
+ * @desc UBI library descriptor
+ * @devn UBI device number
+ * @vol_id volume ID to read
+ *
+ * Opens a volume for reading. Reading itself can then be performed
+ * with fread(). The stream can be closed with fclose(). Returns a
+ * stream on success, else NULL.
+ */
+FILE *
+ubi_vol_fopen_read(ubi_lib_t desc, int devn, uint32_t vol_id);
+
+/**
+ * ubi_vol_fopen_update - open a volume for writing, returning a FILE *
+ * @desc UBI library descriptor
+ * @devn UBI device number
+ * @vol_id volume ID to update
+ * @bytes No. of bytes which shall be written.
+ *
+ * Initiates a volume update on a given volume. The caller must then
+ * actually write the appropriate number of bytes to the volume by
+ * calling fwrite(). The file can be closed with fclose(). Returns a
+ * stream on success, else NULL.
+ */
+FILE *
+ubi_vol_fopen_update(ubi_lib_t desc, int devn, uint32_t vol_id,
+ unsigned long long bytes);
+
+/**
+ * ubi_vol_get_used_bytes - determine used bytes in a UBI volume
+ * @vol_fd File descriptor of UBI volume
+ * @bytes Pointer to result
+ *
+ * Returns 0 on success, else error.
+ */
+int ubi_vol_get_used_bytes(int vol_fd, unsigned long long *bytes);
+
+/**
+ * ubi_open - open UBI library.
+ *
+ * @desc A pointer to an UBI library descriptor
+ *
+ * Returns zero in case of success.
+ */
+int ubi_open(ubi_lib_t *desc);
+
+/**
+ * ubi_close - close UBI library.
+ *
+ * @desc A pointer to an UBI library descriptor
+ */
+int ubi_close(ubi_lib_t *desc);
+
+
+/**
+ * ubi_perror - print UBI error.
+ *
+ * @prefix a prefix string to prepend to the error message
+ * @code error code
+ *
+ * If @code is %-1, this function calls 'perror()'
+ */
+void ubi_perror(const char *prefix, int code);
+
+/**
+ * ubi_set_cdev_pattern - set 'sprintf()'-like pattern of paths to UBI
+ * character devices.
+ *
+ * @desc UBI library descriptor
+ * @pattern the pattern to set
+ *
+ * The default UBI character device path is "/dev/ubi%u".
+ */
+int ubi_set_cdev_pattern(ubi_lib_t desc, const char *pattern);
+
+/**
+ * ubi_get_dev_info get information about an UBI device.
+ *
+ * @desc UBI library descriptor
+ * @devn UBI device number
+ * @di the requested information is returned here
+ */
+int ubi_get_dev_info(ubi_lib_t desc, unsigned int devn,
+ struct ubi_dev_info *di);
+
+/**
+ * ubi_set_vol_cdev_pattern - set 'sprintf()'-like pattµern ofpaths to UBI
+ * volume character devices.
+ *
+ * @desc UBI library descriptor
+ * @pattern the pattern to set
+ *
+ * The default UBI character device path is "/dev/ubi%u_%u".
+ */
+int ubi_set_vol_cdev_pattern(ubi_lib_t desc, const char *pattern);
+
+/**
+ * ubi_get_vol_info - get information about an UBI volume
+ *
+ * @desc UBI library descriptor
+ * @devn UBI device number the volume belongs to
+ * @vol_id the requested volume number
+ * @vi volume information is returned here
+ *
+ * Users must free the volume name string @vi->name.
+ */
+int ubi_get_vol_info(ubi_lib_t desc, unsigned int devn, unsigned int vol_id,
+ struct ubi_vol_info *vi);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !__UBI_H__ */
diff --git a/ubi-utils/lib/Makefile.am b/ubi-utils/lib/Makefile.am
new file mode 100644
index 0000000..1b0dc01
--- /dev/null
+++ b/ubi-utils/lib/Makefile.am
@@ -0,0 +1,58 @@
+AUTOMAKE_OPTIONS = foreign
+INCLUDES=-I$(top_srcdir)/inc -I$(top_srcdir)/../../kernel/include
+
+# -----------------------------------------------------------------------------
+# all export libs which shall be generated
+lib_LTLIBRARIES = libubi.la \
+ libpfiflash.la
+
+# -----------------------------------------------------------------------------
+# all convinence libs which shall be generated
+noinst_LTLIBRARIES = libcrc32.la \
+ libubigen.la \
+ liberror.la \
+ liblist.la \
+ libbootenv.la \
+ libpfi.la \
+ libpeb.la \
+ libreader.la \
+ libubimirror.la
+
+# -----------------------------------------------------------------------------
+# exported libs
+libpfiflash_la_SOURCES = $(top_srcdir)/src/libpfiflash/pfiflash.c
+libpfiflash_la_LDFLAGS = -no-undefined -version-info 1:0:0
+libpfiflash_la_LIBADD = libreader.la \
+ libubimirror.la \
+ libubi.la
+
+libubi_la_SOURCES = $(top_srcdir)/src/libubi/libubi.c \
+ $(top_srcdir)/src/libubi/libubi_sysfs.c
+libubi_la_LDFLAGS = -no-undefined -version-info 1:0:0
+
+# -----------------------------------------------------------------------------
+# complex convinence libs, beware for double includes.
+libreader_la_SOURCES = $(top_srcdir)/src/libreader/reader.c
+libreader_la_LIBADD = libpfi.la \
+ liblist.la \
+ libpeb.la \
+ libbootenv.la
+
+libubigen_la_SOURCES = $(top_srcdir)/src/libubigen/ubigen.c
+libubigen_la_LIBADD = libcrc32.la
+
+libbootenv_la_SOURCES = $(top_srcdir)/src/libbootenv/bootenv.c \
+ $(top_srcdir)/src/libbootenv/hashmap.c
+libbootenv_la_LIBADD = libcrc32.la
+
+libubimirror_la_SOURCES = $(top_srcdir)/src/libubimirror/ubimirror.c
+libubimirror_la_LIBADD = libubi.la
+
+
+# -----------------------------------------------------------------------------
+# simple convinence libs
+libcrc32_la_SOURCES = $(top_srcdir)/src/libcrc32/crc32.c
+liberror_la_SOURCES = $(top_srcdir)/src/liberror/error.c
+liblist_la_SOURCES = $(top_srcdir)/src/liblist/list.c
+libpeb_la_SOURCES = $(top_srcdir)/src/libpeb/peb.c
+libpfi_la_SOURCES = $(top_srcdir)/src/libpfi/pfi.c
diff --git a/ubi-utils/perl/f128_nand_sample.cfg b/ubi-utils/perl/f128_nand_sample.cfg
new file mode 100644
index 0000000..e468d9d
--- /dev/null
+++ b/ubi-utils/perl/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/perl/f64_nor_sample.cfg b/ubi-utils/perl/f64_nor_sample.cfg
new file mode 100644
index 0000000..fd44e27
--- /dev/null
+++ b/ubi-utils/perl/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/perl/mkpfi b/ubi-utils/perl/mkpfi
new file mode 100755
index 0000000..2cce587
--- /dev/null
+++ b/ubi-utils/perl/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);
+ &copy_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
diff --git a/ubi-utils/perl/ubicrc32.pl b/ubi-utils/perl/ubicrc32.pl
new file mode 100755
index 0000000..add5f9d
--- /dev/null
+++ b/ubi-utils/perl/ubicrc32.pl
@@ -0,0 +1,74 @@
+#!/usr/bin/perl -w
+
+# Subroutine crc32(): Calculates the CRC on a given string.
+
+{
+ my @table = ();
+
+ # @brief Calculate CRC32 for a given string.
+ sub crc32
+ {
+ unless (@table) {
+ # Initialize the CRC table
+ my $poly = 0xEDB88320;
+ @table = ();
+
+ for my $i (0..255) {
+ my $c = $i;
+
+ for my $j (0..7) {
+ $c = ($c & 1) ? (($c >> 1) ^ $poly) : ($c >> 1);
+ }
+ $table[$i] = $c;
+ }
+ }
+ my $s = shift; # string to calculate the CRC for
+ my $crc = shift; # CRC start value
+
+ defined($crc)
+ or $crc = 0xffffffff; # Default CRC start value
+
+ for (my $i = 0; $i < length($s); $i++) {
+ $crc = $table[($crc ^ ord(substr($s, $i, 1))) & 0xff]
+ ^ ($crc >> 8);
+ }
+ return $crc;
+ }
+}
+
+sub crc32_on_file
+{
+ my $file = shift;
+
+ my $crc32 = crc32('');
+ my $buf = '';
+ my $ret = 0;
+
+ while ($ret = read($file, $buf, 8192)) {
+ $crc32 = crc32($buf, $crc32);
+ }
+ defined($ret)
+ or return undef;
+ printf("0x%x\n", $crc32);
+}
+
+
+# Main routine: Calculate the CRCs on the given files and print the
+# results.
+
+{
+ if (@ARGV) {
+ while (my $path = shift) {
+ my $file;
+ open $file, "<", $path
+ or die "Error opening '$path'.\n";
+
+ &crc32_on_file($file)
+ or die "Error reading from '$path'.\n";
+ close $file;
+ }
+ } else {
+ &crc32_on_file(\*STDIN)
+ or die "Error reading from stdin.\n";
+ }
+}
diff --git a/ubi-utils/scripts/Makefile b/ubi-utils/scripts/Makefile
new file mode 100644
index 0000000..ebd9bc6
--- /dev/null
+++ b/ubi-utils/scripts/Makefile
@@ -0,0 +1,75 @@
+#
+# Makefile
+#
+# Testcase for UBI pfi update.
+#
+# Author: Frank Haverkamp <haverkam@de.ibm.com>
+#
+
+card = test
+mkpfi_cfg = test.cfg
+
+#
+# Some default values you might want to overwrite. Try it if you need
+# it and add more if needed. Note that no real sanity checking is done
+# on those values. If you do it wrong your card has no valid PDD data.
+#
+
+PATH := $(PATH):/opt/ppc/usr/bin:../perl:..
+
+dd = dd
+sed = sed
+bin2nand = bin2nand
+ubigen = ubigen
+mkpfi = mkpfi -v
+pfi2bin = pfi2bin -v
+
+vmlinux_bin ?= test_vmlinux.bin
+rootfs_bin ?= test_rootfs.bin
+spl_bin ?= test_u-boot.bin
+pdd_txt ?= pdd.txt
+
+flashtype ?= nand
+pagesize ?= 2048
+
+compl ?= $(card)_complete
+compl_pfi ?= $(compl).pfi
+compl_img ?= $(compl).img
+
+compl_nand2048_mif=$(compl).$(flashtype)$(pagesize).mif
+compl_nand2048_img=$(compl).$(flashtype)$(pagesize).img
+
+all: $(compl_pfi) $(compl_nand2048_mif)
+
+$(compl_pfi): $(vmlinux_bin) $(rootfs_bin) $(spl_bin)
+ $(mkpfi) -c $(mkpfi_cfg)
+
+# Binary data and out of band data (OOB)
+#
+$(compl_nand2048_mif): $(compl_img)
+ $(bin2nand) -p $(pagesize) -o $(compl_nand2048_mif) $<
+
+# Binary data only
+#
+$(compl_img): $(compl_pfi)
+ $(pfi2bin) -j $(pdd_txt) -o $@ $<
+
+#
+# Default data
+#
+# If the binary data is not available in the current working directory
+# we try to create symlinks to our test data.
+#
+$(vmlinux_bin) $(rootfs_bin) $(spl_bin):
+ @echo
+ @echo "No $@ found, will use defaults !"
+ @echo
+ @echo "OR press CTRL-C to provide your own $@" && \
+ sleep 1 && \
+ $(dd) if=/dev/urandom of=$@ bs=1M count=1
+
+clean:
+ $(RM) *.pfi *~
+
+distclean: clean
+ $(RM) *.bin *.mif *.oob *.img
diff --git a/ubi-utils/scripts/README b/ubi-utils/scripts/README
new file mode 100644
index 0000000..899b4a1
--- /dev/null
+++ b/ubi-utils/scripts/README
@@ -0,0 +1,11 @@
+README
+======
+
+This procedure creates a test pfi which should be flashed to our
+system with pfiflash. The testcase should read the data back and
+compare with the original.
+
+We should try not forget to run these tests before we release
+a new version of UBI.
+
+Frank
diff --git a/ubi-utils/scripts/TODO b/ubi-utils/scripts/TODO
new file mode 100644
index 0000000..f093e77
--- /dev/null
+++ b/ubi-utils/scripts/TODO
@@ -0,0 +1,5 @@
+TODO
+====
+
+ * Range checking is broken, reserving 2M and offering 3M binary data
+ ... works!? No!
diff --git a/ubi-utils/scripts/jffs2_test.sh b/ubi-utils/scripts/jffs2_test.sh
new file mode 100755
index 0000000..0cc9f0c
--- /dev/null
+++ b/ubi-utils/scripts/jffs2_test.sh
@@ -0,0 +1,91 @@
+#!/bin/sh
+#
+# Testcase for JFFS2 verification. We do not want to see any
+# kernel errors occuring when this is executed.
+#
+#
+# To have a standardized output I define the following function to be
+# used when a test was ok or when it failed.
+#
+failed ()
+{
+ echo "FAILED"
+}
+
+passed ()
+{
+ echo "PASSED"
+}
+
+#
+# Print sucess message. Consider to exit with zero as return code.
+#
+exit_success ()
+{
+ echo "SUCCESS"
+ exit 0
+}
+
+#
+# Print failure message. Consider to exit with non zero return code.
+#
+exit_failure ()
+{
+ echo "FAILED"
+ exit 1
+}
+
+echo "***********************************************************************"
+echo "* jffs2 testing ... *"
+echo "***********************************************************************"
+
+ulimit -c unlimited
+
+for i in `seq 5000`; do
+ echo "Testing $i byte (dd if=/dev/urandom of=foo bs=$i count=1) ... "
+ dd if=/dev/urandom of=test.bin bs=$i count=1;
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+
+ echo "Copy to different file ... "
+ dd if=test.bin of=new.bin bs=$i count=1;
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+
+ echo "Comparing files ... "
+ cmp test.bin new.bin
+ dd if=test.bin of=new.bin bs=$i count=1;
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+done
+
+for i in `seq 5000`; do
+ echo "Testing $i byte (dd if=/dev/urandom of=foo bs=$i count=1) ... "
+ dd if=/dev/urandom of=foo bs=$i count=1;
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+done
+
+for i in `seq 5000`; do
+ echo "Testing $i byte (dd if=/dev/zero of=foo bs=$i count=1) ... "
+ dd if=/dev/zero of=foo bs=$i count=1;
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+done
+
+echo "***********************************************************************"
+echo "* Congratulations, no errors found! *"
+echo "* Have fun with your cool JFFS2 using system! *"
+echo "***********************************************************************"
+
+exit_success
diff --git a/ubi-utils/scripts/pdd.txt b/ubi-utils/scripts/pdd.txt
new file mode 100644
index 0000000..a3ad915
--- /dev/null
+++ b/ubi-utils/scripts/pdd.txt
@@ -0,0 +1,16 @@
+pdd=flash_type,flash_size,flash_eraseblock_size,flash_page_size,card_serialnumber,card_type,ethaddr,eth1addr,eth0,eth1,total,card_hardwarelevel
+pdd_preserve=ethaddr,eth1addr,card_serialnumber
+# To be personalized
+ethaddr=00:04:34:56:78:9A
+eth1addr=00:04:34:56:78:9B
+card_serialnumber=SN0
+# Static for this card type
+total=102M
+card_type=nand_driven_testcard
+card_hardwarelevel=0
+eth0=bcm5222,eth0,0
+eth1=bcm5222,eth0,1
+flash_type=NAND
+flash_size=0x08000000
+flash_eraseblock_size=0x00020000
+flash_page_size=0x00000800
diff --git a/ubi-utils/scripts/run_all.sh b/ubi-utils/scripts/run_all.sh
new file mode 100755
index 0000000..040bcbd
--- /dev/null
+++ b/ubi-utils/scripts/run_all.sh
@@ -0,0 +1,101 @@
+#!/bin/sh
+
+exit_success ()
+{
+ echo "UBI Utils Test Scripts - SUCCESS!"
+ exit 0
+}
+
+exit_failure ()
+{
+ echo $1
+ echo "UBI Utils Test Scripts - FAILED!"
+ exit 1
+}
+
+echo UBI Utils Test Scripts
+
+devno=$1
+logfile=temp-test-log.txt
+
+if test -z "$devno";
+then
+ echo "Usage is $0 <mtd device number>"
+ exit 1
+fi
+
+cwd=`pwd` || exit_failure "pwd failed"
+
+log="${cwd}/${logfile}"
+
+PATH=$PATH:$cwd:..
+
+cat /dev/null > $log || exit_failure "Failed to create $log"
+
+echo "Setting up for jffs2_test.sh" | tee -a $log
+
+avail=`cat /sys/class/ubi/ubi${devno}/avail_eraseblocks`
+size=`cat /sys/class/ubi/ubi${devno}/eraseblock_size`
+
+bytes=`expr $avail \* $size`
+
+ubimkvol -d$devno -s$bytes -n0 -Njtstvol || exit_failure "ubimkvol failed"
+
+mkdir -p /mnt/test_file_system || exit_failure "mkdir failed"
+
+mtd=`cat /proc/mtd | grep jtstvol | cut -d: -f1`
+
+if test -z "$mtd";
+then
+ exit_failure "mtd device not found"
+fi
+
+mount -t jffs2 $mtd /mnt/test_file_system || exit_failure "mount failed"
+
+cd /mnt/test_file_system || exit_failure "cd failed"
+
+echo Running jffs2_test.sh | tee -a $log
+
+jffs2_test.sh >> $log 2>&1 || exit_failure "jffs2_test.sh failed"
+
+rm -f *
+
+cd $cwd || exit_failure "cd failed"
+
+umount /mnt/test_file_system || exit_failure "umount failed"
+
+ubirmvol -d$devno -n0 || exit_failure "ubirmvol failed"
+
+major=`cat /sys/class/ubi/ubi${devno}/dev | cut -d: -f1`
+
+for minor in `seq 0 32`; do
+ if test ! -e /dev/ubi${devno}_$minor ;
+ then
+ mknod /dev/ubi${devno}_$minor c $major $(($minor + 1))
+ fi
+done
+
+rm -f testdata.bin readdata.bin
+
+echo Running ubi_jffs2_test.sh | tee -a $log
+
+ubi_jffs2_test.sh >> $log 2>&1 || exit_failure "ubi_jffs2_test.sh failed"
+
+echo Running ubi_test.sh | tee -a $log
+
+ubi_test.sh >> $log 2>&1 || exit_failure "ubi_test.sh failed"
+
+for minor in `seq 0 32`; do
+ if test -e /sys/class/ubi/ubi${devno}/$minor;
+ then
+ ubirmvol -d$devno -n$minor || exit_failure "ubirmvol failed"
+ fi
+done
+
+echo Running ubi_tools_test.sh | tee -a $log
+
+ubi_tools_test.sh >> $log 2>&1 || exit_failure "ubi_tools_test failed"
+
+rm -f $log
+
+exit_success
diff --git a/ubi-utils/scripts/test.cfg b/ubi-utils/scripts/test.cfg
new file mode 100644
index 0000000..0b5ec48
--- /dev/null
+++ b/ubi-utils/scripts/test.cfg
@@ -0,0 +1,23 @@
+[targets]
+test_complete=spl,kernel,rootfs
+
+[spl]
+image=test_u-boot.bin
+ubi_ids=10,11
+ubi_size=1MiB
+ubi_type=static
+ubi_names=test_spl_0,test_spl_1
+
+[kernel]
+image=test_vmlinux.bin
+ubi_ids=12,13
+ubi_size=2MiB
+ubi_type=static
+ubi_names=test_kernel_0,test_kernel_1
+
+[rootfs]
+image=test_rootfs.bin
+ubi_ids=14,15
+ubi_size=2MiB
+ubi_type=dynamic
+ubi_names=test_rootfs_0,test_rootfs_1
diff --git a/ubi-utils/scripts/ubi_jffs2_test.sh b/ubi-utils/scripts/ubi_jffs2_test.sh
new file mode 100755
index 0000000..883903d
--- /dev/null
+++ b/ubi-utils/scripts/ubi_jffs2_test.sh
@@ -0,0 +1,411 @@
+#!/bin/sh
+#
+# UBI Volume creation/deletion/write/read and JFFS2 on top of UBI
+# testcases.
+#
+# Written in shell language to reduce dependencies to more sophisticated
+# interpreters, which may not be available on some stupid platforms.
+#
+# Author: Frank Haverkamp <haver@vnet.ibm.com>
+#
+# 1.0 Initial version
+# 1.1 Added fixup for delayed device node creation by udev
+# This points to a problem in the tools, mabe in the desing
+# Tue Oct 31 14:14:54 CET 2006
+#
+
+VERSION="1.1"
+
+export PATH=$PATH:/bin:~/bin:/usr/local/bin:/home/dedekind/work/prj/ubi/tools/flashutils/bin/
+
+ITERATIONS=250
+ALIGNMENT=2048
+
+UBIMKVOL="ubimkvol -a $ALIGNMENT"
+UBIRMVOL=ubirmvol
+UBIUPDATEVOL=ubiupdatevol
+
+SIZE_512K=524288
+SIZE_1M=1310720
+
+MINVOL=10
+MAXVOL=12
+
+TLOG=/dev/null
+
+#
+# To have a standardized output I define the following function to be
+# used when a test was ok or when it failed.
+#
+failed ()
+{
+ echo "FAILED"
+}
+
+passed ()
+{
+ echo "PASSED"
+}
+
+#
+# Print sucess message. Consider to exit with zero as return code.
+#
+exit_success ()
+{
+ echo "SUCCESS"
+ exit 0
+}
+
+#
+# Print failure message. Consider to exit with non zero return code.
+#
+exit_failure ()
+{
+ echo "FAILED"
+ exit 1
+}
+
+###############################################################################
+#
+# START
+#
+###############################################################################
+
+fix_sysfs_issue ()
+{
+ echo "*** Fixing the sysfs issue with the /dev nodes ... "
+
+ minor=0
+ major=`grep ubi0 /proc/devices | sed -e 's/\(.*\) ubi0/\1/'`
+
+ rm -rf /dev/ubi0
+ mknod /dev/ubi0 c $major 0
+
+ for minor in `seq $MINVOL $MAXVOL`; do
+ echo " -> mknod /dev/ubi0_$minor c $major $(($minor + 1))"
+ rm -rf /dev/ubi0_$minor
+ mknod /dev/ubi0_$minor c $major $(($minor + 1))
+ done
+ passed
+}
+
+#
+# FIXME Udev needs some time until the device nodes are created.
+# This will cause trouble if after ubimkvol an update attempt
+# is started immediately, since the device node is not yet
+# available. We should either fix the tools with inotify or
+# other ideas or figure out a different way to solve the problem
+# e.g. to use ubi0 and make the volume device nodes obsolete...
+#
+udev_wait ()
+{
+ echo -n "FIXME Waiting for udev to create/delete device node "
+ grep 2\.6\.5 /proc/version > /dev/null
+ if [ $? -eq "0" ]; then
+ for i in `seq 0 5`; do
+ sleep 1; echo -n ".";
+ done
+ echo " ok"
+ fi
+}
+
+# delete_volume - Delete a volume. If it does not exist, do not try
+# to delete it.
+# @id: volume id
+#
+delete_volume ()
+{
+ volume=$1
+
+ ### FIXME broken sysfs!!!!
+ if [ -e /sys/class/ubi/$volume -o \
+ -e /sys/class/ubi/ubi0/$volume -o \
+ -e /sys/class/ubi/ubi0_$volume ]; then
+
+ echo "*** Truncate volume if it exists ... "
+ echo " $UBIUPDATEVOL -d0 -n$volume -t"
+ $UBIUPDATEVOL -d0 -n$volume -t
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+
+ echo -n "*** Delete volume if it exists ... "
+ $UBIRMVOL -d0 -n$volume
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+ # udev_wait
+ fi
+}
+
+# writevol_test - Tests volume creation and writing data to it.
+#
+# @volume: Volume number
+# @size: Size of random data to write
+# @type: Volume type static or dynamic
+#
+writevol_test ()
+{
+ volume=$1
+ size=$2
+ type=$3
+
+ echo "*** Write volume test with size $size"
+
+### Make sure that volume exist, delete existing volume, create new
+
+ delete_volume $volume
+
+ echo "*** Try to create volume"
+ echo " $UBIMKVOL -d0 -n$volume -t$type -NNEW$volume -s $size ... "
+ $UBIMKVOL -d0 -n$volume -t$type -N"NEW$volume" -s $size
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+ udev_wait
+
+### Try to create same volume again
+ echo -n "*** Try to create some volume again, this must fail ... "
+ $UBIMKVOL -d0 -n$volume -t$type -N"NEW$volume" -s $size
+ if [ $? -eq "0" ] ; then
+ exit_failure
+ fi
+ passed
+
+### Now create test data, write it, read it, compare it
+ echo -n "*** Create test data ... "
+ dd if=/dev/urandom of=testdata.bin bs=$size count=1
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+
+ echo "*** Now writing data to volume ... "
+ echo " $UBIUPDATEVOL -d0 -n$volume testdata.bin"
+ ls -l testdata.bin
+ $UBIUPDATEVOL -d0 -n$volume testdata.bin
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+
+ echo "*** Download data with dd bs=1 ... "
+ dd if=/dev/ubi0_$volume of=readdata.bin bs=$size count=1
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+
+ echo -n "*** Comparing data ... "
+ cmp readdata.bin testdata.bin
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+
+ echo -n "*** Now truncate volume ... "
+ $UBIUPDATEVOL -d0 -n$volume -t
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+}
+
+jffs2_torture ()
+{
+ cat /dev/null > TLOG
+
+ echo "*** Torture test ... "
+
+ for i in `seq $iterations`; do
+ dd if=/dev/urandom of=test.bin bs=$i count=1 2>> $TLOG
+ if [ $? -ne "0" ] ; then
+ echo "Testing $i byte (dd if=/dev/urandom of=foo bs=$i count=1) ... "
+ exit_failure
+ fi
+ #passed
+
+ dd if=test.bin of=new.bin bs=$i count=1 2>> $TLOG
+ if [ $? -ne "0" ] ; then
+ echo "dd if=test.bin of=new.bin bs=$i count=1 2>> $TLOG"
+ exit_failure
+ fi
+ #passed
+
+ #echo "Comparing files ... "
+ cmp test.bin new.bin
+ dd if=test.bin of=new.bin bs=$i count=1 2>> $TLOG
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ #passed
+ #echo -n "."
+ done
+
+ echo -n "step0:ok "
+
+ for i in `seq $iterations`; do
+ dd if=/dev/urandom of=foo bs=$i count=1 2>> $TLOG
+ if [ $? -ne "0" ] ; then
+ echo "Testing $i byte (dd if=/dev/urandom of=foo bs=$i count=1) ... "
+ exit_failure
+ fi
+ #passed
+ done
+
+ echo -n "step1:ok "
+
+ for i in `seq $iterations`; do
+ dd if=/dev/zero of=foo bs=1 count=$i 2>> $TLOG
+ if [ $? -ne "0" ] ; then
+ echo "Testing $i byte (dd if=/dev/zero of=foo bs=1 count=$i) ... "
+ exit_failure
+ fi
+ #passed
+ done
+
+ echo -n "step2:ok "
+
+ for i in `seq $iterations`; do
+ dd if=/dev/zero of=foo bs=$i count=16 2>> $TLOG
+ if [ $? -ne "0" ] ; then
+ echo "Testing $i byte (dd if=/dev/zero of=foo bs=$i count=1024) ... "
+ exit_failure
+ fi
+ #passed
+ done
+
+ echo -n "step3:ok "
+
+ passed
+}
+
+# writevol_test - Tests volume creation and writing data to it.
+#
+# @volume: Volume number
+# @size: Size of random data to write
+# @type: Volume type static or dynamic
+#
+jffs2_test ()
+{
+ name=$1
+ iterations=$2
+ directory=`pwd`
+
+ ### Setup
+ ulimit -c unlimited
+
+ echo -n "*** Create directory /mnt/$name ... "
+ mkdir -p /mnt/$name
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+
+ echo -n "*** mount -t jffs2 mtd:$name /mnt/$name ... "
+ mount -t jffs2 mtd:$name /mnt/$name
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+
+ echo -n "*** change directory ... "
+ cd /mnt/$name
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+
+ ls
+ echo "*** list directory ... "
+ ls -la
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+
+ ### Torture
+ echo -n "*** touch I_WAS_HERE ... "
+ touch I_WAS_HERE
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+
+ jffs2_torture
+
+ echo "*** list directory ... "
+ ls -la
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+
+ ### Cleanup
+ echo -n "*** go back ... "
+ cd $directory
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+
+ ### Still mounted, ubiupdatevol must fail!
+
+ echo -n "*** $UBIUPDATEVOL -d0 -n$volume -t must fail! ..."
+ $UBIUPDATEVOL -d0 -n$volume -t
+ if [ $? -eq "0" ] ; then
+ exit_failure
+ fi
+ passed
+
+ echo -n "*** umount /mnt/$name ... "
+ umount /mnt/$name
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+
+ return
+}
+
+echo "***********************************************************************"
+echo "* UBI JFFS2 Testing starts now ... *"
+echo "* Good luck! *"
+echo "***********************************************************************"
+echo "VERSION: $VERSION"
+
+# Set to zero if not running on example hardware
+grep ubi /proc/devices > /dev/null
+if [ $? -ne "0" ]; then
+ echo "No UBI found in /proc/devices! I am broken!"
+ exit_failure
+fi
+
+# Set to zero if not running on example hardware
+grep 1142 /proc/cpuinfo > /dev/null
+if [ $? -eq "0" ]; then
+ echo "Running on example hardware"
+ mount -o remount,rw / /
+ sleep 1
+ fix_sysfs_issue
+else
+ echo "Running on Artems hardware"
+fi
+
+for volume in `seq $MINVOL $MAXVOL`; do
+ echo -n "************ VOLUME $volume NEW$volume "
+ echo "******************************************"
+ writevol_test $volume $SIZE_1M dynamic
+ jffs2_test NEW$volume $ITERATIONS
+ delete_volume $volume
+done
+
+echo "***********************************************************************"
+echo "* Congratulations, no errors found! *"
+echo "* Have fun with your cool UBI system! *"
+echo "***********************************************************************"
+
+exit_success
diff --git a/ubi-utils/scripts/ubi_test.sh b/ubi-utils/scripts/ubi_test.sh
new file mode 100755
index 0000000..73e4b19
--- /dev/null
+++ b/ubi-utils/scripts/ubi_test.sh
@@ -0,0 +1,328 @@
+#!/bin/sh
+#
+# UBI Volume creation/deletion/write/read test script
+#
+# Written in shell language to reduce dependencies to more sophisticated
+# interpreters, which may not be available on some stupid platforms.
+#
+# Author: Frank Haverkamp <haver@vnet.ibm.com>
+#
+# 1.0 Initial version
+# 1.1 Use ubiupdatevol instead of ubiwritevol
+#
+
+VERSION="1.1"
+
+export PATH=$PATH:~/bin:/usr/local/bin:/home/dedekind/work/prj/ubi/tools/flashutils/bin/
+
+UBIMKVOL=ubimkvol
+UBIRMVOL=ubirmvol
+UBIUPDATEVOL=ubiupdatevol
+
+# 128 KiB 131072
+# 256 KiB 262144
+# 512 KiB 524288
+
+SIZE_512K=524288
+SIZE_1M=1310720
+
+SELF=$0
+MINVOL=10
+MAXVOL=12
+
+#
+# To have a standardized output I define the following function to be
+# used when a test was ok or when it failed.
+#
+failed ()
+{
+ echo "FAILED"
+}
+
+passed ()
+{
+ echo "PASSED"
+}
+
+#
+# Print sucess message. Consider to exit with zero as return code.
+#
+exit_success ()
+{
+ echo "SUCCESS"
+ exit 0
+}
+
+#
+# Print failure message. Consider to exit with non zero return code.
+#
+exit_failure ()
+{
+ echo "FAILED"
+ exit 1
+}
+
+###############################################################################
+#
+# START
+#
+###############################################################################
+
+fix_sysfs_issue ()
+{
+ echo -n "*** Fixing the sysfs issue with the /dev nodes ... "
+
+ minor=0
+ major=`grep ubi0 /proc/devices | sed -e 's/\(.*\) ubi0/\1/'`
+
+ rm -rf /dev/ubi0
+ mknod /dev/ubi0 c $major 0
+
+ for minor in `seq 0 $MAXVOL`; do
+ ### echo " mknod /dev/ubi0_$minor c $major $(($minor + 1))"
+ rm -rf /dev/ubi0_$minor
+ mknod /dev/ubi0_$minor c $major $(($minor + 1))
+ done
+ passed
+}
+
+# delete_volume - Delete a volume. If it does not exist, do not try
+# to delete it.
+# @id: volume id
+#
+delete_volume ()
+{
+ volume=$1
+
+ ### FIXME broken sysfs!!!!
+ if [ -e /sys/class/ubi/$volume -o -e /sys/class/ubi/ubi0/$volume -o -e /sys/class/ubi/ubi0_$volume ]; then
+
+ echo -n "*** Truncate volume if it exists ... "
+ $UBIUPDATEVOL -d0 -n$volume -t
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+
+ echo -n "*** Delete volume if it exists ... "
+ $UBIRMVOL -d0 -n$volume
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+ fi
+}
+
+mkvol_rmvol_test ()
+{
+ type=$1
+
+### Test if volume delete on non-existing volumes fails nicely
+
+ for i in `seq $MINVOL $MAXVOL`; do
+ echo "*** Delete if exist or not $i ... "
+
+ delete_volume $i
+ passed
+ done
+
+### Now deleting volumes must fail
+
+ for i in `seq $MINVOL $MAXVOL`; do
+ echo "*** Trying to delete non existing UBI Volume $i ... "
+
+ $UBIRMVOL -d0 -n$i
+ if [ $? -eq "0" ] ; then
+ exit_failure
+ fi
+ passed
+ done
+
+### Test if volume creation works ok
+
+ for i in `seq $MINVOL $MAXVOL`; do
+ echo "*** Creating UBI Volume $i ... "
+ echo " $UBIMKVOL -d0 -n$i -t$type -NNEW$i -s $SIZE_512K"
+
+ $UBIMKVOL -d0 -n$i -t$type -N"NEW$i" -s $SIZE_512K
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+ done
+
+### Now deleting volumes must be ok
+
+ for i in `seq $MINVOL $MAXVOL`; do
+ echo "*** Trying to delete UBI Volume $i ... "
+
+ $UBIRMVOL -d0 -n$i
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+ done
+
+### Now allocate too large volume
+
+ echo -n "*** Try to create too large volume"
+ $UBIMKVOL -d0 -n$MINVOL -t$type -N"NEW$MINVOL" -s 800000000
+ if [ $? -eq "0" ] ; then
+ exit_failure
+ fi
+ passed
+}
+
+# writevol_test - Tests volume creation and writing data to it.
+#
+# @size: Size of random data to write
+# @type: Volume type static or dynamic
+#
+writevol_test ()
+{
+ size=$1
+ type=$2
+
+ echo "*** Write volume test with size $size"
+
+### Make sure that volume exist, delete existing volume, create new
+
+ delete_volume $MINVOL
+
+ echo -n "*** Try to create volume ... "
+ $UBIMKVOL -d0 -n$MINVOL -t$type -N"NEW$MINVOL" -s $SIZE_1M
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+
+### Try to create same volume again
+ echo -n "*** Try to create some volume again, this must fail ... "
+ $UBIMKVOL -d0 -n$MINVOL -t$type -N"NEW$MINVOL" -s $SIZE_1M
+ if [ $? -eq "0" ] ; then
+ exit_failure
+ fi
+ passed
+
+### Now create test data, write it, read it, compare it
+ echo -n "*** Create test data ... "
+ dd if=/dev/urandom of=testdata.bin bs=$size count=1
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+
+ echo "*** Now writing data to volume ... "
+ # sleep 5
+ ls -l testdata.bin
+ echo " $UBIUPDATEVOL -d0 -n$MINVOL testdata.bin"
+ $UBIUPDATEVOL -d0 -n$MINVOL testdata.bin
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+
+ if [ $type = "static" ] ; then
+ echo "*** Download data with cat ... "
+ cat /dev/ubi0_$MINVOL > readdata.bin
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+ else
+ echo "*** Download data with dd bs=1 ... "
+ dd if=/dev/ubi0_$MINVOL of=readdata.bin bs=$size count=1
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+
+ # Size 1 does not work with this test ...
+ #
+ #echo "*** Download data with dd bs=$size ... "
+ #dd if=/dev/ubi0_$MINVOL of=readdata2.bin bs=$size count=1
+ #if [ $? -ne "0" ] ; then
+ # exit_failure
+ #fi
+ #passed
+
+ #echo -n "*** Comparing data (1) ... "
+ #cmp readdata.bin readdata2.bin
+ #if [ $? -ne "0" ] ; then
+ # exit_failure
+ #fi
+ #passed
+ fi
+
+ echo -n "*** Comparing data ... "
+ cmp readdata.bin testdata.bin
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+}
+
+echo "***********************************************************************"
+echo "* UBI Testing starts now ... *"
+echo "* Good luck! *"
+echo "***********************************************************************"
+
+# Set to zero if not running on example hardware
+grep ubi /proc/devices > /dev/null
+if [ $? -ne "0" ]; then
+ echo "No UBI found in /proc/devices! I am broken!"
+ exit_failure
+fi
+
+# Set to zero if not running on example hardware
+grep 1142 /proc/cpuinfo > /dev/null
+if [ $? -eq "0" ]; then
+ echo "Running on example hardware"
+ mount -o remount,rw / /
+ sleep 1
+ fix_sysfs_issue
+else
+ echo "Running on Artems hardware"
+fi
+
+echo "***********************************************************************"
+echo "* mkvol/rmvol testing for static volumes ... *"
+echo "***********************************************************************"
+
+mkvol_rmvol_test static
+
+echo "***********************************************************************"
+echo "* mkvol/rmvol testing for dynamic volumes ... *"
+echo "***********************************************************************"
+
+mkvol_rmvol_test dynamic
+
+echo "***********************************************************************"
+echo "* write to static volumes ... *"
+echo "***********************************************************************"
+
+# 10 Erase blocks = (128 KiB - 64 * 2) * 10
+# = 1309440 bytes
+# 128 KiB 131072
+# 256 KiB 262144
+# 512 KiB 524288
+
+for size in 262144 131073 131072 2048 1 4096 12800 31313 ; do
+ writevol_test $size static
+done
+
+echo "***********************************************************************"
+echo "* write to dynamic volumes ... *"
+echo "***********************************************************************"
+echo "VERSION: $VERSION"
+
+for size in 131073 131072 2048 1 4096 12800 31313 262144 ; do
+ writevol_test $size dynamic
+done
+
+echo "***********************************************************************"
+echo "* Congratulations, no errors found! *"
+echo "* Have fun with your cool UBI system! *"
+echo "***********************************************************************"
+
+exit_success
diff --git a/ubi-utils/scripts/ubi_tools_test.sh b/ubi-utils/scripts/ubi_tools_test.sh
new file mode 100755
index 0000000..7f121f1
--- /dev/null
+++ b/ubi-utils/scripts/ubi_tools_test.sh
@@ -0,0 +1,252 @@
+#!/bin/sh
+#
+# UBI Volume creation/deletion/write/read test script.
+# Uses our flash update tools and the associated toolchain for flash
+# image creation.
+#
+# Written in shell language to reduce dependencies to more sophisticated
+# interpreters, which may not be available on some stupid platforms.
+#
+# Author: Frank Haverkamp <haver@vnet.ibm.com>
+#
+# 1.0 Initial version
+#
+
+VERSION="1.0"
+
+export PATH=$PATH:~/bin:/usr/local/bin:/home/dedekind/work/prj/ubi/tools/flashutils/bin/
+
+UBIMKVOL=ubimkvol
+UBIRMVOL=ubirmvol
+UBIWRITEVOL=ubiupdatevol
+PFIFLASH=pfiflash
+CMP=cmp
+
+MAXVOL=32
+
+test_pfi=test_complete.pfi
+real_pfi=example_complete.pfi
+
+# 128 KiB 131072
+# 256 KiB 262144
+# 512 KiB 524288
+
+#
+# To have a standardized output I define the following function to be
+# used when a test was ok or when it failed.
+#
+failed ()
+{
+ echo "FAILED"
+}
+
+passed ()
+{
+ echo "PASSED"
+}
+
+#
+# Print sucess message. Consider to exit with zero as return code.
+#
+exit_success ()
+{
+ echo "SUCCESS"
+ exit 0
+}
+
+#
+# Print failure message. Consider to exit with non zero return code.
+#
+exit_failure ()
+{
+ echo "FAILED"
+ exit 1
+}
+
+###############################################################################
+#
+# START
+#
+###############################################################################
+
+fix_sysfs_issue ()
+{
+ echo -n "*** Fixing the sysfs issue with the /dev nodes ... "
+
+ minor=0
+ major=`grep ubi0 /proc/devices | sed -e 's/\(.*\) ubi0/\1/'`
+
+ rm -rf /dev/ubi0
+ mknod /dev/ubi0 c $major 0
+
+ for minor in `seq 0 $MAXVOL`; do
+ ### echo " mknod /dev/ubi0_$minor c $major $(($minor + 1))"
+ rm -rf /dev/ubi0_$minor
+ mknod /dev/ubi0_$minor c $major $(($minor + 1))
+ done
+ passed
+}
+
+# delete_volume - Delete a volume. If it does not exist, do not try
+# to delete it.
+# @id: volume id
+#
+delete_volume ()
+{
+ volume=$1
+
+ ### FIXME broken sysfs!!!!
+ if [ -e /sys/class/ubi/$volume -o -e /sys/class/ubi/ubi0/$volume -o -e /sys/class/ubi/ubi0_$volume ]; then
+
+ echo -n "*** Truncate volume if it exists ... "
+ $UBIWRITEVOL -d0 -n$volume -t
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+
+ echo -n "*** Delete volume if it exists ... "
+ $UBIRMVOL -d0 -n$volume
+ if [ $? -ne "0" ] ; then
+ exit_failure
+ fi
+ passed
+ fi
+}
+
+echo "***********************************************************************"
+echo "* UBI Tools Testing starts now ... *"
+echo "* Good luck! *"
+echo "***********************************************************************"
+
+# Set to zero if not running on example hardware
+grep ubi /proc/devices > /dev/null
+if [ $? -ne "0" ]; then
+ echo "No UBI found in /proc/devices! I am broken!"
+ exit_failure
+fi
+
+# Set to zero if not running on example hardware
+grep 1142 /proc/cpuinfo > /dev/null
+if [ $? -eq "0" ]; then
+ echo "Running on example hardware"
+ mount -o remount,rw / /
+ sleep 1
+ fix_sysfs_issue
+else
+ echo "Running on other hardware"
+fi
+
+### Test basic stuff
+pfiflash_basic ()
+{
+ echo "Calling pfiflash with test-data ... "
+ echo " $PFIFLASH $test_pfi"
+ $PFIFLASH $test_pfi
+ if [ $? -ne "0" ]; then
+ echo "Uhhh something went wrong!"
+ exit_failure
+ fi
+ passed
+
+ echo "Testing if data is correct 10 and 11 ... "
+ $CMP /dev/ubi0_10 /dev/ubi0_11
+ if [ $? -ne "0" ]; then
+ echo "Mirrored volumes not equal!"
+ exit_failure
+ fi
+ passed
+
+ echo "Comparing against original data ... "
+ $CMP /dev/ubi0_10 test_u-boot.bin
+ if [ $? -ne "0" ]; then
+ echo "Compared volume not equal!"
+ exit_failure
+ fi
+ passed
+
+ echo "Testing if data is correct 12 and 13 ... "
+ $CMP /dev/ubi0_12 /dev/ubi0_13
+ if [ $? -ne "0" ]; then
+ echo "Mirrored volumes not equal!"
+ exit_failure
+ fi
+ passed
+
+ echo "Comparing against original data ... "
+ $CMP /dev/ubi0_12 test_vmlinux.bin
+ if [ $? -ne "0" ]; then
+ echo "Compared volume not equal!"
+ exit_failure
+ fi
+ passed
+
+ echo "Testing if data is correct 14 and 15 ... "
+ $CMP /dev/ubi0_14 /dev/ubi0_15
+ if [ $? -ne "0" ]; then
+ echo "Mirrored volumes not equal!"
+ exit_failure
+ fi
+ passed
+}
+
+### Test each and everything
+pfiflash_advanced ()
+{
+ if [ -e example_complete.pfi ]; then
+ echo "Calling pfiflash with real data ... "
+ $PFIFLASH -p overwrite --complete example_complete.pfi
+ if [ $? -ne "0" ]; then
+ echo "Uhhh something went wrong!"
+ exit_failure
+ fi
+ passed
+
+ echo "Testing if data is correct 2 and 3 ... "
+ $CMP /dev/ubi0_2 /dev/ubi0_3
+ if [ $? -ne "0" ]; then
+ echo "Mirrored volumes not equal!"
+ exit_failure
+ fi
+ passed
+
+ echo "Comparing against original data ... "
+ $CMP /dev/ubi0_2 u-boot.bin
+ if [ $? -ne "0" ]; then
+ echo "Compared volume not equal!"
+ exit_failure
+ fi
+ passed
+
+ echo "Testing if data is correct 6 and 7 ... "
+ $CMP /dev/ubi0_6 /dev/ubi0_7
+ if [ $? -ne "0" ]; then
+ echo "Mirrored volumes not equal!"
+ exit_failure
+ fi
+ passed
+
+ echo "Comparing against original data ... "
+ $CMP /dev/ubi0_6 vmlinux.bin
+ if [ $? -ne "0" ]; then
+ echo "Compared volume not equal!"
+ exit_failure
+ fi
+ passed
+ fi
+}
+
+echo "***********************************************************************"
+echo "* Testing pfiflash ... *"
+echo "***********************************************************************"
+echo "VERSION: $VERSION"
+
+pfiflash_basic
+pfiflash_advanced
+
+echo "***********************************************************************"
+echo "* Congratulations, no errors found! *"
+echo "* Have fun with your cool UBI system! *"
+echo "***********************************************************************"
+
+exit_success
diff --git a/ubi-utils/src/bin2nand.c b/ubi-utils/src/bin2nand.c
new file mode 100644
index 0000000..7c4c816
--- /dev/null
+++ b/ubi-utils/src/bin2nand.c
@@ -0,0 +1,346 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2007
+ *
+ * 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.
+ *
+ * Author: Oliver Lohmann
+ */
+
+/*
+ * Create a flashable NAND image from a binary image
+ *
+ * History:
+ * 1.0 Initial release (tglx)
+ * 1.1 Understands hex and dec input parameters (tglx)
+ * 1.2 Generates separated OOB data, if needed. (oloh)
+ * 1.3 Padds data/oob to a given size. (oloh)
+ * 1.4 Removed argp because we want to use uClibc.
+ * 1.5 Minor cleanup
+ * 1.6 written variable not initialized (-j did not work) (haver)
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+
+#include "error.h"
+#include "config.h"
+#include "nandecc.h"
+
+#define PROGRAM_VERSION "1.6"
+
+#define CHECK_ENDP(option, endp) do { \
+ if (*endp) { \
+ fprintf(stderr, \
+ "Parse error option \'%s\'. " \
+ "No correct numeric value.\n" \
+ , option); \
+ exit(EXIT_FAILURE); \
+ } \
+} while(0)
+
+typedef enum action_t {
+ ACT_NORMAL = 0x00000001,
+} action_t;
+
+#define PAGESIZE 2048
+#define PADDING 0 /* 0 means, do not adjust anything */
+#define BUFSIZE 4096
+
+static char doc[] = "\nVersion: " PROGRAM_VERSION "\n\tBuilt on "
+ BUILD_CPU" "BUILD_OS" at "__DATE__" "__TIME__"\n"
+ "\n"
+ "bin2nand - a tool for adding OOB information to a "
+ "binary input file.\n";
+
+static const char *optionsstr =
+" -c, --copyright Print copyright informatoin.\n"
+" -j, --padding=<num> Padding in Byte/Mi/ki. Default = no padding\n"
+" -p, --pagesize=<num> Pagesize in Byte/Mi/ki. Default = 2048\n"
+" -o, --output=<fname> Output filename. Interleaved Data/OOB if\n"
+" output-oob not specified.\n"
+" -q, --output-oob=<fname> Write OOB data in separate file.\n"
+" -?, --help Give this help list\n"
+" --usage Give a short usage message\n"
+" -V, --version Print program version\n";
+
+static const char *usage =
+"Usage: bin2nand [-c?V] [-j <num>] [-p <num>] [-o <fname>] [-q <fname>]\n"
+" [--copyright] [--padding=<num>] [--pagesize=<num>]\n"
+" [--output=<fname>] [--output-oob=<fname>] [--help] [--usage]\n"
+" [--version]\n";
+
+struct option long_options[] = {
+ { .name = "copyright", .has_arg = 0, .flag = NULL, .val = 'c' },
+ { .name = "padding", .has_arg = 1, .flag = NULL, .val = 'j' },
+ { .name = "pagesize", .has_arg = 1, .flag = NULL, .val = 'p' },
+ { .name = "output", .has_arg = 1, .flag = NULL, .val = 'o' },
+ { .name = "output-oob", .has_arg = 1, .flag = NULL, .val = 'q' },
+ { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' },
+ { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 },
+ { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' },
+ { NULL, 0, NULL, 0}
+};
+
+static const char copyright [] __attribute__((unused)) =
+ "Copyright IBM Corp. 2006";
+
+typedef struct myargs {
+ action_t action;
+
+ size_t pagesize;
+ size_t padding;
+
+ FILE* fp_in;
+ char *file_out_data; /* Either: Data and OOB interleaved
+ or plain data */
+ char *file_out_oob; /* OOB Data only. */
+
+ /* special stuff needed to get additional arguments */
+ char *arg1;
+ char **options; /* [STRING...] */
+} myargs;
+
+
+static int ustrtoull(const char *cp, char **endp, unsigned int base)
+{
+ unsigned long long res = strtoull(cp, endp, base);
+
+ switch (**endp) {
+ case 'G':
+ res *= 1024;
+ case 'M':
+ res *= 1024;
+ case 'k':
+ case 'K':
+ res *= 1024;
+ /* "Ki", "ki", "Mi" or "Gi" are to be used. */
+ if ((*endp)[1] == 'i')
+ (*endp) += 2;
+ }
+ return res;
+}
+
+static int
+parse_opt(int argc, char **argv, myargs *args)
+{
+ char* endp;
+
+ while (1) {
+ int key;
+
+ key = getopt_long(argc, argv, "cj:p:o:q:?V", long_options, NULL);
+ if (key == -1)
+ break;
+
+ switch (key) {
+ case 'p': /* pagesize */
+ args->pagesize = (size_t)
+ ustrtoull(optarg, &endp, 0);
+ CHECK_ENDP("p", endp);
+ break;
+ case 'j': /* padding */
+ args->padding = (size_t)
+ ustrtoull(optarg, &endp, 0);
+ CHECK_ENDP("j", endp);
+ break;
+ case 'o': /* output */
+ args->file_out_data = optarg;
+ break;
+ case 'q': /* output oob */
+ args->file_out_oob = optarg;
+ break;
+ case '?': /* help */
+ printf("%s", doc);
+ printf("%s", optionsstr);
+ exit(0);
+ break;
+ case 'V':
+ printf("%s\n", PROGRAM_VERSION);
+ exit(0);
+ break;
+ case 'c':
+ printf("%s\n", copyright);
+ exit(0);
+ default:
+ printf("%s", usage);
+ exit(-1);
+ }
+ }
+
+ if (optind < argc) {
+ args->fp_in = fopen(argv[optind++], "rb");
+ if ((args->fp_in) == NULL) {
+ err_quit("Cannot open file %s for input\n",
+ argv[optind++]);
+ }
+ }
+
+ return 0;
+}
+
+static int
+process_page(uint8_t* buf, size_t pagesize,
+ FILE *fp_data, FILE* fp_oob, size_t* written)
+{
+ int eccpoi, oobsize;
+ size_t i;
+ uint8_t oobbuf[64];
+
+ memset(oobbuf, 0xff, sizeof(oobbuf));
+
+ switch(pagesize) {
+ case 2048: oobsize = 64; eccpoi = 64 / 2; break;
+ case 512: oobsize = 16; eccpoi = 16 / 2; break;
+ default:
+ err_msg("Unsupported page size: %d\n", pagesize);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < pagesize; i += 256, eccpoi += 3) {
+ oobbuf[eccpoi++] = 0x0;
+ /* Calculate ECC */
+ nand_calculate_ecc(&buf[i], &oobbuf[eccpoi]);
+ }
+
+ /* write data */
+ *written += fwrite(buf, 1, pagesize, fp_data);
+
+ /* either separate oob or interleave with data */
+ if (fp_oob) {
+ fwrite(oobbuf, 1, oobsize, fp_oob);
+ if (ferror(fp_oob)) {
+ err_msg("IO error\n");
+ return -EIO;
+ }
+ }
+ else {
+ fwrite(oobbuf, 1, oobsize, fp_data);
+ if (ferror(fp_data)) {
+ err_msg("IO error\n");
+ return -EIO;
+ }
+ }
+
+ return 0;
+}
+
+int main (int argc, char** argv)
+{
+ int rc = -1;
+ int res = 0;
+ size_t written = 0, read;
+ myargs args = {
+ .action = ACT_NORMAL,
+ .pagesize = PAGESIZE,
+ .padding = PADDING,
+ .fp_in = NULL,
+ .file_out_data = NULL,
+ .file_out_oob = NULL,
+ };
+
+ FILE* fp_out_data = stdout;
+ FILE* fp_out_oob = NULL;
+
+ parse_opt(argc, argv, &args);
+
+ uint8_t* buf = calloc(1, BUFSIZE);
+ if (!buf) {
+ err_quit("Cannot allocate page buffer.\n");
+ }
+
+ if (!args.fp_in) {
+ err_msg("No input image specified!\n");
+ goto err;
+ }
+
+ if (args.file_out_data) {
+ fp_out_data = fopen(args.file_out_data, "wb");
+ if (fp_out_data == NULL) {
+ err_sys("Cannot open file %s for output\n",
+ args.file_out_data);
+ goto err;
+ }
+ }
+
+ if (args.file_out_oob) {
+ fp_out_oob = fopen(args.file_out_oob, "wb");
+ if (fp_out_oob == NULL) {
+ err_sys("Cannot open file %s for output\n",
+ args.file_out_oob);
+ goto err;
+ }
+ }
+
+
+ while(1) {
+ read = fread(buf, 1, args.pagesize, args.fp_in);
+ if (feof(args.fp_in) && read == 0)
+ break;
+
+ if (read < args.pagesize) {
+ err_msg("Image not page aligned\n");
+ goto err;
+ }
+
+ if (ferror(args.fp_in)) {
+ err_msg("Read error\n");
+ goto err;
+ }
+
+ res = process_page(buf, args.pagesize, fp_out_data,
+ fp_out_oob, &written);
+ if (res != 0)
+ goto err;
+ }
+
+ while (written < args.padding) {
+ memset(buf, 0xff, args.pagesize);
+ res = process_page(buf, args.pagesize, fp_out_data,
+ fp_out_oob, &written);
+ if (res != 0)
+ goto err;
+ }
+
+ rc = 0;
+err:
+ free(buf);
+
+ if (args.fp_in)
+ fclose(args.fp_in);
+
+ if (fp_out_oob)
+ fclose(fp_out_oob);
+
+ if (fp_out_data && fp_out_data != stdout)
+ fclose(fp_out_data);
+
+ if (rc != 0) {
+ err_msg("Error during conversion. rc: %d\n", rc);
+ if (args.file_out_data)
+ remove(args.file_out_data);
+ if (args.file_out_oob)
+ remove(args.file_out_oob);
+ }
+ return rc;
+}
diff --git a/ubi-utils/src/bootenv.c b/ubi-utils/src/bootenv.c
new file mode 100644
index 0000000..ed15dc7
--- /dev/null
+++ b/ubi-utils/src/bootenv.c
@@ -0,0 +1,984 @@
+/*
+ * 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.
+ *
+ * Author: Oliver Lohmann
+ */
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <sys/stat.h>
+#include <bootenv.h>
+
+#include "hashmap.h"
+#include "error.h"
+
+#include <mtd/ubi-header.h>
+#include "crc32.h"
+
+#define __unused __attribute__((unused))
+
+#define BOOTENV_MAXLINE 512 /* max line size of a bootenv.txt file */
+
+/* Structures */
+struct bootenv {
+ hashmap_t map; ///< Pointer to hashmap which holds data structure.
+};
+
+struct bootenv_list {
+ hashmap_t head; ///< Pointer to list which holds the data structure.
+};
+
+/**
+ * @brief Remove the '\n' from a given line.
+ * @param line Input/Output line.
+ * @param size Size of the line.
+ * @param fp File Pointer.
+ * @return 0
+ * @return or error
+ */
+static int
+remove_lf(char *line, size_t size, FILE* fp)
+{
+ size_t i;
+
+ for (i = 0; i < size; i++) {
+ if (line[i] == '\n') {
+ line[i] = '\0';
+ return 0;
+ }
+ }
+
+ if (!feof(fp)) {
+ return BOOTENV_EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * @brief Determine if a line contains only WS.
+ * @param line The line to process.
+ * @param size Size of input line.
+ * @return 1 Yes, only WS.
+ * @return 0 No, contains data.
+ */
+static int
+is_ws(const char *line, size_t size)
+{
+ size_t i = 0;
+
+ while (i < size) {
+ switch (line[i]) {
+ case '\n':
+ return 1;
+ case '#':
+ return 1;
+ case ' ':
+ i++;
+ continue;
+ case '\t':
+ i++;
+ continue;
+ default: /* any other char -> no cmnt */
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+
+/* ------------------------------------------------------------------------- */
+
+/**
+ * @brief Build a list from a comma seperated value string.
+ * @param list Pointer to hashmap structure which shall store
+ * the list.
+ * @param value Comma seperated value string.
+ * @return 0
+ * @return or error.
+ */
+static int
+build_list_definition(hashmap_t list, const char *value)
+{
+ int rc = 0;
+ char *str = NULL;
+ char *ptr = NULL;
+ size_t len, i, j;
+
+ /* str: val1,val2 , val4,...,valN */
+ len = strlen(value);
+ str = (char*) malloc((len+1) * sizeof(char));
+
+ /* 1. reformat string: remove spaces */
+ for (i = 0, j = 0; i < len; i++) {
+ if (value[i] == ' ')
+ continue;
+
+ str[j] = value[i];
+ j++;
+ }
+ str[j] = '\0';
+
+ /* str: val1,val2,val4,...,valN\0*/
+ /* 2. replace ',' seperator with '\0' */
+ len = strlen(str);
+ for (i = 0; i < len; i++) {
+ if (str[i] == ',') {
+ str[i] = '\0';
+ }
+ }
+
+ /* str: val1\0val2\0val4\0...\0valN\0*/
+ /* 3. insert definitions into a hash map, using it like a list */
+ i = j = 0;
+ ptr = str;
+ while (((i = strlen(ptr)) > 0) && (j < len)) {
+ rc = hashmap_add(list, ptr, "");
+ if (rc != 0) {
+ free(str);
+ return rc;
+ }
+ j += i+1;
+ if (j < len)
+ ptr += i+1;
+ }
+
+ free(str);
+ return rc;
+}
+
+/**
+ * @brief Extract a key value pair and add it to a hashmap
+ * @param str Input string which contains a key value pair.
+ * @param env The updated handle which contains the new pair.
+ * @return 0
+ * @return or error
+ * @note The input string format is: "key=value"
+ */
+static int
+extract_pair(const char *str, bootenv_t env)
+{
+ int rc = 0;
+ char *key = NULL;
+ char *val = NULL;
+
+ key = strdup(str);
+ if (key == NULL)
+ return -ENOMEM;
+
+ val = strstr(key, "=");
+ if (val == NULL) {
+ rc = BOOTENV_EBADENTRY;
+ goto err;
+ }
+
+ *val = '\0'; /* split strings */
+ val++;
+
+ rc = bootenv_set(env, key, val);
+
+ err:
+ free(key);
+ return rc;
+}
+
+int
+bootenv_destroy(bootenv_t* env)
+{
+ int rc = 0;
+
+ if (env == NULL || *env == NULL)
+ return -EINVAL;
+
+ bootenv_t tmp = *env;
+
+ rc = hashmap_free(tmp->map);
+ if (rc != 0)
+ return rc;
+
+ free(tmp);
+ return rc;
+}
+
+int
+bootenv_create(bootenv_t* env)
+{
+ bootenv_t res;
+ res = (bootenv_t) calloc(1, sizeof(struct bootenv));
+
+ if (res == NULL)
+ return -ENOMEM;
+
+ res->map = hashmap_new();
+
+ if (res->map == NULL) {
+ free(res);
+ return -ENOMEM;
+ }
+
+ *env = res;
+
+ return 0;
+}
+
+
+/**
+ * @brief Read a formatted buffer and scan it for valid bootenv
+ * key/value pairs. Add those pairs into a hashmap.
+ * @param env Hashmap which shall be used to hold the data.
+ * @param buf Formatted buffer.
+ * @param size Size of the buffer.
+ * @return 0
+ * @return or error
+ */
+static int
+rd_buffer(bootenv_t env, const char *buf, size_t size)
+{
+ const char *curr = buf; /* ptr to current key/value pair */
+ uint32_t i, j; /* current length, chars processed */
+
+ if (buf[size - 1] != '\0') /* must end in '\0' */
+ return BOOTENV_EFMT;
+
+ for (j = 0; j < size; j += i, curr += i) {
+ /* strlen returns the size of the string upto
+ but not including the null terminator;
+ adding 1 to account for '\0' */
+ i = strlen(curr) + 1;
+
+ if (i == 1)
+ return 0; /* no string found */
+
+ if (extract_pair(curr, env) != 0)
+ return BOOTENV_EINVAL;
+ }
+
+ return 0;
+}
+
+
+int
+bootenv_read_crc(FILE* fp, bootenv_t env, size_t size, uint32_t* ret_crc)
+{
+ int rc;
+ char *buf = NULL;
+ size_t i = 0;
+ uint32_t crc32_table[256];
+
+ if ((fp == NULL) || (env == NULL))
+ return -EINVAL;
+
+ /* allocate temp buffer */
+ buf = (char*) calloc(1, size * sizeof(char));
+ if (buf == NULL)
+ return -ENOMEM;
+
+ /* FIXME Andreas, please review this I removed size-1 and
+ * replaced it by just size, I saw the kernel image starting
+ * with a 0x0060.... and not with the 0x60.... what it should
+ * be. Is this a tools problem or is it a problem here where
+ * fp is moved not to the right place due to the former size-1
+ * here.
+ */
+ while((i < size) && (!feof(fp))) {
+ int c = fgetc(fp);
+ if (c == EOF) {
+ /* FIXME isn't this dangerous, to update
+ the boot envs with incomplete data? */
+ buf[i++] = '\0';
+ break; /* we have enough */
+ }
+ if (ferror(fp)) {
+ rc = -EIO;
+ goto err;
+ }
+
+ buf[i++] = (char)c;
+ }
+
+ /* calculate crc to return */
+ if (ret_crc != NULL) {
+ init_crc32_table(crc32_table);
+ *ret_crc = clc_crc32(crc32_table, UBI_CRC32_INIT, buf, size);
+ }
+
+ /* transfer to hashmap */
+ rc = rd_buffer(env, buf, size);
+
+err:
+ free(buf);
+ return rc;
+}
+
+
+/**
+ * If we have a single file containing the boot-parameter size should
+ * be specified either as the size of the file or as BOOTENV_MAXSIZE.
+ * If the bootparameter are in the middle of a file we need the exact
+ * length of the data.
+ */
+int
+bootenv_read(FILE* fp, bootenv_t env, size_t size)
+{
+ return bootenv_read_crc(fp, env, size, NULL);
+}
+
+
+int
+bootenv_read_txt(FILE* fp, bootenv_t env)
+{
+ int rc = 0;
+ char *buf = NULL;
+ char *line = NULL;
+ char *lstart = NULL;
+ char *curr = NULL;
+ size_t len;
+ size_t size;
+
+ if ((fp == NULL) || (env == NULL))
+ return -EINVAL;
+
+ size = BOOTENV_MAXSIZE;
+
+ /* allocate temp buffers */
+ buf = (char*) calloc(1, size * sizeof(char));
+ lstart = line = (char*) calloc(1, size * sizeof(char));
+ if ((buf == NULL) || (line == NULL)) {
+ rc = -ENOMEM;
+ goto err;
+ }
+
+ curr = buf;
+ while ((line = fgets(line, size, fp)) != NULL) {
+ if (is_ws(line, size)) {
+ continue;
+ }
+ rc = remove_lf(line, BOOTENV_MAXSIZE, fp);
+ if (rc != 0) {
+ goto err;
+ }
+
+ /* copy new line to binary buffer */
+ len = strlen(line);
+ if (len > size) {
+ rc = -EFBIG;
+ goto err;
+ }
+ size -= len; /* track remaining space */
+
+ memcpy(curr, line, len);
+ curr += len + 1; /* for \0 seperator */
+ }
+
+ rc = rd_buffer(env, buf, BOOTENV_MAXSIZE);
+err:
+ if (buf != NULL)
+ free(buf);
+ if (lstart != NULL)
+ free(lstart);
+ return rc;
+}
+
+static int
+fill_output_buffer(bootenv_t env, char *buf, size_t buf_size_max __unused,
+ size_t *written)
+{
+ int rc = 0;
+ size_t keys_size, i;
+ size_t wr = 0;
+ const char **keys = NULL;
+ const char *val = NULL;
+
+ rc = bootenv_get_key_vector(env, &keys_size, 1, &keys);
+ if (rc != 0)
+ goto err;
+
+ for (i = 0; i < keys_size; i++) {
+ if (wr > BOOTENV_MAXSIZE) {
+ rc = -ENOSPC;
+ goto err;
+ }
+
+ rc = bootenv_get(env, keys[i], &val);
+ if (rc != 0)
+ goto err;
+
+ wr += snprintf(buf + wr, BOOTENV_MAXSIZE - wr,
+ "%s=%s", keys[i], val);
+ wr++; /* for \0 */
+ }
+
+ *written = wr;
+
+err:
+ if (keys != NULL)
+ free(keys);
+
+ return rc;
+}
+
+int
+bootenv_write_crc(FILE* fp, bootenv_t env, uint32_t* ret_crc)
+{
+ int rc = 0;
+ size_t size = 0;
+ char *buf = NULL;
+ uint32_t crc32_table[256];
+
+ if ((fp == NULL) || (env == NULL))
+ return -EINVAL;
+
+ buf = (char*) calloc(1, BOOTENV_MAXSIZE * sizeof(char));
+ if (buf == NULL)
+ return -ENOMEM;
+
+
+ rc = fill_output_buffer(env, buf, BOOTENV_MAXSIZE, &size);
+ if (rc != 0)
+ goto err;
+
+ /* calculate crc to return */
+ if (ret_crc != NULL) {
+ init_crc32_table(crc32_table);
+ *ret_crc = clc_crc32(crc32_table, UBI_CRC32_INIT, buf, size);
+ }
+
+ if (fwrite(buf, size, 1, fp) != 1) {
+ rc = -EIO;
+ goto err;
+ }
+
+err:
+ if (buf != NULL)
+ free(buf);
+ return rc;
+}
+
+int
+bootenv_write(FILE* fp, bootenv_t env)
+{
+ return bootenv_write_crc(fp, env, NULL);
+}
+
+int
+bootenv_size(bootenv_t env, size_t *size)
+{
+ int rc = 0;
+ char *buf = NULL;
+
+ if (env == NULL)
+ return -EINVAL;
+
+ buf = (char*) calloc(1, BOOTENV_MAXSIZE * sizeof(char));
+ if (buf == NULL)
+ return -ENOMEM;
+
+ rc = fill_output_buffer(env, buf, BOOTENV_MAXSIZE, size);
+ if (rc != 0)
+ goto err;
+
+err:
+ if (buf != NULL)
+ free(buf);
+ return rc;
+}
+
+int
+bootenv_write_txt(FILE* fp, bootenv_t env)
+{
+ int rc = 0;
+ size_t size, wr, i;
+ const char **keys = NULL;
+ const char *key = NULL;
+ const char *val = NULL;
+
+ if ((fp == NULL) || (env == NULL))
+ return -EINVAL;
+
+ rc = bootenv_get_key_vector(env, &size, 1, &keys);
+ if (rc != 0)
+ goto err;
+
+ for (i = 0; i < size; i++) {
+ key = keys[i];
+ rc = bootenv_get(env, key, &val);
+ if (rc != 0)
+ goto err;
+
+ wr = fprintf(fp, "%s=%s\n", key, val);
+ if (wr != strlen(key) + strlen(val) + 2) {
+ rc = -EIO;
+ goto err;
+ }
+ }
+
+err:
+ if (keys != NULL)
+ free(keys);
+ return rc;
+}
+
+int
+bootenv_valid(bootenv_t env __unused)
+{
+ /* @FIXME No sanity check implemented. */
+ return 0;
+}
+
+int
+bootenv_copy_bootenv(bootenv_t in, bootenv_t *out)
+{
+ int rc = 0;
+ const char *tmp = NULL;
+ const char **keys = NULL;
+ size_t vec_size, i;
+
+ if ((in == NULL) || (out == NULL))
+ return -EINVAL;
+
+ /* purge output var for sure... */
+ rc = bootenv_destroy(out);
+ if (rc != 0)
+ return rc;
+
+ /* create the new map */
+ rc = bootenv_create(out);
+ if (rc != 0)
+ goto err;
+
+ /* get the key list from the input map */
+ rc = bootenv_get_key_vector(in, &vec_size, 0, &keys);
+ if (rc != 0)
+ goto err;
+
+ if (vec_size != hashmap_size(in->map)) {
+ rc = BOOTENV_ECOPY;
+ goto err;
+ }
+
+ /* make a deep copy of the hashmap */
+ for (i = 0; i < vec_size; i++) {
+ rc = bootenv_get(in, keys[i], &tmp);
+ if (rc != 0)
+ goto err;
+
+ rc = bootenv_set(*out, keys[i], tmp);
+ if (rc != 0)
+ goto err;
+ }
+
+err:
+ if (keys != NULL)
+ free(keys);
+
+ return rc;
+}
+
+/* ------------------------------------------------------------------------- */
+
+
+int
+bootenv_pdd_keep(bootenv_t env_old, bootenv_t env_new, bootenv_t *env_res,
+ int *warnings, char *err_buf __unused,
+ size_t err_buf_size __unused)
+{
+ bootenv_list_t l_old = NULL;
+ bootenv_list_t l_new = NULL;
+ const char *pdd_old = NULL;
+ const char *pdd_new = NULL;
+ const char *tmp = NULL;
+ const char **vec_old = NULL;
+ const char **vec_new = NULL;
+ const char **pdd_up_vec = NULL;
+ size_t vec_old_size, vec_new_size, pdd_up_vec_size, i;
+ int rc = 0;
+
+ if ((env_old == NULL) || (env_new == NULL) || (env_res == NULL))
+ return -EINVAL;
+
+ /* get the pdd strings, e.g.:
+ * pdd_old=a,b,c
+ * pdd_new=a,c,d,e */
+ rc = bootenv_get(env_old, "pdd", &pdd_old);
+ if (rc != 0)
+ goto err;
+ rc = bootenv_get(env_new, "pdd", &pdd_new);
+ if (rc != 0)
+ goto err;
+
+ /* put it into a list and then convert it to an vector */
+ rc = bootenv_list_create(&l_old);
+ if (rc != 0)
+ goto err;
+ rc = bootenv_list_create(&l_new);
+ if (rc != 0)
+ goto err;
+
+ rc = bootenv_list_import(l_old, pdd_old);
+ if (rc != 0)
+ goto err;
+
+ rc = bootenv_list_import(l_new, pdd_new);
+ if (rc != 0)
+ goto err;
+
+ rc = bootenv_list_to_vector(l_old, &vec_old_size, &vec_old);
+ if (rc != 0)
+ goto err;
+
+ rc = bootenv_list_to_vector(l_new, &vec_new_size, &vec_new);
+ if (rc != 0)
+ goto err;
+
+ rc = bootenv_copy_bootenv(env_new, env_res);
+ if (rc != 0)
+ goto err;
+
+ /* calculate the update vector between the old and new pdd */
+ pdd_up_vec = hashmap_get_update_key_vector(vec_old, vec_old_size,
+ vec_new, vec_new_size, &pdd_up_vec_size);
+
+ if (pdd_up_vec == NULL) {
+ rc = -ENOMEM;
+ goto err;
+ }
+
+ if (pdd_up_vec_size != 0) {
+ /* need to warn the user about the unset of
+ * some pdd/bootenv values */
+ *warnings = BOOTENV_WPDD_STRING_DIFFERS;
+
+ /* remove all entries in the new bootenv load */
+ for (i = 0; i < pdd_up_vec_size; i++) {
+ bootenv_unset(*env_res, pdd_up_vec[i]);
+ }
+ }
+
+ /* generate the keep array and copy old pdd values to new bootenv */
+ for (i = 0; i < vec_old_size; i++) {
+ rc = bootenv_get(env_old, vec_old[i], &tmp);
+ if (rc != 0) {
+ rc = BOOTENV_EPDDINVAL;
+ goto err;
+ }
+ rc = bootenv_set(*env_res, vec_old[i], tmp);
+ if (rc != 0) {
+ goto err;
+ }
+ }
+ /* put the old pdd string into the result map */
+ rc = bootenv_set(*env_res, "pdd", pdd_old);
+ if (rc != 0) {
+ goto err;
+ }
+
+
+err:
+ if (vec_old != NULL)
+ free(vec_old);
+ if (vec_new != NULL)
+ free(vec_new);
+ if (pdd_up_vec != NULL)
+ free(pdd_up_vec);
+
+ bootenv_list_destroy(&l_old);
+ bootenv_list_destroy(&l_new);
+ return rc;
+}
+
+
+int
+bootenv_pdd_overwrite(bootenv_t env_old, bootenv_t env_new,
+ bootenv_t *env_res, int *warnings __unused,
+ char *err_buf __unused, size_t err_buf_size __unused)
+{
+ if ((env_old == NULL) || (env_new == NULL) || (env_res == NULL))
+ return -EINVAL;
+
+ return bootenv_copy_bootenv(env_new, env_res);
+}
+
+int
+bootenv_pdd_merge(bootenv_t env_old, bootenv_t env_new, bootenv_t *env_res,
+ int *warnings __unused, char *err_buf, size_t err_buf_size)
+{
+ if ((env_old == NULL) || (env_new == NULL) || (env_res == NULL))
+ return -EINVAL;
+
+ snprintf(err_buf, err_buf_size, "The PDD merge operation is not "
+ "implemented. Contact: <oliloh@de.ibm.com>");
+
+ return BOOTENV_ENOTIMPL;
+}
+
+/* ------------------------------------------------------------------------- */
+
+int
+bootenv_get(bootenv_t env, const char *key, const char **value)
+{
+ if (env == NULL)
+ return -EINVAL;
+
+ *value = hashmap_lookup(env->map, key);
+ if (*value == NULL)
+ return BOOTENV_ENOTFOUND;
+
+ return 0;
+}
+
+int
+bootenv_get_num(bootenv_t env, const char *key, uint32_t *value)
+{
+ char *endptr = NULL;
+ const char *str;
+
+ if (env == NULL)
+ return 0;
+
+ str = hashmap_lookup(env->map, key);
+ if (!str)
+ return -EINVAL;
+
+ *value = strtoul(str, &endptr, 0);
+
+ if (*endptr == '\0') {
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+int
+bootenv_set(bootenv_t env, const char *key, const char *value)
+{
+ if (env == NULL)
+ return -EINVAL;
+
+ return hashmap_add(env->map, key, value);
+}
+
+int
+bootenv_unset(bootenv_t env, const char *key)
+{
+ if (env == NULL)
+ return -EINVAL;
+
+ return hashmap_remove(env->map, key);
+}
+
+int
+bootenv_get_key_vector(bootenv_t env, size_t* size, int sort,
+ const char ***vector)
+{
+ if ((env == NULL) || (size == NULL))
+ return -EINVAL;
+
+ *vector = hashmap_get_key_vector(env->map, size, sort);
+
+ if (*vector == NULL)
+ return -EINVAL;
+
+ return 0;
+}
+
+int
+bootenv_dump(bootenv_t env)
+{
+ if (env == NULL)
+ return -EINVAL;
+
+ return hashmap_dump(env->map);
+}
+
+int
+bootenv_list_create(bootenv_list_t *list)
+{
+ bootenv_list_t res;
+ res = (bootenv_list_t) calloc(1, sizeof(struct bootenv_list));
+
+ if (res == NULL)
+ return -ENOMEM;
+
+ res->head = hashmap_new();
+
+ if (res->head == NULL) {
+ free(res);
+ return -ENOMEM;
+ }
+
+ *list = res;
+ return 0;
+}
+
+int
+bootenv_list_destroy(bootenv_list_t *list)
+{
+ int rc = 0;
+
+ if (list == NULL)
+ return -EINVAL;
+
+ bootenv_list_t tmp = *list;
+ if (tmp == 0)
+ return 0;
+
+ rc = hashmap_free(tmp->head);
+ if (rc != 0)
+ return rc;
+
+ free(tmp);
+ *list = NULL;
+ return 0;
+}
+
+int
+bootenv_list_import(bootenv_list_t list, const char *str)
+{
+ if (list == NULL)
+ return -EINVAL;
+
+ return build_list_definition(list->head, str);
+}
+
+int
+bootenv_list_export(bootenv_list_t list, char **string)
+{
+ size_t size, i, j, bufsize, tmp, rc = 0;
+ const char **items;
+
+ if (list == NULL)
+ return -EINVAL;
+
+ bufsize = BOOTENV_MAXLINE;
+ char *res = (char*) malloc(bufsize * sizeof(char));
+ if (res == NULL)
+ return -ENOMEM;
+
+ rc = bootenv_list_to_vector(list, &size, &items);
+ if (rc != 0) {
+ goto err;
+ }
+
+ j = 0;
+ for (i = 0; i < size; i++) {
+ tmp = strlen(items[i]);
+ if (j >= bufsize) {
+ bufsize += BOOTENV_MAXLINE;
+ res = (char*) realloc(res, bufsize * sizeof(char));
+ if (res == NULL) {
+ rc = -ENOMEM;
+ goto err;
+ }
+ }
+ memcpy(res + j, items[i], tmp);
+ j += tmp;
+ if (i < (size - 1)) {
+ res[j] = ',';
+ j++;
+ }
+ }
+ j++;
+ res[j] = '\0';
+ free(items);
+ *string = res;
+ return 0;
+err:
+ free(items);
+ return rc;
+}
+
+int
+bootenv_list_add(bootenv_list_t list, const char *item)
+{
+ if ((list == NULL) || (item == NULL))
+ return -EINVAL;
+
+ return hashmap_add(list->head, item, "");
+}
+
+int
+bootenv_list_remove(bootenv_list_t list, const char *item)
+{
+ if ((list == NULL) || (item == NULL))
+ return -EINVAL;
+
+ return hashmap_remove(list->head, item);
+}
+
+int
+bootenv_list_is_in(bootenv_list_t list, const char *item)
+{
+ if ((list == NULL) || (item == NULL))
+ return -EINVAL;
+
+ return hashmap_lookup(list->head, item) != NULL ? 1 : 0;
+}
+
+int
+bootenv_list_to_vector(bootenv_list_t list, size_t *size, const char ***vector)
+{
+ if ((list == NULL) || (size == NULL))
+ return -EINVAL;
+
+ *vector = hashmap_get_key_vector(list->head, size, 1);
+ if (*vector == NULL)
+ return -ENOMEM;
+
+ return 0;
+}
+
+int
+bootenv_list_to_num_vector(bootenv_list_t list, size_t *size,
+ uint32_t **vector)
+{
+ int rc = 0;
+ size_t i;
+ uint32_t* res = NULL;
+ char *endptr = NULL;
+ const char **a = NULL;
+
+ rc = bootenv_list_to_vector(list, size, &a);
+ if (rc != 0)
+ goto err;
+
+ res = (uint32_t*) malloc (*size * sizeof(uint32_t));
+ if (!res)
+ goto err;
+
+ for (i = 0; i < *size; i++) {
+ res[i] = strtoul(a[i], &endptr, 0);
+ if (*endptr != '\0')
+ goto err;
+ }
+
+ if (a)
+ free(a);
+ *vector = res;
+ return 0;
+
+err:
+ if (a)
+ free(a);
+ if (res)
+ free(res);
+ return rc;
+}
diff --git a/ubi-utils/src/bootenv.h b/ubi-utils/src/bootenv.h
new file mode 100644
index 0000000..9003d70
--- /dev/null
+++ b/ubi-utils/src/bootenv.h
@@ -0,0 +1,424 @@
+#ifndef __BOOTENV_H__
+#define __BOOTENV_H__
+/*
+ * 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.
+ */
+
+#include <stdio.h> /* FILE */
+#include <stdint.h>
+#include <pfiflash.h>
+
+/* DOXYGEN DOCUMENTATION */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @file bootenv.h
+ * @author oliloh@de.ibm.com
+ * @version 1.3
+ *
+ * 1.3 Some renaming
+ */
+
+/**
+ * @mainpage Usage
+ *
+ * @section intro Introduction
+ * This library provides all functionality to handle with the so-called
+ * platform description data (PDD) and the bootparameters defined in
+ * U-Boot. It is able to apply the defined PDD operations in PDD update
+ * scenarios. For more information about the PDD and bootparameter
+ * environment "bootenv" confer the PDD documentation.
+ *
+ * @section ret Return codes
+ * This library defines some return codes which will be delivered classified
+ * as warnings or errors. See the "Defines" section for details and numeric
+ * values.
+ *
+ * @section benv Bootenv format description
+ * There are two different input formats:
+ * - text files
+ * - binary files
+ *
+ * @subsection txt Text Files
+ * Text files have to be specified like:
+ * @verbatim key1=value1,value2,value7\n key2=value55,value1\n key4=value1\n@endverbatim
+ *
+ * @subsection bin Binary files
+ * Binary files have to be specified like:
+ * @verbatim<CRC32-bit>key1=value1,value2,value7\0key2=value55,value1\0... @endverbatim
+ * You can confer the U-Boot documentation for more details.
+ *
+ * @section benvlists Bootenv lists format description.
+ * Values referenced in the preceeding subsection can be
+ * defined like lists:
+ * @verbatim value1,value2,value3 @endverbatim
+ * There are some situation where a conversion of a comma
+ * seperated list can be useful, e.g. to get a list
+ * of defined PDD entries.
+ */
+
+#define BOOTENV_MAXSIZE (1024 * 100) /* max 100kiB space for bootenv */
+
+/**
+ * @def BOOTENV_ECRC
+ * @brief Given binary file is to large.
+ * @def BOOTENV_EFMT
+ * @brief Given bootenv section has an invalid format
+ * @def BOOTENV_EBADENTRY
+ * @brief Bad entry in the bootenv section.
+ * @def BOOTENV_EINVAL
+ * @brief Invalid bootenv defintion.
+ * @def BOOTENV_ENOPDD
+ * @brief Given bootenv sectoin has no PDD defintion string (pdd=...).
+ * @def BOOTENV_EPDDINVAL
+ * @brief Given bootenv section has an invalid PDD defintion.
+ * @def BOOTENV_ENOTIMPL
+ * @brief Functionality not implemented.
+ * @def BOOTENV_ECOPY
+ * @brief Bootenv memory copy error
+ * @def BOOTENV_ENOTFOUND
+ * @brief Given key has has no value.
+ * @def BOOTENV_EMAX
+ * @brief Highest error value.
+ */
+#define BOOTENV_ETOOBIG 1
+#define BOOTENV_EFMT 2
+#define BOOTENV_EBADENTRY 3
+#define BOOTENV_EINVAL 4
+#define BOOTENV_ENOPDD 5
+#define BOOTENV_EPDDINVAL 6
+#define BOOTENV_ENOTIMPL 7
+#define BOOTENV_ECOPY 8
+#define BOOTENV_ENOTFOUND 9
+#define BOOTENV_EMAX 10
+
+/**
+ * @def BOOTENV_W
+ * @brief A warning which is handled internally as an error
+ * but can be recovered by manual effort.
+ * @def BOOTENV_WPDD_STRING_DIFFERS
+ * @brief The PDD strings of old and new PDD differ and
+ * can cause update problems, because new PDD values
+ * are removed from the bootenv section completely.
+ */
+#define BOOTENV_W 20
+#define BOOTENV_WPDD_STRING_DIFFERS 21
+#define BOOTENV_WMAX 22 /* highest warning value */
+
+
+typedef struct bootenv *bootenv_t;
+ /**< A bootenv library handle. */
+
+typedef struct bootenv_list *bootenv_list_t;
+ /**< A handle for a value list. */
+
+typedef int(*pdd_func_t)(bootenv_t, bootenv_t, bootenv_t*,
+ int*, char*, size_t);
+
+
+/**
+ * @brief Get a new handle.
+ * @return 0
+ * @return or error
+ * */
+int bootenv_create(bootenv_t *env);
+
+/**
+ * @brief Cleanup structure.
+ * @param env Bootenv structure which shall be destroyed.
+ * @return 0
+ * @return or error
+ */
+int bootenv_destroy(bootenv_t *env);
+
+/**
+ * @brief Copy a bootenv handle.
+ * @param in The input bootenv.
+ * @param out The copied output bootenv. Discards old data.
+ * @return 0
+ * @return or error
+ */
+int bootenv_copy_bootenv(bootenv_t in, bootenv_t *out);
+
+/**
+ * @brief Looks for a value inside the bootenv data.
+ * @param env Handle to a bootenv structure.
+ * @param key The key.
+ * @return NULL key not found
+ * @return !NULL ptr to value
+ */
+int bootenv_get(bootenv_t env, const char *key, const char **value);
+
+
+/**
+ * @brief Looks for a value inside the bootenv data and converts it to num.
+ * @param env Handle to a bootenv structure.
+ * @param key The key.
+ * @param value A pointer to the resulting numerical value
+ * @return NULL key not found
+ * @return !NULL ptr to value
+ */
+int bootenv_get_num(bootenv_t env, const char *key, uint32_t *value);
+
+/**
+ * @brief Set a bootenv value by key.
+ * @param env Handle to a bootenv structure.
+ * @param key Key.
+ * @param value Value to set.
+ * @return 0
+ * @return or error
+ */
+int bootenv_set(bootenv_t env, const char *key, const char *value);
+
+/**
+ * @brief Remove the given key (and its value) from a bootenv structure.
+ * @param env Handle to a bootenv structure.
+ * @param key Key.
+ * @return 0
+ * @return or error
+ */
+int bootenv_unset(bootenv_t env, const char *key);
+
+
+/**
+ * @brief Get a vector of all keys which are currently set
+ * within a bootenv handle.
+ * @param env Handle to a bootenv structure.
+ * @param size The size of the allocated array structure.
+ * @param sort Flag, if set the vector is sorted ascending.
+ * @return NULL on error.
+ * @return !NULL a pointer to the first element the allocated vector.
+ * @warning Free the allocate memory yourself!
+ */
+int bootenv_get_key_vector(bootenv_t env, size_t *size, int sort,
+ const char ***vector);
+
+/**
+ * @brief Calculate the size in bytes which are necessary to write the
+ * current bootenv section in a *binary file.
+ * @param env bootenv handle.
+ * @param size The size in bytes of the bootenv handle.
+ * @return 0
+ * @return or ERROR.
+ */
+int bootenv_size(bootenv_t env, size_t *size);
+
+/**
+ * @brief Read a binary bootenv file.
+ * @param fp File pointer to input stream.
+ * @param env bootenv handle.
+ * @param size maximum data size.
+ * @return 0
+ * @return or ERROR.
+ */
+int bootenv_read(FILE* fp, bootenv_t env, size_t size);
+
+/**
+ * @param ret_crc return value of crc of read data
+ */
+int bootenv_read_crc(FILE* fp, bootenv_t env, size_t size, uint32_t *ret_crc);
+
+/**
+ * @brief Read bootenv data from an text/ascii file.
+ * @param fp File pointer to ascii PDD file.
+ * @param env bootenv handle
+ * @return 0
+ * @return or ERROR.
+ */
+int bootenv_read_txt(FILE* fp, bootenv_t env);
+
+/**
+ * @brief Write a bootenv structure to the given location (binary).
+ * @param fp Filepointer to binary file.
+ * @param env Bootenv structure which shall be written.
+ * @return 0
+ * @return or error
+ */
+int bootenv_write(FILE* fp, bootenv_t env);
+
+/**
+ * @param ret_crc return value of crc of read data
+ */
+int bootenv_write_crc(FILE* fp, bootenv_t env, uint32_t* ret_crc);
+
+/**
+ * @brief Write a bootenv structure to the given location (text).
+ * @param fp Filepointer to text file.
+ * @param env Bootenv structure which shall be written.
+ * @return 0
+ * @return or error
+ */
+int bootenv_write_txt(FILE* fp, bootenv_t env);
+
+/**
+ * @brief Prototype for a PDD handling funtion
+ */
+
+/**
+ * @brief The PDD keep operation.
+ * @param env_old The old bootenv structure.
+ * @param env_new The new bootenv structure.
+ * @param env_res The result of PDD keep.
+ * @param warnings A flag which marks any warnings.
+ * @return 0
+ * @return or error
+ * @note For a complete documentation about the algorithm confer the
+ * PDD documentation.
+ */
+int bootenv_pdd_keep(bootenv_t env_old, bootenv_t env_new,
+ bootenv_t *env_res, int *warnings,
+ char *err_buf, size_t err_buf_size);
+
+
+/**
+ * @brief The PDD merge operation.
+ * @param env_old The old bootenv structure.
+ * @param env_new The new bootenv structure.
+ * @param env_res The result of merge-pdd.
+ * @param warnings A flag which marks any warnings.
+ * @return 0
+ * @return or error
+ * @note For a complete documentation about the algorithm confer the
+ * PDD documentation.
+ */
+int bootenv_pdd_merge(bootenv_t env_old, bootenv_t env_new,
+ bootenv_t *env_res, int *warnings,
+ char *err_buf, size_t err_buf_size);
+
+/**
+ * @brief The PDD overwrite operation.
+ * @param env_old The old bootenv structure.
+ * @param env_new The new bootenv structure.
+ * @param env_res The result of overwrite-pdd.
+ * @param warnings A flag which marks any warnings.
+ * @return 0
+ * @return or error
+ * @note For a complete documentation about the algorithm confer the
+ * PDD documentation.
+ */
+int bootenv_pdd_overwrite(bootenv_t env_new,
+ bootenv_t env_old, bootenv_t *env_res, int *warnings,
+ char *err_buf, size_t err_buf_size);
+
+/**
+ * @brief Dump a bootenv structure to stdout. (Debug)
+ * @param env Handle to a bootenv structure.
+ * @return 0
+ * @return or error
+ */
+int bootenv_dump(bootenv_t env);
+
+/**
+ * @brief Validate a bootenv structure.
+ * @param env Handle to a bootenv structure.
+ * @return 0
+ * @return or error
+ */
+int bootenv_valid(bootenv_t env);
+
+/**
+ * @brief Create a new bootenv list structure.
+ * @return NULL on error
+ * @return or a new list handle.
+ * @note This structure is used to store values in a list.
+ * A useful addition when handling PDD strings.
+ */
+int bootenv_list_create(bootenv_list_t *list);
+
+/**
+ * @brief Destroy a bootenv list structure
+ * @param list Handle to a bootenv list structure.
+ * @return 0
+ * @return or error
+ */
+int bootenv_list_destroy(bootenv_list_t *list);
+
+/**
+ * @brief Import a list from a comma seperated string
+ * @param list Handle to a bootenv list structure.
+ * @param str Comma seperated string list.
+ * @return 0
+ * @return or error
+ */
+int bootenv_list_import(bootenv_list_t list, const char *str);
+
+/**
+ * @brief Export a list to a string of comma seperated values.
+ * @param list Handle to a bootenv list structure.
+ * @return NULL one error
+ * @return or pointer to a newly allocated string.
+ * @warning Free the allocated memory by yourself!
+ */
+int bootenv_list_export(bootenv_list_t list, char **string);
+
+/**
+ * @brief Add an item to the list.
+ * @param list A handle of a list structure.
+ * @param item An item.
+ * @return 0
+ * @return or error
+ */
+int bootenv_list_add(bootenv_list_t list, const char *item);
+
+/**
+ * @brief Remove an item from the list.
+ * @param list A handle of a list structure.
+ * @param item An item.
+ * @return 0
+ * @return or error
+ */
+int bootenv_list_remove(bootenv_list_t list, const char *item);
+
+/**
+ * @brief Check if a given item is in a given list.
+ * @param list A handle of a list structure.
+ * @param item An item.
+ * @return 1 Item is in list.
+ * @return 0 Item is not in list.
+ */
+int bootenv_list_is_in(bootenv_list_t list, const char *item);
+
+
+/**
+ * @brief Convert a list into a vector of all values inside the list.
+ * @param list Handle to a bootenv structure.
+ * @param size The size of the allocated vector structure.
+ * @return 0
+ * @return or error
+ * @warning Free the allocate memory yourself!
+ */
+int bootenv_list_to_vector(bootenv_list_t list, size_t *size,
+ const char ***vector);
+
+/**
+ * @brief Convert a list into a vector of all values inside the list.
+ * @param list Handle to a bootenv structure.
+ * @param size The size of the allocated vector structure.
+ * @return 0
+ * @return or error
+ * @warning Free the allocate memory yourself!
+ */
+int bootenv_list_to_num_vector(bootenv_list_t list, size_t *size,
+ uint32_t **vector);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /*__BOOTENV_H__ */
diff --git a/ubi-utils/src/config.h b/ubi-utils/src/config.h
new file mode 100644
index 0000000..8c4dd54
--- /dev/null
+++ b/ubi-utils/src/config.h
@@ -0,0 +1,28 @@
+#ifndef __CONFIG_H__
+#define __CONFIG_H__
+/*
+ * 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.
+ *
+ * Author: Frank Haverkamp
+ */
+
+#define PACKAGE_BUGREPORT \
+ "haver@vnet.ibm.com, dedekind@linutronix.de, or tglx@linutronix.de"
+
+#define __unused __attribute__((unused))
+
+#endif
diff --git a/ubi-utils/src/crc32.c b/ubi-utils/src/crc32.c
new file mode 100644
index 0000000..666e217
--- /dev/null
+++ b/ubi-utils/src/crc32.c
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ *
+ * Author: Thomas Gleixner
+ */
+
+/*
+ * CRC32 functions
+ *
+ * Can be compiled as seperate object, but is included into the ipl source
+ * so gcc can inline the functions. We optimize for size so the omission of
+ * the function frame is helpful.
+ *
+ */
+
+#include <stdint.h>
+#include <crc32.h>
+
+/* CRC polynomial */
+#define CRC_POLY 0xEDB88320
+
+/**
+ * init_crc32_table - Initialize crc table
+ *
+ * @table: pointer to the CRC table which must be initialized
+ *
+ * Create CRC32 table for given polynomial. The table is created with
+ * the lowest order term in the highest order bit. So the x^32 term
+ * has to implied in the crc calculation function.
+ */
+void init_crc32_table(uint32_t *table)
+{
+ uint32_t crc;
+ int i, j;
+
+ for (i = 0; i < 256; i++) {
+ crc = i;
+ for (j = 8; j > 0; j--) {
+ if (crc & 1)
+ crc = (crc >> 1) ^ CRC_POLY;
+ else
+ crc >>= 1;
+ }
+ table[i] = crc;
+ }
+}
+
+/**
+ * clc_crc32 - Calculate CRC32 over a buffer
+ *
+ * @table: pointer to the CRC table
+ * @crc: initial crc value
+ * @buf: pointer to the buffer
+ * @len: number of bytes to calc
+ *
+ * Returns the updated crc value.
+ *
+ * The algorithm resembles a hardware shift register, but calculates 8
+ * bit at once.
+ */
+uint32_t clc_crc32(uint32_t *table, uint32_t crc, void *buf,
+ int len)
+{
+ const unsigned char *p = buf;
+
+ while(--len >= 0)
+ crc = table[(crc ^ *p++) & 0xff] ^ (crc >> 8);
+ return crc;
+}
diff --git a/ubi-utils/src/crc32.h b/ubi-utils/src/crc32.h
new file mode 100644
index 0000000..31362b0
--- /dev/null
+++ b/ubi-utils/src/crc32.h
@@ -0,0 +1,36 @@
+#ifndef __CRC32_H__
+#define __CRC32_H__
+/*
+ * 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.
+ */
+
+/*
+ * Author: Thomas Gleixner
+ *
+ * CRC32 functions
+ *
+ * Can be compiled as seperate object, but is included into the ipl source
+ * so gcc can inline the functions. We optimize for size so the omission of
+ * the function frame is helpful.
+ *
+ */
+#include <stdint.h>
+
+void init_crc32_table(uint32_t *table);
+uint32_t clc_crc32(uint32_t *table, uint32_t crc, void *buf, int len);
+
+#endif /* __CRC32_H__ */
diff --git a/ubi-utils/src/eb_chain.c b/ubi-utils/src/eb_chain.c
new file mode 100644
index 0000000..501a838
--- /dev/null
+++ b/ubi-utils/src/eb_chain.c
@@ -0,0 +1,287 @@
+/*
+ * 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.
+ */
+
+/*
+ * Author: Drake Dowsett, dowsett@de.ibm.com
+ * Contact: Andreas Arnez, arnez@de.ibm.com
+ */
+
+/* see eb_chain.h */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include "eb_chain.h"
+
+#define COPY(dst, src) \
+ do \
+ { \
+ dst = malloc(sizeof(*dst)); \
+ if (dst == NULL) \
+ return -ENOMEM; \
+ memcpy(dst, src, sizeof(*dst)); \
+ } while (0)
+
+
+/**
+ * inserts an eb_info into the chain starting at head, then searching
+ * linearly for the correct position;
+ * new should contain valid vid and ec headers and the data_crc should
+ * already have been checked before insertion, otherwise the chain
+ * could be have un an undesired manner;
+ * returns -ENOMEM if alloc fails, otherwise SHOULD always return 0,
+ * if not, the code reached the last line and returned -EAGAIN,
+ * meaning there is a bug or a case not being handled here;
+ **/
+int
+eb_chain_insert(eb_info_t *head, eb_info_t new)
+{
+ uint32_t vol, num, ver;
+ uint32_t new_vol, new_num, new_ver;
+ eb_info_t prev, cur, hist, ins;
+ eb_info_t *prev_ptr;
+
+ if ((head == NULL) || (new == NULL))
+ return 0;
+
+ if (*head == NULL)
+ {
+ COPY(*head, new);
+ (*head)->next = NULL;
+ return 0;
+ }
+
+ new_vol = ubi32_to_cpu(new->inner.vol_id);
+ new_num = ubi32_to_cpu(new->inner.lnum);
+ new_ver = ubi32_to_cpu(new->inner.leb_ver);
+
+ /** TRAVERSE HORIZONTALY **/
+
+ cur = *head;
+ prev = NULL;
+
+ /* traverse until vol_id/lnum align */
+ vol = ubi32_to_cpu(cur->inner.vol_id);
+ num = ubi32_to_cpu(cur->inner.lnum);
+ while ((new_vol > vol) || ((new_vol == vol) && (new_num > num)))
+ {
+ /* insert new at end of chain */
+ if (cur->next == NULL)
+ {
+ COPY(ins, new);
+ ins->next = NULL;
+ cur->next = ins;
+ return 0;
+ }
+
+ prev = cur;
+ cur = cur->next;
+ vol = ubi32_to_cpu(cur->inner.vol_id);
+ num = ubi32_to_cpu(cur->inner.lnum);
+ }
+
+ if (prev == NULL)
+ prev_ptr = head;
+ else
+ prev_ptr = &(prev->next);
+
+ /* insert new into the middle of chain */
+ if ((new_vol != vol) || (new_num != num))
+ {
+ COPY(ins, new);
+ ins->next = cur;
+ *prev_ptr = ins;
+ return 0;
+ }
+
+ /** TRAVERSE VERTICALY **/
+
+ hist = cur;
+ prev = NULL;
+
+ /* traverse until versions align */
+ ver = ubi32_to_cpu(cur->inner.leb_ver);
+ while (new_ver < ver)
+ {
+ /* insert new at bottom of history */
+ if (hist->older == NULL)
+ {
+ COPY(ins, new);
+ ins->next = NULL;
+ ins->older = NULL;
+ hist->older = ins;
+ return 0;
+ }
+
+ prev = hist;
+ hist = hist->older;
+ ver = ubi32_to_cpu(hist->inner.leb_ver);
+ }
+
+ if (prev == NULL)
+ {
+ /* replace active version */
+ COPY(ins, new);
+ ins->next = hist->next;
+ *prev_ptr = ins;
+
+ /* place cur in vertical histroy */
+ ins->older = hist;
+ hist->next = NULL;
+ return 0;
+ }
+ else
+ {
+ /* insert between versions, beneath active version */
+ COPY(ins, new);
+ ins->next = NULL;
+ ins->older = prev->older;
+ prev->older = ins;
+ return 0;
+ }
+
+ /* logically impossible to reach this point... hopefully */
+ return -EAGAIN;
+}
+
+
+/**
+ * sets the pointer at pos to the position of the first entry in the chain
+ * with of vol_id and, if given, with the same lnum as *lnum;
+ * if there is no entry in the chain, then *pos is NULL on return;
+ * always returns 0;
+ **/
+int
+eb_chain_position(eb_info_t *head, uint32_t vol_id, uint32_t *lnum,
+ eb_info_t *pos)
+{
+ uint32_t vol, num;
+ eb_info_t cur;
+
+ if ((head == NULL) || (*head == NULL) || (pos == NULL))
+ return 0;
+
+ *pos = NULL;
+
+ cur = *head;
+ while (cur != NULL)
+ {
+ vol = ubi32_to_cpu(cur->inner.vol_id);
+ num = ubi32_to_cpu(cur->inner.lnum);
+
+ if (vol_id == vol)
+ if ((lnum == NULL) || (*lnum == num))
+ {
+ *pos = cur;
+ return 0;
+ }
+
+ cur = cur->next;
+ }
+
+ return 0;
+}
+
+
+/**
+ * prints to stream, the vol_id, lnum and leb_ver for each entry in the
+ * chain, starting at head;
+ * this is intended for debuging purposes;
+ * always returns 0;
+ **/
+int
+eb_chain_print(FILE* stream, eb_info_t *head)
+{
+ eb_info_t cur;
+
+ if (head == NULL)
+ return 0;
+
+ if (stream == NULL)
+ stream = stdout;
+
+ if (*head == NULL)
+ {
+ fprintf(stream, "EMPTY\n");
+ return 0;
+ }
+
+ cur = *head;
+ while (cur != NULL)
+ {
+ eb_info_t hist;
+
+ fprintf(stream, " VOL %4u-%04u | VER 0x%8x\n",
+ ubi32_to_cpu(cur->inner.vol_id),
+ ubi32_to_cpu(cur->inner.lnum),
+ ubi32_to_cpu(cur->inner.leb_ver));
+
+ hist = cur->older;
+ while (hist != NULL)
+ {
+ fprintf(stream, "+ VOL %4u-%04u | VER 0x%8x\n",
+ ubi32_to_cpu(hist->inner.vol_id),
+ ubi32_to_cpu(hist->inner.lnum),
+ ubi32_to_cpu(hist->inner.leb_ver));
+
+ hist = hist->older;
+ }
+
+ cur = cur->next;
+ }
+
+ return 0;
+}
+
+
+/**
+ * frees the memory of the entire chain, starting at head;
+ * head will be NULL on return;
+ * always returns 0;
+ **/
+int
+eb_chain_destroy(eb_info_t *head)
+{
+ if (head == NULL)
+ return 0;
+
+ while (*head != NULL)
+ {
+ eb_info_t cur;
+ eb_info_t hist;
+
+ cur = *head;
+ *head = (*head)->next;
+
+ hist = cur->older;
+ while (hist != NULL)
+ {
+ eb_info_t temp;
+
+ temp = hist;
+ hist = hist->older;
+
+ free(temp);
+ }
+
+ free(cur);
+ }
+
+ return 0;
+}
+
diff --git a/ubi-utils/src/eb_chain.h b/ubi-utils/src/eb_chain.h
new file mode 100644
index 0000000..f640c54
--- /dev/null
+++ b/ubi-utils/src/eb_chain.h
@@ -0,0 +1,73 @@
+#ifndef __EB_CHAIN_H__
+#define __EB_CHAIN_H__
+
+/*
+ * 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.
+ */
+
+/*
+ * Author: Drake Dowsett
+ * Contact: Andreas Arnez (arnez@de.ibm.com)
+ *
+ * Eraseblock Chain
+ *
+ * A linked list structure to order eraseblocks by volume and logical number
+ * and to update by version number. Doesn't contain actual eraseblock data
+ * but rather the erasecounter and volume id headers as well as a position
+ * indicator.
+ *
+ * Diagram Example:
+ *
+ * [V1.0v0]->[V1.1v2]->[V1.2v1]->[V2.0v2]->[V2.1v0]->[V2.2v1]->NULL
+ * | | | | | |
+ * NULL [V1.1v1] [V1.2v0] [V2.0v1] NULL [V2.2v0]
+ * | | | |
+ * [V1.1v0] NULL [V2.0v0] NULL
+ * | |
+ * NULL NULL
+ *
+ * [VA.BvC] represents the eb_info for the eraseblock with the vol_id A,
+ * lnum B and leb_ver C
+ * -> represents the `next' pointer
+ * | represents the `older' pointer
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <mtd/ubi-header.h>
+
+typedef struct eb_info *eb_info_t;
+struct eb_info {
+ struct ubi_ec_hdr outer;
+ struct ubi_vid_hdr inner;
+ fpos_t eb_top;
+ uint32_t linear;
+
+ eb_info_t next;
+ eb_info_t older;
+};
+
+int eb_chain_insert(eb_info_t *head, eb_info_t item);
+
+int eb_chain_position(eb_info_t *head, uint32_t vol_id, uint32_t *lnum,
+ eb_info_t *pos);
+
+int eb_chain_print(FILE *stream, eb_info_t *head);
+
+int eb_chain_destroy(eb_info_t *head);
+
+#endif /* __EB_CHAIN_H__ */
diff --git a/ubi-utils/src/error.c b/ubi-utils/src/error.c
new file mode 100644
index 0000000..4aaedad
--- /dev/null
+++ b/ubi-utils/src/error.c
@@ -0,0 +1,240 @@
+/*
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <syslog.h>
+#include <stdlib.h>
+#include <sys/errno.h>
+#include <string.h>
+#include "error.h"
+
+#define MAXLINE 4096
+#define MAXWIDTH 80
+
+static FILE *logfp = NULL;
+
+static void err_doit(int, int, const char *, va_list);
+
+int
+read_procfile(FILE *fp_out, const char *procfile)
+{
+ FILE *fp;
+
+ if (!fp_out)
+ return -ENXIO;
+
+ fp = fopen(procfile, "r");
+ if (!fp)
+ return -ENOENT;
+
+ while(!feof(fp)) {
+ int c = fgetc(fp);
+
+ if (c == EOF)
+ return 0;
+
+ if (putc(c, fp_out) == EOF)
+ return -EIO;
+
+ if (ferror(fp))
+ return -EIO;
+ }
+ return fclose(fp);
+}
+
+void
+error_initlog(const char *logfile)
+{
+ if (!logfile)
+ return;
+
+ logfp = fopen(logfile, "a+");
+ read_procfile(logfp, "/proc/cpuinfo");
+}
+
+void
+info_msg(const char *fmt, ...)
+{
+ FILE* fpout;
+ char buf[MAXLINE + 1];
+ va_list ap;
+ int n;
+
+ fpout = stdout;
+
+ va_start(ap, fmt);
+ vsnprintf(buf, MAXLINE, fmt, ap);
+ n = strlen(buf);
+ strcat(buf, "\n");
+
+ fputs(buf, fpout);
+ fflush(fpout);
+ if (fpout != stdout)
+ fclose(fpout);
+
+ va_end(ap);
+ return;
+}
+
+void
+__err_ret(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ err_doit(1, LOG_INFO, fmt, ap);
+ va_end(ap);
+ return;
+}
+
+void
+__err_sys(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ err_doit(1, LOG_ERR, fmt, ap);
+ va_end(ap);
+ exit(EXIT_FAILURE);
+}
+
+
+void
+__err_msg(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ err_doit(0, LOG_INFO, fmt, ap);
+ va_end(ap);
+
+ return;
+}
+
+void
+__err_quit(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ err_doit(0, LOG_ERR, fmt, ap);
+ va_end(ap);
+ exit(EXIT_FAILURE);
+}
+
+void
+__err_dump(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ err_doit(1, LOG_ERR, fmt, ap);
+ va_end(ap);
+ abort(); /* dump core and terminate */
+ exit(EXIT_FAILURE); /* shouldn't get here */
+}
+
+/**
+ * If a logfile is used we must not print on stderr and stdout
+ * anymore. Since pfilfash might be used in a server context, it is
+ * even dangerous to write to those descriptors.
+ */
+static void
+err_doit(int errnoflag, int level __attribute__((unused)),
+ const char *fmt, va_list ap)
+{
+ FILE* fpout;
+ int errno_save, n;
+ char buf[MAXLINE + 1];
+ fpout = stderr;
+
+ errno_save = errno; /* value caller might want printed */
+
+ vsnprintf(buf, MAXLINE, fmt, ap); /* safe */
+
+ n = strlen(buf);
+
+ if (errnoflag)
+ snprintf(buf + n, MAXLINE - n, ": %s", strerror(errno_save));
+ strcat(buf, "\n");
+
+ if (logfp) {
+ fputs(buf, logfp);
+ fflush(logfp);
+ return; /* exit when logging completes */
+ }
+
+ if (fpout == stderr) {
+ /* perform line wrap when outputting to stderr */
+ int word_len, post_len, chars;
+ char *buf_ptr;
+ const char *frmt = "%*s%n %n";
+
+ chars = 0;
+ buf_ptr = buf;
+ while (sscanf(buf_ptr, frmt, &word_len, &post_len) != EOF) {
+ int i;
+ char word[word_len + 1];
+ char post[post_len + 1];
+
+ strncpy(word, buf_ptr, word_len);
+ word[word_len] = '\0';
+ buf_ptr += word_len;
+ post_len -= word_len;
+
+ if (chars + word_len > MAXWIDTH) {
+ fputc('\n', fpout);
+ chars = 0;
+ }
+ fputs(word, fpout);
+ chars += word_len;
+
+ if (post_len > 0) {
+ strncpy(post, buf_ptr, post_len);
+ post[post_len] = '\0';
+ buf_ptr += post_len;
+ }
+ for (i = 0; i < post_len; i++) {
+ int inc = 1, chars_new;
+
+ if (post[i] == '\t')
+ inc = 8;
+ if (post[i] == '\n') {
+ inc = 0;
+ chars_new = 0;
+ } else
+ chars_new = chars + inc;
+
+ if (chars_new > MAXWIDTH) {
+ fputc('\n', fpout);
+ chars_new = inc;
+ }
+ fputc(post[i], fpout);
+ chars = chars_new;
+ }
+ }
+ }
+ else
+ fputs(buf, fpout);
+ fflush(fpout);
+ if (fpout != stderr)
+ fclose(fpout);
+
+ return;
+}
diff --git a/ubi-utils/src/error.h b/ubi-utils/src/error.h
new file mode 100644
index 0000000..05d8078
--- /dev/null
+++ b/ubi-utils/src/error.h
@@ -0,0 +1,84 @@
+#ifndef __ERROR_H__
+#define __ERROR_H__
+/*
+ * 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.
+ */
+
+#include <stdio.h>
+
+void error_initlog(const char *logfile);
+int read_procfile(FILE *fp_out, const char *procfile);
+
+void __err_ret(const char *fmt, ...);
+void __err_sys(const char *fmt, ...);
+void __err_msg(const char *fmt, ...);
+void __err_quit(const char *fmt, ...);
+void __err_dump(const char *fmt, ...);
+
+void info_msg(const char *fmt, ...);
+
+#ifdef DEBUG
+#define __loc_msg(str) do { \
+ __err_msg("[%s. FILE: %s FUNC: %s LINE: %d]\n", \
+ str, __FILE__, __FUNCTION__, __LINE__); \
+} while (0)
+#else
+#define __loc_msg(str)
+#endif
+
+
+#define err_dump(fmt, ...) do { \
+ __loc_msg("ErrDump"); \
+ __err_dump(fmt, ##__VA_ARGS__); \
+} while (0)
+
+#define err_quit(fmt, ...) do { \
+ __loc_msg("ErrQuit"); \
+ __err_quit(fmt, ##__VA_ARGS__); \
+} while (0)
+
+
+#define err_ret(fmt, ...) do { \
+ __loc_msg("ErrRet"); \
+ __err_ret(fmt, ##__VA_ARGS__); \
+} while (0)
+
+#define err_sys(fmt, ...) do { \
+ __loc_msg("ErrSys"); \
+ __err_sys(fmt, ##__VA_ARGS__); \
+} while (0)
+
+#define err_msg(fmt, ...) do { \
+ __loc_msg("ErrMsg"); \
+ __err_msg(fmt, ##__VA_ARGS__); \
+} while (0)
+
+#define log_msg(fmt, ...) do { \
+ /* __loc_msg("LogMsg"); */ \
+ __err_msg(fmt, ##__VA_ARGS__); \
+} while (0)
+
+#ifdef DEBUG
+#define dbg_msg(fmt, ...) do { \
+ __loc_msg("DbgMsg"); \
+ __err_msg(fmt, ##__VA_ARGS__); \
+} while (0)
+#else
+#define dbg_msg(fmt, ...) do {} while (0)
+#endif
+
+#endif /* __ERROR_H__ */
diff --git a/ubi-utils/src/example_ubi.h b/ubi-utils/src/example_ubi.h
new file mode 100644
index 0000000..23c7b54
--- /dev/null
+++ b/ubi-utils/src/example_ubi.h
@@ -0,0 +1,28 @@
+#ifndef __EXAMPLE_UBI_H__
+#define __EXAMPLE_UBI_H__
+/*
+ * 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.
+ */
+
+/**
+ * Defaults for our cards.
+ */
+#define EXAMPLE_UBI_DEVICE 0
+#define EXAMPLE_BOOTENV_VOL_ID_1 4
+#define EXAMPLE_BOOTENV_VOL_ID_2 5
+
+#endif /* __EXAMPLE_UBI_H__ */
diff --git a/ubi-utils/src/hashmap.c b/ubi-utils/src/hashmap.c
new file mode 100644
index 0000000..3511d56
--- /dev/null
+++ b/ubi-utils/src/hashmap.c
@@ -0,0 +1,412 @@
+/*
+ * 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.
+ *
+ * Author: Oliver Lohmann
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "error.h"
+#include "hashmap.h"
+#define DEFAULT_BUCKETS 4096
+
+#if 0
+#define INFO_MSG(fmt...) do { \
+ info_msg(fmt); \
+} while (0)
+#else
+#define INFO_MSG(fmt...)
+#endif
+
+struct hashentry {
+ char* key; /* key '0' term. str */
+ char* value; /* payload '0' term. str */
+
+ hashentry_t next;
+};
+
+struct hashmap {
+ size_t entries; /* current #entries */
+ size_t maxsize; /* no. of hash buckets */
+ hashentry_t* data; /* array of buckets */
+};
+
+static int
+is_empty(hashentry_t l)
+{
+ return l == NULL ? 1 : 0;
+}
+
+hashmap_t
+hashmap_new(void)
+{
+ hashmap_t res;
+ res = (hashmap_t) calloc(1, sizeof(struct hashmap));
+
+ if (res == NULL)
+ return NULL;
+
+ res->maxsize = DEFAULT_BUCKETS;
+ res->entries = 0;
+
+ res->data = (hashentry_t*)
+ calloc(1, res->maxsize * sizeof(struct hashentry));
+
+ if (res->data == NULL)
+ return NULL;
+
+ return res;
+}
+
+static hashentry_t
+new_entry(const char* key, const char* value)
+{
+ hashentry_t res;
+
+ res = (hashentry_t) calloc(1, sizeof(struct hashentry));
+
+ if (res == NULL)
+ return NULL;
+
+ /* allocate key and value and copy them */
+ res->key = strdup(key);
+ if (res->key == NULL) {
+ free(res);
+ return NULL;
+ }
+
+ res->value = strdup(value);
+ if (res->value == NULL) {
+ free(res->key);
+ free(res);
+ return NULL;
+ }
+
+ res->next = NULL;
+
+ return res;
+}
+
+static hashentry_t
+free_entry(hashentry_t e)
+{
+ if (!is_empty(e)) {
+ if(e->key != NULL) {
+ free(e->key);
+ }
+ if(e->value != NULL)
+ free(e->value);
+ free(e);
+ }
+
+ return NULL;
+}
+
+static hashentry_t
+remove_entry(hashentry_t l, const char* key, size_t* entries)
+{
+ hashentry_t lnext;
+ if (is_empty(l))
+ return NULL;
+
+ if(strcmp(l->key,key) == 0) {
+ lnext = l->next;
+ l = free_entry(l);
+ (*entries)--;
+ return lnext;
+ }
+
+ l->next = remove_entry(l->next, key, entries);
+
+ return l;
+}
+
+static hashentry_t
+insert_entry(hashentry_t l, hashentry_t e, size_t* entries)
+{
+ if (is_empty(l)) {
+ (*entries)++;
+ return e;
+ }
+
+ /* check for update */
+ if (strcmp(l->key, e->key) == 0) {
+ e->next = l->next;
+ l = free_entry(l);
+ return e;
+ }
+
+ l->next = insert_entry(l->next, e, entries);
+ return l;
+}
+
+static hashentry_t
+remove_all(hashentry_t l, size_t* entries)
+{
+ hashentry_t lnext;
+ if (is_empty(l))
+ return NULL;
+
+ lnext = l->next;
+ free_entry(l);
+ (*entries)--;
+
+ return remove_all(lnext, entries);
+}
+
+static const char*
+value_lookup(hashentry_t l, const char* key)
+{
+ if (is_empty(l))
+ return NULL;
+
+ if (strcmp(l->key, key) == 0)
+ return l->value;
+
+ return value_lookup(l->next, key);
+}
+
+static void
+print_all(hashentry_t l)
+{
+ if (is_empty(l)) {
+ printf("\n");
+ return;
+ }
+
+ printf("%s=%s", l->key, l->value);
+ if (!is_empty(l->next)) {
+ printf(",");
+ }
+
+ print_all(l->next);
+}
+
+static void
+keys_to_array(hashentry_t l, const char** a, size_t* i)
+{
+ if (is_empty(l))
+ return;
+
+ a[*i] = l->key;
+ (*i)++;
+
+ keys_to_array(l->next, a, i);
+}
+
+uint32_t
+hash_str(const char* str, uint32_t mapsize)
+{
+ uint32_t hash = 0;
+ uint32_t x = 0;
+ uint32_t i = 0;
+ size_t len = strlen(str);
+
+ for(i = 0; i < len; str++, i++) {
+ hash = (hash << 4) + (*str);
+ if((x = hash & 0xF0000000L) != 0) {
+ hash ^= (x >> 24);
+ hash &= ~x;
+ }
+ }
+
+ return (hash & 0x7FFFFFFF) % mapsize;
+}
+
+
+int
+hashmap_is_empty(hashmap_t map)
+{
+ if (map == NULL)
+ return -EINVAL;
+
+ return map->entries > 0 ? 1 : 0;
+}
+
+const char*
+hashmap_lookup(hashmap_t map, const char* key)
+{
+ uint32_t i;
+
+ if ((map == NULL) || (key == NULL))
+ return NULL;
+
+ i = hash_str(key, map->maxsize);
+
+ return value_lookup(map->data[i], key);
+}
+
+int
+hashmap_add(hashmap_t map, const char* key, const char* value)
+{
+ uint32_t i;
+ hashentry_t entry;
+
+ if ((map == NULL) || (key == NULL) || (value == NULL))
+ return -EINVAL;
+
+ i = hash_str(key, map->maxsize);
+ entry = new_entry(key, value);
+ if (entry == NULL)
+ return -ENOMEM;
+
+ map->data[i] = insert_entry(map->data[i],
+ entry, &map->entries);
+
+ INFO_MSG("HASH_ADD: chain[%d] key:%s val:%s",i, key, value);
+ return 0;
+}
+
+int
+hashmap_remove(hashmap_t map, const char* key)
+{
+ uint32_t i;
+
+ if ((map == NULL) || (key == NULL))
+ return -EINVAL;
+
+ i = hash_str(key, map->maxsize);
+ map->data[i] = remove_entry(map->data[i], key, &map->entries);
+
+ return 0;
+}
+
+size_t
+hashmap_size(hashmap_t map)
+{
+ if (map != NULL)
+ return map->entries;
+ else
+ return 0;
+}
+
+int
+hashmap_free(hashmap_t map)
+{
+ size_t i;
+
+ if (map == NULL)
+ return -EINVAL;
+
+ /* "children" first */
+ for(i = 0; i < map->maxsize; i++) {
+ map->data[i] = remove_all(map->data[i], &map->entries);
+ }
+ free(map->data);
+ free(map);
+
+ return 0;
+}
+
+int
+hashmap_dump(hashmap_t map)
+{
+ size_t i;
+ if (map == NULL)
+ return -EINVAL;
+
+ for(i = 0; i < map->maxsize; i++) {
+ if (map->data[i] != NULL) {
+ printf("[%zd]: ", i);
+ print_all(map->data[i]);
+ }
+ }
+
+ return 0;
+}
+
+static const char**
+sort_key_vector(const char** a, size_t size)
+{
+ /* uses bubblesort */
+ size_t i, j;
+ const char* tmp;
+
+ if (size <= 0)
+ return a;
+
+ for (i = size - 1; i > 0; i--) {
+ for (j = 0; j < i; j++) {
+ if (strcmp(a[j], a[j+1]) > 0) {
+ tmp = a[j];
+ a[j] = a[j+1];
+ a[j+1] = tmp;
+ }
+ }
+ }
+ return a;
+}
+
+const char**
+hashmap_get_key_vector(hashmap_t map, size_t* size, int sort)
+{
+ const char** res;
+ size_t i, j;
+ *size = map->entries;
+
+ res = (const char**) malloc(*size * sizeof(char*));
+ if (res == NULL)
+ return NULL;
+
+ j = 0;
+ for(i=0; i < map->maxsize; i++) {
+ keys_to_array(map->data[i], res, &j);
+ }
+
+ if (sort)
+ res = sort_key_vector(res, *size);
+
+ return res;
+}
+
+int
+hashmap_key_is_in_vector(const char** vec, size_t size, const char* key)
+{
+ size_t i;
+ for (i = 0; i < size; i++) {
+ if (strcmp(vec[i], key) == 0) /* found */
+ return 1;
+ }
+
+ return 0;
+}
+
+const char**
+hashmap_get_update_key_vector(const char** vec1, size_t vec1_size,
+ const char** vec2, size_t vec2_size, size_t* res_size)
+{
+ const char** res;
+ size_t i, j;
+
+ *res_size = vec2_size;
+
+ res = (const char**) malloc(*res_size * sizeof(char*));
+ if (res == NULL)
+ return NULL;
+
+ /* get all keys from vec2 which are not set in vec1 */
+ j = 0;
+ for (i = 0; i < vec2_size; i++) {
+ if (!hashmap_key_is_in_vector(vec1, vec1_size, vec2[i]))
+ res[j++] = vec2[i];
+ }
+
+ *res_size = j;
+ return res;
+}
diff --git a/ubi-utils/src/hashmap.h b/ubi-utils/src/hashmap.h
new file mode 100644
index 0000000..1b13e95
--- /dev/null
+++ b/ubi-utils/src/hashmap.h
@@ -0,0 +1,49 @@
+#ifndef __HASHMAP_H__
+#define __HASHMAP_H__
+/*
+ * 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.
+ *
+ * Author: Oliver Lohmann
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+
+typedef struct hashentry *hashentry_t;
+typedef struct hashmap *hashmap_t;
+
+hashmap_t hashmap_new(void);
+int hashmap_free(hashmap_t map);
+
+int hashmap_add(hashmap_t map, const char* key, const char* value);
+int hashmap_update(hashmap_t map, const char* key, const char* value);
+int hashmap_remove(hashmap_t map, const char* key);
+const char* hashmap_lookup(hashmap_t map, const char* key);
+
+const char** hashmap_get_key_vector(hashmap_t map, size_t* size, int sort);
+int hashmap_key_is_in_vector(const char** vec, size_t size, const char* key);
+const char** hashmap_get_update_key_vector(const char** vec1, size_t vec1_size,
+ const char** vec2, size_t vec2_size, size_t* res_size);
+
+int hashmap_dump(hashmap_t map);
+
+int hashmap_is_empty(hashmap_t map);
+size_t hashmap_size(hashmap_t map);
+
+uint32_t hash_str(const char* str, uint32_t mapsize);
+
+#endif /* __HASHMAP_H__ */
diff --git a/ubi-utils/src/libpfiflash.c b/ubi-utils/src/libpfiflash.c
new file mode 100644
index 0000000..4f1f5cd
--- /dev/null
+++ b/ubi-utils/src/libpfiflash.c
@@ -0,0 +1,1076 @@
+/*
+ * 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.
+ */
+
+/*
+ * Authors: Oliver Lohmann <oliloh@de.ibm.com>
+ * Drake Dowsett <dowsett@de.ibm.com>
+ * Contact: Andreas Arnez <anrez@de.ibm.com>
+ */
+
+/* TODO Compare data before writing it. This implies that the volume
+ * parameters are compared first: size, alignment, name, type, ...,
+ * this is the same, compare the data. Volume deletion is deffered
+ * until the difference has been found out.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#define __USE_GNU
+#include <string.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+
+#include <libubiold.h>
+#include <pfiflash.h>
+
+#include <mtd/ubi-user.h> /* FIXME Is this ok here? */
+#include <mtd/mtd-user.h>
+
+#include "pfiflash_error.h"
+#include "ubimirror.h"
+#include "error.h"
+#include "reader.h"
+#include "example_ubi.h"
+#include "bootenv.h"
+
+/* ubi-header.h and crc32.h needed for CRC checking */
+#include <mtd/ubi-header.h> /* FIXME Is this ok here? */
+#include "crc32.h"
+
+#define __unused __attribute__((unused))
+
+static const char copyright [] __unused =
+ "Copyright (c) International Business Machines Corp., 2006";
+
+/* simply clear buffer, then write into front of it */
+#define EBUF(fmt...) \
+ snprintf(err_buf, err_buf_size, fmt);
+
+/* make a history of buffer and then prepend something in front */
+#define EBUF_PREPEND(fmt) \
+ do { \
+ int EBUF_HISTORY_LENGTH = strlen(err_buf); \
+ char EBUF_HISTORY[EBUF_HISTORY_LENGTH + 1]; \
+ strncpy(EBUF_HISTORY, err_buf, EBUF_HISTORY_LENGTH + 1);\
+ EBUF(fmt ": %s", EBUF_HISTORY); \
+ } while (0)
+
+/* An array of PDD function pointers indexed by the algorithm. */
+static pdd_func_t pdd_funcs[PDD_HANDLING_NUM] =
+ {
+ &bootenv_pdd_keep,
+ &bootenv_pdd_merge,
+ &bootenv_pdd_overwrite
+ };
+
+typedef enum ubi_update_process_t {
+ UBI_REMOVE = 0,
+ UBI_WRITE,
+} ubi_update_process_t;
+
+
+/**
+ * skip_raw_volumes - reads data from pfi to advance fp past raw block
+ * @pfi: fp to pfi data
+ * @pfi_raws: header information
+ *
+ * Error handling):
+ * when early EOF in pfi data
+ * - returns -PFIFLASH_ERR_EOF, err_buf matches text to err
+ * when file I/O error
+ * - returns -PFIFLASH_ERR_FIO, err_buf matches text to err
+ **/
+static int
+skip_raw_volumes(FILE* pfi, list_t pfi_raws,
+ char* err_buf, size_t err_buf_size)
+{
+ int rc;
+ void *i;
+ list_t ptr;
+
+ if (is_empty(pfi_raws))
+ return 0;
+
+ rc = 0;
+ foreach(i, ptr, pfi_raws) {
+ size_t j;
+ pfi_raw_t raw;
+
+ raw = (pfi_raw_t)i;
+ for(j = 0; j < raw->data_size; j++) {
+ int c;
+
+ c = fgetc(pfi);
+ if (c == EOF)
+ rc = -PFIFLASH_ERR_EOF;
+ else if (ferror(pfi))
+ rc = -PFIFLASH_ERR_FIO;
+
+ if (rc != 0)
+ goto err;
+ }
+ }
+
+ err:
+ EBUF(PFIFLASH_ERRSTR[-rc]);
+ return rc;
+}
+
+
+/**
+ * my_ubi_mkvol - wraps the ubi_mkvol functions and impl. bootenv update hook
+ * @devno: UBI device number.
+ * @s: Current seqnum.
+ * @u: Information about the UBI volume from the PFI.
+ *
+ * Error handling:
+ * when UBI system couldn't be opened
+ * - returns -PFIFLASH_ERR_UBI_OPEN, err_buf matches text to err
+ * when UBI system couldn't create a volume
+ * - returns -PFIFLASH_ERR_UBI_MKVOL, err_buf matches text to err
+ **/
+static int
+my_ubi_mkvol(int devno, int s, pfi_ubi_t u,
+ char *err_buf, size_t err_buf_size)
+{
+ int rc, type;
+ ubi_lib_t ulib;
+
+ rc = 0;
+ ulib = NULL;
+
+ log_msg("[ ubimkvol id=%d, size=%d, data_size=%d, type=%d, "
+ "alig=%d, nlen=%d, name=%s",
+ u->ids[s], u->size, u->data_size, u->type, u->alignment,
+ strnlen(u->names[s], PFI_UBI_VOL_NAME_LEN), u->names[s]);
+
+ rc = ubi_open(&ulib);
+ if (rc != 0) {
+ rc = -PFIFLASH_ERR_UBI_OPEN;
+ EBUF(PFIFLASH_ERRSTR[-rc]);
+ goto err;
+ }
+
+ switch (u->type) {
+ case pfi_ubi_static:
+ type = UBI_STATIC_VOLUME; break;
+ case pfi_ubi_dynamic:
+ default:
+ type = UBI_DYNAMIC_VOLUME;
+ }
+
+ rc = ubi_mkvol(ulib, devno, u->ids[s], type, u->size, u->alignment,
+ u->names[s]);
+ if (rc != 0) {
+ rc = -PFIFLASH_ERR_UBI_MKVOL;
+ EBUF(PFIFLASH_ERRSTR[-rc], u->ids[s]);
+ goto err;
+ }
+
+ err:
+ if (ulib != NULL)
+ ubi_close(&ulib);
+
+ return rc;
+}
+
+
+/**
+ * my_ubi_rmvol - a wrapper around the UBI library function ubi_rmvol
+ * @devno UBI device number
+ * @id UBI volume id to remove
+ *
+ * If the volume does not exist, the function will return success.
+ *
+ * Error handling:
+ * when UBI system couldn't be opened
+ * - returns -PFIFLASH_ERR_UBI_OPEN, err_buf matches text to err
+ * when UBI system couldn't update (truncate) a volume
+ * - returns -PFIFLASH_ERR_UBI_VOL_UPDATE, err_buf matches text to err
+ * when UBI system couldn't remove a volume
+ * - returns -PFIFLASH_ERR_UBI_RMVOL, err_buf matches text to err
+ **/
+static int
+my_ubi_rmvol(int devno, uint32_t id,
+ char *err_buf, size_t err_buf_size)
+{
+ int rc, fd;
+ ubi_lib_t ulib;
+
+ rc = 0;
+ ulib = NULL;
+
+ log_msg("[ ubirmvol id=%d", id);
+
+ rc = ubi_open(&ulib);
+ if (rc != 0) {
+ rc = -PFIFLASH_ERR_UBI_OPEN;
+ EBUF(PFIFLASH_ERRSTR[-rc]);
+ goto err;
+ }
+
+ /* truncate whether it exist or not */
+ fd = ubi_vol_open(ulib, devno, id, O_RDWR);
+ if (fd == -1)
+ return 0; /* not existent, return 0 */
+
+ rc = ubi_vol_update(fd, 0);
+ ubi_vol_close(fd);
+ if (rc < 0) {
+ rc = -PFIFLASH_ERR_UBI_VOL_UPDATE;
+ EBUF(PFIFLASH_ERRSTR[-rc], id);
+ goto err; /* if EBUSY than empty device, continue */
+ }
+
+ rc = ubi_rmvol(ulib, devno, id);
+ if (rc != 0) {
+#ifdef DEBUG
+ int rc_old = rc;
+ dbg_msg("Remove UBI volume %d returned with error: %d "
+ "errno=%d", id, rc_old, errno);
+#endif
+
+ rc = -PFIFLASH_ERR_UBI_RMVOL;
+ EBUF(PFIFLASH_ERRSTR[-rc], id);
+
+ /* TODO Define a ubi_rmvol return value which says
+ * sth like EUBI_NOSUCHDEV. In this case, a failed
+ * operation is acceptable. Everything else has to be
+ * classified as real error. But talk to Andreas Arnez
+ * before defining something odd...
+ */
+ /* if ((errno == EINVAL) || (errno == ENODEV))
+ return 0; */ /* currently it is EINVAL or ENODEV */
+
+ goto err;
+ }
+
+ err:
+ if (ulib != NULL)
+ ubi_close(&ulib);
+
+ return rc;
+}
+
+
+/**
+ * read_bootenv_volume - reads the current bootenv data from id into be_old
+ * @devno UBI device number
+ * @id UBI volume id to remove
+ * @bootenv_old to hold old boot_env data
+ *
+ * Error handling:
+ * when UBI system couldn't be opened
+ * - returns -PFIFLASH_ERR_UBI_OPEN, err_buf matches text to err
+ * when UBI system couldn't open a volume to read
+ * - returns -PFIFLASH_ERR_UBI_VOL_FOPEN, err_buf matches text to err
+ * when couldn't read bootenv data
+ * - returns -PFIFLASH_ERR_BOOTENV_READ, err_buf matches text to err
+ **/
+static int
+read_bootenv_volume(int devno, uint32_t id, bootenv_t bootenv_old,
+ char *err_buf, size_t err_buf_size)
+{
+ int rc;
+ FILE* fp_in;
+ ubi_lib_t ulib;
+
+ rc = 0;
+ fp_in = NULL;
+ ulib = NULL;
+
+ rc = ubi_open(&ulib);
+ if (rc != 0) {
+ rc = -PFIFLASH_ERR_UBI_OPEN;
+ EBUF(PFIFLASH_ERRSTR[-rc]);
+ goto err;
+ }
+
+ fp_in = ubi_vol_fopen_read(ulib, devno, id);
+ if (!fp_in) {
+ rc = -PFIFLASH_ERR_UBI_VOL_FOPEN;
+ EBUF(PFIFLASH_ERRSTR[-rc], id);
+ goto err;
+ }
+
+ log_msg("[ reading old bootenvs ...");
+
+ /* Save old bootenvs for reference */
+ rc = bootenv_read(fp_in, bootenv_old, BOOTENV_MAXSIZE);
+ if (rc != 0) {
+ rc = -PFIFLASH_ERR_BOOTENV_READ;
+ EBUF(PFIFLASH_ERRSTR[-rc]);
+ goto err;
+ }
+
+ err:
+ if (fp_in)
+ fclose(fp_in);
+ if (ulib)
+ ubi_close(&ulib);
+
+ return rc;
+}
+
+
+/**
+ * write_bootenv_volume - writes data from PFI file int to bootenv UBI volume
+ * @devno UBI device number
+ * @id UBI volume id
+ * @bootend_old old PDD data from machine
+ * @pdd_f function to handle PDD with
+ * @fp_in new pdd data contained in PFI
+ * @fp_in_size data size of new pdd data in PFI
+ * @pfi_crc crc value from PFI header
+ *
+ * Error handling:
+ * when UBI system couldn't be opened
+ * - returns -PFIFLASH_ERR_UBI_OPEN, err_buf matches text to err
+ * when bootenv can't be created
+ * - returns -PFIFLASH_ERR_BOOTENV_CREATE, err_buf matches text to err
+ * when bootenv can't be read
+ * - returns -PFIFLASH_ERR_BOOTENV_READ, err_buf matches text to err
+ * when PDD handling function returns and error
+ * - passes rc and err_buf data
+ * when CRC check fails
+ * - returns -PFIFLASH_ERR_CRC_CHECK, err_buf matches text to err
+ * when bootenv can't be resized
+ * - returns -PFIFLASH_ERR_BOOTENV_SIZE, err_buf matches text to err
+ * when UBI system couldn't open a volume
+ * - returns -PFIFLASH_ERR_UBI_VOL_FOPEN, err_buf matches text to err
+ * when couldn't write bootenv data
+ * - returns -PFIFLASH_ERR_BOOTENV_WRITE, err_buf matches text to err
+ **/
+static int
+write_bootenv_volume(int devno, uint32_t id, bootenv_t bootenv_old,
+ pdd_func_t pdd_f, FILE* fp_in, size_t fp_in_size,
+ uint32_t pfi_crc,
+ char *err_buf, size_t err_buf_size)
+{
+ int rc, warnings;
+ uint32_t crc;
+ size_t update_size;
+ FILE *fp_out;
+ bootenv_t bootenv_new, bootenv_res;
+ ubi_lib_t ulib;
+
+ rc = 0;
+ warnings = 0;
+ crc = 0;
+ update_size = 0;
+ fp_out = NULL;
+ bootenv_new = NULL;
+ bootenv_res = NULL;
+ ulib = NULL;
+
+ log_msg("[ ubiupdatevol bootenv id=%d, fp_in=%p", id, fp_in);
+
+ /* Workflow:
+ * 1. Apply PDD operation and get the size of the returning
+ * bootenv_res section. Without the correct size it wouldn't
+ * be possible to call UBI update vol.
+ * 2. Call UBI update vol
+ * 3. Get FILE* to vol dev
+ * 4. Write to FILE*
+ */
+
+ rc = ubi_open(&ulib);
+ if (rc != 0) {
+ rc = -PFIFLASH_ERR_UBI_OPEN;
+ EBUF(PFIFLASH_ERRSTR[-rc]);
+ goto err;
+ }
+
+ rc = bootenv_create(&bootenv_new);
+ if (rc != 0) {
+ rc = -PFIFLASH_ERR_BOOTENV_CREATE;
+ EBUF(PFIFLASH_ERRSTR[-rc], " 'new'");
+ goto err;
+ }
+
+ rc = bootenv_create(&bootenv_res);
+ if (rc != 0) {
+ rc = -PFIFLASH_ERR_BOOTENV_CREATE;
+ EBUF(PFIFLASH_ERRSTR[-rc], " 'res'");
+ goto err;
+ }
+
+ rc = bootenv_read_crc(fp_in, bootenv_new, fp_in_size, &crc);
+ if (rc != 0) {
+ rc = -PFIFLASH_ERR_BOOTENV_READ;
+ EBUF(PFIFLASH_ERRSTR[-rc]);
+ goto err;
+ } else if (crc != pfi_crc) {
+ rc = -PFIFLASH_ERR_CRC_CHECK;
+ EBUF(PFIFLASH_ERRSTR[-rc], pfi_crc, crc);
+ goto err;
+ }
+
+ rc = pdd_f(bootenv_old, bootenv_new, &bootenv_res, &warnings,
+ err_buf, err_buf_size);
+ if (rc != 0) {
+ EBUF_PREPEND("handling PDD");
+ goto err;
+ }
+ else if (warnings)
+ /* TODO do something with warnings */
+ dbg_msg("A warning in the PDD operation occured: %d",
+ warnings);
+
+ rc = bootenv_size(bootenv_res, &update_size);
+ if (rc != 0) {
+ rc = -PFIFLASH_ERR_BOOTENV_SIZE;
+ EBUF(PFIFLASH_ERRSTR[-rc]);
+ goto err;
+ }
+
+ fp_out = ubi_vol_fopen_update(ulib, devno, id, update_size);
+ if (!fp_out) {
+ rc = -PFIFLASH_ERR_UBI_VOL_FOPEN;
+ EBUF(PFIFLASH_ERRSTR[-rc], id);
+ goto err;
+ }
+
+ rc = bootenv_write(fp_out, bootenv_res);
+ if (rc != 0) {
+ rc = -PFIFLASH_ERR_BOOTENV_WRITE;
+ EBUF(PFIFLASH_ERRSTR[-rc], devno, id);
+ goto err;
+ }
+
+ err:
+ if (ulib != NULL)
+ ubi_close(&ulib);
+ if (bootenv_new != NULL)
+ bootenv_destroy(&bootenv_new);
+ if (bootenv_res != NULL)
+ bootenv_destroy(&bootenv_res);
+ if (fp_out)
+ fclose(fp_out);
+
+ return rc;
+}
+
+
+/**
+ * write_normal_volume - writes data from PFI file int to regular UBI volume
+ * @devno UBI device number
+ * @id UBI volume id
+ * @update_size size of data stream
+ * @fp_in PFI data file pointer
+ * @pfi_crc CRC data from PFI header
+ *
+ * Error handling:
+ * when UBI system couldn't be opened
+ * - returns -PFIFLASH_ERR_UBI_OPEN, err_buf matches text to err
+ * when UBI system couldn't open a volume
+ * - returns -PFIFLASH_ERR_UBI_VOL_FOPEN, err_buf matches text to err
+ * when unexpected EOF is encountered
+ * - returns -PFIFLASH_ERR_EOF, err_buf matches text to err
+ * when file I/O error
+ * - returns -PFIFLASH_ERR_FIO, err_buf matches text to err
+ * when CRC check fails
+ * - retruns -PFIFLASH_ERR_CRC_CHECK, err_buf matches text to err
+ **/
+static int
+write_normal_volume(int devno, uint32_t id, size_t update_size, FILE* fp_in,
+ uint32_t pfi_crc,
+ char *err_buf, size_t err_buf_size)
+{
+ int rc;
+ uint32_t crc, crc32_table[256];
+ size_t bytes_left;
+ FILE* fp_out;
+ ubi_lib_t ulib;
+
+ rc = 0;
+ crc = UBI_CRC32_INIT;
+ bytes_left = update_size;
+ fp_out = NULL;
+ ulib = NULL;
+
+ log_msg("[ ubiupdatevol id=%d, update_size=%d fp_in=%p",
+ id, update_size, fp_in);
+
+ rc = ubi_open(&ulib);
+ if (rc != 0) {
+ rc = -PFIFLASH_ERR_UBI_OPEN;
+ EBUF(PFIFLASH_ERRSTR[-rc]);
+ goto err;
+ }
+
+ fp_out = ubi_vol_fopen_update(ulib, devno, id, update_size);
+ if (!fp_out) {
+ rc = -PFIFLASH_ERR_UBI_VOL_FOPEN;
+ EBUF(PFIFLASH_ERRSTR[-rc], id);
+ goto err;
+ }
+
+ init_crc32_table(crc32_table);
+ while (bytes_left) {
+ char buf[1024];
+ size_t to_rw = sizeof buf > bytes_left ?
+ bytes_left : sizeof buf;
+ if (fread(buf, 1, to_rw, fp_in) != to_rw) {
+ rc = -PFIFLASH_ERR_EOF;
+ EBUF(PFIFLASH_ERRSTR[-rc]);
+ goto err;
+ }
+ crc = clc_crc32(crc32_table, crc, buf, to_rw);
+ if (fwrite(buf, 1, to_rw, fp_out) != to_rw) {
+ rc = -PFIFLASH_ERR_FIO;
+ EBUF(PFIFLASH_ERRSTR[-rc]);
+ goto err;
+ }
+ bytes_left -= to_rw;
+ }
+
+ if (crc != pfi_crc) {
+ rc = -PFIFLASH_ERR_CRC_CHECK;
+ EBUF(PFIFLASH_ERRSTR[-rc], pfi_crc, crc);
+ goto err;
+ }
+
+ err:
+ if (fp_out)
+ fclose(fp_out);
+ if (ulib)
+ ubi_close(&ulib);
+
+ return rc;
+}
+
+static int
+erase_mtd_region(FILE* file_p, int start, int length)
+{
+ int rc, fd;
+ erase_info_t erase;
+ mtd_info_t mtdinfo;
+ loff_t offset = start;
+ loff_t end = offset + length;
+
+ fd = fileno(file_p);
+ if (fd < 0)
+ return -PFIFLASH_ERR_MTD_ERASE;
+
+ rc = ioctl(fd, MEMGETINFO, &mtdinfo);
+ if (rc)
+ return -PFIFLASH_ERR_MTD_ERASE;
+
+ /* check for bad blocks in case of NAND flash */
+ if (mtdinfo.type == MTD_NANDFLASH) {
+ while (offset < end) {
+ rc = ioctl(fd, MEMGETBADBLOCK, &offset);
+ if (rc > 0) {
+ return -PFIFLASH_ERR_MTD_ERASE;
+ }
+
+ offset += mtdinfo.erasesize;
+ }
+ }
+
+ erase.start = start;
+ erase.length = length;
+
+ rc = ioctl(fd, MEMERASE, &erase);
+ if (rc) {
+ return -PFIFLASH_ERR_MTD_ERASE;
+ }
+
+ return rc;
+}
+
+/**
+ * process_raw_volumes - writes the raw sections of the PFI data
+ * @pfi PFI data file pointer
+ * @pfi_raws list of PFI raw headers
+ * @rawdev device to use to write raw data
+ *
+ * Error handling:
+ * when early EOF in PFI data
+ * - returns -PFIFLASH_ERR_EOF, err_buf matches text to err
+ * when file I/O error
+ * - returns -PFIFLASH_ERR_FIO, err_buf matches text to err
+ * when CRC check fails
+ * - returns -PFIFLASH_ERR_CRC_CHECK, err_buf matches text to err
+ * when opening MTD device fails
+ * - reutrns -PFIFLASH_ERR_MTD_OPEN, err_buf matches text to err
+ * when closing MTD device fails
+ * - returns -PFIFLASH_ERR_MTD_CLOSE, err_buf matches text to err
+ **/
+static int
+process_raw_volumes(FILE* pfi, list_t pfi_raws, const char* rawdev,
+ char* err_buf, size_t err_buf_size)
+{
+ int rc;
+ char *pfi_data;
+ void *i;
+ uint32_t crc, crc32_table[256];
+ size_t j, k;
+ FILE* mtd = NULL;
+ list_t ptr;
+
+ if (is_empty(pfi_raws))
+ return 0;
+
+ if (rawdev == NULL)
+ return 0;
+
+ rc = 0;
+
+ pfi_data = NULL;
+
+ log_msg("[ rawupdate dev=%s", rawdev);
+
+ crc = UBI_CRC32_INIT;
+ init_crc32_table(crc32_table);
+
+ /* most likely only one element in list, but just in case */
+ foreach(i, ptr, pfi_raws) {
+ pfi_raw_t r = (pfi_raw_t)i;
+
+ /* read in pfi data */
+ if (pfi_data != NULL)
+ free(pfi_data);
+ pfi_data = malloc(r->data_size * sizeof(char));
+ for (j = 0; j < r->data_size; j++) {
+ int c = fgetc(pfi);
+ if (c == EOF) {
+ rc = -PFIFLASH_ERR_EOF;
+ EBUF(PFIFLASH_ERRSTR[-rc]);
+ goto err;
+ } else if (ferror(pfi)) {
+ rc = -PFIFLASH_ERR_FIO;
+ EBUF(PFIFLASH_ERRSTR[-rc]);
+ goto err;
+ }
+ pfi_data[j] = (char)c;
+ }
+ crc = clc_crc32(crc32_table, crc, pfi_data, r->data_size);
+
+ /* check crc */
+ if (crc != r->crc) {
+ rc = -PFIFLASH_ERR_CRC_CHECK;
+ EBUF(PFIFLASH_ERRSTR[-rc], r->crc, crc);
+ goto err;
+ }
+
+ /* open device */
+ mtd = fopen(rawdev, "r+");
+ if (mtd == NULL) {
+ rc = -PFIFLASH_ERR_MTD_OPEN;
+ EBUF(PFIFLASH_ERRSTR[-rc], rawdev);
+ goto err;
+ }
+
+ for (j = 0; j < r->starts_size; j++) {
+ rc = erase_mtd_region(mtd, r->starts[j], r->data_size);
+ if (rc) {
+ EBUF(PFIFLASH_ERRSTR[-rc]);
+ goto err;
+ }
+
+ fseek(mtd, r->starts[j], SEEK_SET);
+ for (k = 0; k < r->data_size; k++) {
+ int c = fputc((int)pfi_data[k], mtd);
+ if (c == EOF) {
+ fclose(mtd);
+ rc = -PFIFLASH_ERR_EOF;
+ EBUF(PFIFLASH_ERRSTR[-rc]);
+ goto err;
+ }
+ if ((char)c != pfi_data[k]) {
+ fclose(mtd);
+ rc = -1;
+ goto err;
+ }
+ }
+ }
+ rc = fclose(mtd);
+ mtd = NULL;
+ if (rc != 0) {
+ rc = -PFIFLASH_ERR_MTD_CLOSE;
+ EBUF(PFIFLASH_ERRSTR[-rc], rawdev);
+ goto err;
+ }
+ }
+
+ err:
+ if (mtd != NULL)
+ fclose(mtd);
+ if (pfi_data != NULL)
+ free(pfi_data);
+ return rc;
+}
+
+
+/**
+ * erase_unmapped_ubi_volumes - skip volumes provided by PFI file, clear rest
+ * @devno UBI device number
+ * @pfi_ubis list of UBI header data
+ *
+ * Error handling:
+ * when UBI id is out of bounds
+ * - returns -PFIFLASH_ERR_UBI_VID_OOB, err_buf matches text to err
+ * when UBI volume can't be removed
+ * - passes rc, prepends err_buf with contextual aid
+ **/
+static int
+erase_unmapped_ubi_volumes(int devno, list_t pfi_ubis,
+ char *err_buf, size_t err_buf_size)
+{
+ int rc;
+ uint8_t ubi_volumes[PFI_UBI_MAX_VOLUMES];
+ size_t i;
+ list_t ptr;
+ pfi_ubi_t u;
+
+ rc = 0;
+
+ for (i = 0; i < PFI_UBI_MAX_VOLUMES; i++)
+ ubi_volumes[i] = 1;
+
+ foreach(u, ptr, pfi_ubis) {
+ /* iterate over each vol_id */
+ for(i = 0; i < u->ids_size; i++) {
+ if (u->ids[i] >= PFI_UBI_MAX_VOLUMES) {
+ rc = -PFIFLASH_ERR_UBI_VID_OOB;
+ EBUF(PFIFLASH_ERRSTR[-rc], u->ids[i]);
+ goto err;
+ }
+ /* remove from removal list */
+ ubi_volumes[u->ids[i]] = 0;
+ }
+ }
+
+ for (i = 0; i < PFI_UBI_MAX_VOLUMES; i++) {
+ if (ubi_volumes[i]) {
+ rc = my_ubi_rmvol(devno, i, err_buf, err_buf_size);
+ if (rc != 0) {
+ EBUF_PREPEND("remove volume failed");
+ goto err;
+ }
+ }
+ }
+
+ err:
+ return rc;
+}
+
+
+/**
+ * process_ubi_volumes - delegate tasks regarding UBI volumes
+ * @pfi PFI data file pointer
+ * @seqnum sequence number
+ * @pfi_ubis list of UBI header data
+ * @bootenv_old storage for current system PDD
+ * @pdd_f function to handle PDD
+ * @ubi_update_process whether reading or writing
+ *
+ * Error handling:
+ * when and unknown ubi_update_process is given
+ * - returns -PFIFLASH_ERR_UBI_UNKNOWN, err_buf matches text to err
+ * otherwise
+ * - passes rc and err_buf
+ **/
+static int
+process_ubi_volumes(FILE* pfi, int seqnum, list_t pfi_ubis,
+ bootenv_t bootenv_old, pdd_func_t pdd_f,
+ ubi_update_process_t ubi_update_process,
+ char *err_buf, size_t err_buf_size)
+{
+ int rc;
+ pfi_ubi_t u;
+ list_t ptr;
+
+ rc = 0;
+
+ foreach(u, ptr, pfi_ubis) {
+ int s = seqnum;
+
+ if (s > ((int)u->ids_size - 1))
+ s = 0; /* per default use the first */
+ u->curr_seqnum = s;
+
+ switch (ubi_update_process) {
+ case UBI_REMOVE:
+ /* TODO are all these "EXAMPLE" vars okay? */
+ if ((u->ids[s] == EXAMPLE_BOOTENV_VOL_ID_1) ||
+ (u->ids[s] == EXAMPLE_BOOTENV_VOL_ID_2)) {
+ rc = read_bootenv_volume(EXAMPLE_UBI_DEVICE,
+ u->ids[s], bootenv_old,
+ err_buf, err_buf_size);
+ /* it's okay if there is no bootenv
+ * we're going to write one */
+ if ((rc == -PFIFLASH_ERR_UBI_VOL_FOPEN) ||
+ (rc == -PFIFLASH_ERR_BOOTENV_READ))
+ rc = 0;
+ if (rc != 0)
+ goto err;
+ }
+
+ rc = my_ubi_rmvol(EXAMPLE_UBI_DEVICE, u->ids[s],
+ err_buf, err_buf_size);
+ if (rc != 0)
+ goto err;
+
+ break;
+ case UBI_WRITE:
+ rc = my_ubi_mkvol(EXAMPLE_UBI_DEVICE, s, u,
+ err_buf, err_buf_size);
+ if (rc != 0) {
+ EBUF_PREPEND("creating volume");
+ goto err;
+ }
+
+ if ((u->ids[s] == EXAMPLE_BOOTENV_VOL_ID_1) ||
+ (u->ids[s] == EXAMPLE_BOOTENV_VOL_ID_2)) {
+ rc = write_bootenv_volume(EXAMPLE_UBI_DEVICE,
+ u->ids[s],
+ bootenv_old, pdd_f,
+ pfi,
+ u->data_size,
+ u->crc,
+ err_buf,
+ err_buf_size);
+ if (rc != 0)
+ EBUF_PREPEND("bootenv volume");
+ } else {
+ rc = write_normal_volume(EXAMPLE_UBI_DEVICE,
+ u->ids[s],
+ u->data_size, pfi,
+ u->crc,
+ err_buf,
+ err_buf_size);
+ if (rc != 0)
+ EBUF_PREPEND("normal volume");
+ }
+ if (rc != 0)
+ goto err;
+
+ break;
+ default:
+ rc = -PFIFLASH_ERR_UBI_UNKNOWN;
+ EBUF(PFIFLASH_ERRSTR[-rc]);
+ goto err;
+ }
+ }
+
+ err:
+ return rc;
+}
+
+
+/**
+ * mirror_ubi_volumes - mirror redundant pairs of volumes
+ * @devno UBI device number
+ * @pfi_ubis list of PFI header data
+ *
+ * Error handling:
+ * when UBI system couldn't be opened
+ * - returns -PFIFLASH_ERR_UBI_OPEN, err_buf matches text to err
+ **/
+static int
+mirror_ubi_volumes(uint32_t devno, list_t pfi_ubis,
+ char *err_buf, size_t err_buf_size)
+{
+ int rc;
+ uint32_t j;
+ list_t ptr;
+ pfi_ubi_t i;
+ ubi_lib_t ulib;
+
+ rc = 0;
+ ulib = NULL;
+
+ log_msg("[ mirror ...");
+
+ rc = ubi_open(&ulib);
+ if (rc != 0) {
+ rc = -PFIFLASH_ERR_UBI_OPEN;
+ EBUF(PFIFLASH_ERRSTR[-rc]);
+ goto err;
+ }
+
+ /**
+ * Execute all mirror operations on redundant groups.
+ * Create a volume within a redundant group if it does
+ * not exist already (this is a precondition of
+ * ubimirror).
+ */
+ foreach(i, ptr, pfi_ubis) {
+ for (j = 0; j < i->ids_size; j++) {
+ /* skip self-match */
+ if (i->ids[j] == i->ids[i->curr_seqnum])
+ continue;
+
+ rc = my_ubi_rmvol(devno, i->ids[j],
+ err_buf, err_buf_size);
+ if (rc != 0)
+ goto err;
+
+ rc = my_ubi_mkvol(devno, j, i,
+ err_buf, err_buf_size);
+ if (rc != 0)
+ goto err;
+ }
+ }
+
+ foreach(i, ptr, pfi_ubis) {
+ rc = ubimirror(devno, i->curr_seqnum, i->ids, i->ids_size,
+ err_buf, err_buf_size);
+ if (rc != 0)
+ goto err;
+ }
+
+
+ err:
+ if (ulib != NULL)
+ ubi_close(&ulib);
+
+ return rc;
+}
+
+
+/**
+ * pfiflash_with_raw - exposed func to flash memory with a PFI file
+ * @pfi PFI data file pointer
+ * @complete flag to erase unmapped volumes
+ * @seqnum sequence number
+ * @pdd_handling method to handle pdd (keep, merge, overwrite...)
+ *
+ * Error handling:
+ * when bootenv can't be created
+ * - returns -PFIFLASH_ERR_BOOTENV_CREATE, err_buf matches text to err
+ * when PFI headers can't be read, or
+ * when fail to skip raw sections, or
+ * when error occurs while processing raw volumes, or
+ * when fail to erase unmapped UBI vols, or
+ * when error occurs while processing UBI volumes, or
+ * when error occurs while mirroring UBI volumes
+ * - passes rc, prepends err_buf with contextual aid
+ **/
+int
+pfiflash_with_raw(FILE* pfi, int complete, int seqnum,
+ pdd_handling_t pdd_handling, const char* rawdev,
+ char *err_buf, size_t err_buf_size)
+{
+ int rc;
+ bootenv_t bootenv;
+ pdd_func_t pdd_f;
+
+ if (pfi == NULL)
+ return -EINVAL;
+
+ rc = 0;
+ pdd_f = NULL;
+
+ /* If the user didnt specify a seqnum we start per default
+ * with the index 0 */
+ int curr_seqnum = seqnum < 0 ? 0 : seqnum;
+
+ list_t pfi_raws = mk_empty(); /* list of raw sections from a pfi */
+ list_t pfi_ubis = mk_empty(); /* list of ubi sections from a pfi */
+
+ rc = bootenv_create(&bootenv);
+ if (rc != 0) {
+ rc = -PFIFLASH_ERR_BOOTENV_CREATE;
+ EBUF(PFIFLASH_ERRSTR[-rc], "");
+ goto err;
+ }
+
+ rc = read_pfi_headers(&pfi_raws, &pfi_ubis, pfi, err_buf, err_buf_size);
+ if (rc != 0) {
+ EBUF_PREPEND("reading PFI header");
+ goto err;
+ }
+
+ if (rawdev == NULL)
+ rc = skip_raw_volumes(pfi, pfi_raws, err_buf, err_buf_size);
+ else
+ rc = process_raw_volumes(pfi, pfi_raws, rawdev, err_buf,
+ err_buf_size);
+ if (rc != 0) {
+ EBUF_PREPEND("handling raw section");
+ goto err;
+ }
+
+ if (complete) {
+ rc = erase_unmapped_ubi_volumes(EXAMPLE_UBI_DEVICE, pfi_ubis,
+ err_buf, err_buf_size);
+ if (rc != 0) {
+ EBUF_PREPEND("deleting unmapped UBI volumes");
+ goto err;
+ }
+ }
+
+ if (((int)pdd_handling >= 0) &&
+ (pdd_handling < PDD_HANDLING_NUM))
+ pdd_f = pdd_funcs[pdd_handling];
+ else {
+ rc = -PFIFLASH_ERR_PDD_UNKNOWN;
+ EBUF(PFIFLASH_ERRSTR[-rc]);
+ goto err;
+ }
+
+ rc = process_ubi_volumes(pfi, curr_seqnum, pfi_ubis, bootenv, pdd_f,
+ UBI_REMOVE, err_buf, err_buf_size);
+ if (rc != 0) {
+ EBUF_PREPEND("removing UBI volumes");
+ goto err;
+ }
+
+ rc = process_ubi_volumes(pfi, curr_seqnum, pfi_ubis, bootenv, pdd_f,
+ UBI_WRITE, err_buf, err_buf_size);
+ if (rc != 0) {
+ EBUF_PREPEND("writing UBI volumes");
+ goto err;
+ }
+
+ if (seqnum < 0) { /* mirror redundant pairs */
+ rc = mirror_ubi_volumes(EXAMPLE_UBI_DEVICE, pfi_ubis,
+ err_buf, err_buf_size);
+ if (rc != 0) {
+ EBUF_PREPEND("mirroring UBI volumes");
+ goto err;
+ }
+ }
+
+ err:
+ pfi_raws = remove_all((free_func_t)&free_pfi_raw, pfi_raws);
+ pfi_ubis = remove_all((free_func_t)&free_pfi_ubi, pfi_ubis);
+ bootenv_destroy(&bootenv);
+ return rc;
+}
+
+
+/**
+ * pfiflash - passes to pfiflash_with_raw
+ * @pfi PFI data file pointer
+ * @complete flag to erase unmapped volumes
+ * @seqnum sequence number
+ * @pdd_handling method to handle pdd (keep, merge, overwrite...)
+ **/
+int
+pfiflash(FILE* pfi, int complete, int seqnum, pdd_handling_t pdd_handling,
+ char *err_buf, size_t err_buf_size)
+{
+ return pfiflash_with_raw(pfi, complete, seqnum, pdd_handling,
+ NULL, err_buf, err_buf_size);
+}
diff --git a/ubi-utils/src/libubi.c b/ubi-utils/src/libubi.c
new file mode 100644
index 0000000..17ab4ee
--- /dev/null
+++ b/ubi-utils/src/libubi.c
@@ -0,0 +1,917 @@
+/*
+ * 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.
+ *
+ * Author: Artem B. Bityutskiy
+ *
+ * UBI (Unsorted Block Images) library.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <limits.h>
+#include "libubi.h"
+#include "libubi_int.h"
+
+libubi_t libubi_open(void)
+{
+ int fd, version;
+ struct libubi *lib;
+
+ lib = calloc(1, sizeof(struct libubi));
+ if (!lib)
+ return NULL;
+
+ /* TODO: this must be discovered instead */
+ lib->sysfs = strdup("/sys");
+ if (!lib->sysfs)
+ goto error;
+
+ lib->sysfs_ubi = mkpath(lib->sysfs, SYSFS_UBI);
+ if (!lib->sysfs_ubi)
+ goto error;
+
+ /* Make sure UBI is present */
+ fd = open(lib->sysfs_ubi, O_RDONLY);
+ if (fd == -1)
+ goto error;
+ close(fd);
+
+ lib->ubi_dev = mkpath(lib->sysfs_ubi, UBI_DEV_NAME_PATT);
+ if (!lib->ubi_dev)
+ goto error;
+
+ lib->ubi_version = mkpath(lib->sysfs_ubi, UBI_VER);
+ if (!lib->ubi_version)
+ goto error;
+
+ lib->dev_dev = mkpath(lib->ubi_dev, DEV_DEV);
+ if (!lib->dev_dev)
+ goto error;
+
+ lib->dev_avail_ebs = mkpath(lib->ubi_dev, DEV_AVAIL_EBS);
+ if (!lib->dev_avail_ebs)
+ goto error;
+
+ lib->dev_total_ebs = mkpath(lib->ubi_dev, DEV_TOTAL_EBS);
+ if (!lib->dev_total_ebs)
+ goto error;
+
+ lib->dev_bad_count = mkpath(lib->ubi_dev, DEV_BAD_COUNT);
+ if (!lib->dev_bad_count)
+ goto error;
+
+ lib->dev_eb_size = mkpath(lib->ubi_dev, DEV_EB_SIZE);
+ if (!lib->dev_eb_size)
+ goto error;
+
+ lib->dev_max_ec = mkpath(lib->ubi_dev, DEV_MAX_EC);
+ if (!lib->dev_max_ec)
+ goto error;
+
+ lib->dev_bad_rsvd = mkpath(lib->ubi_dev, DEV_MAX_RSVD);
+ if (!lib->dev_bad_rsvd)
+ goto error;
+
+ lib->dev_max_vols = mkpath(lib->ubi_dev, DEV_MAX_VOLS);
+ if (!lib->dev_max_vols)
+ goto error;
+
+ lib->dev_min_io_size = mkpath(lib->ubi_dev, DEV_MIN_IO_SIZE);
+ if (!lib->dev_min_io_size)
+ goto error;
+
+ lib->ubi_vol = mkpath(lib->sysfs_ubi, UBI_VOL_NAME_PATT);
+ if (!lib->ubi_vol)
+ goto error;
+
+ lib->vol_type = mkpath(lib->ubi_vol, VOL_TYPE);
+ if (!lib->vol_type)
+ goto error;
+
+ lib->vol_dev = mkpath(lib->ubi_vol, VOL_DEV);
+ if (!lib->vol_dev)
+ goto error;
+
+ lib->vol_alignment = mkpath(lib->ubi_vol, VOL_ALIGNMENT);
+ if (!lib->vol_alignment)
+ goto error;
+
+ lib->vol_data_bytes = mkpath(lib->ubi_vol, VOL_DATA_BYTES);
+ if (!lib->vol_data_bytes)
+ goto error;
+
+ lib->vol_rsvd_ebs = mkpath(lib->ubi_vol, VOL_RSVD_EBS);
+ if (!lib->vol_rsvd_ebs)
+ goto error;
+
+ lib->vol_eb_size = mkpath(lib->ubi_vol, VOL_EB_SIZE);
+ if (!lib->vol_eb_size)
+ goto error;
+
+ lib->vol_corrupted = mkpath(lib->ubi_vol, VOL_CORRUPTED);
+ if (!lib->vol_corrupted)
+ goto error;
+
+ lib->vol_name = mkpath(lib->ubi_vol, VOL_NAME);
+ if (!lib->vol_name)
+ goto error;
+
+ if (read_int(lib->ubi_version, &version))
+ goto error;
+ if (version != LIBUBI_UBI_VERSION) {
+ fprintf(stderr, "LIBUBI: this library was made for UBI version "
+ "%d, but UBI version %d is detected\n",
+ LIBUBI_UBI_VERSION, version);
+ goto error;
+ }
+
+ return lib;
+
+error:
+ free(lib->vol_corrupted);
+ free(lib->vol_eb_size);
+ free(lib->vol_rsvd_ebs);
+ free(lib->vol_data_bytes);
+ free(lib->vol_alignment);
+ free(lib->vol_dev);
+ free(lib->vol_type);
+ free(lib->ubi_vol);
+ free(lib->dev_min_io_size);
+ free(lib->dev_max_vols);
+ free(lib->dev_bad_rsvd);
+ free(lib->dev_max_ec);
+ free(lib->dev_eb_size);
+ free(lib->dev_bad_count);
+ free(lib->dev_total_ebs);
+ free(lib->dev_avail_ebs);
+ free(lib->dev_dev);
+ free(lib->ubi_version);
+ free(lib->ubi_dev);
+ free(lib->sysfs_ubi);
+ free(lib->sysfs);
+ free(lib);
+ return NULL;
+}
+
+void libubi_close(libubi_t desc)
+{
+ struct libubi *lib = (struct libubi *)desc;
+
+ free(lib->vol_name);
+ free(lib->vol_corrupted);
+ free(lib->vol_eb_size);
+ free(lib->vol_rsvd_ebs);
+ free(lib->vol_data_bytes);
+ free(lib->vol_alignment);
+ free(lib->vol_dev);
+ free(lib->vol_type);
+ free(lib->ubi_vol);
+ free(lib->dev_min_io_size);
+ free(lib->dev_max_vols);
+ free(lib->dev_bad_rsvd);
+ free(lib->dev_max_ec);
+ free(lib->dev_eb_size);
+ free(lib->dev_bad_count);
+ free(lib->dev_total_ebs);
+ free(lib->dev_avail_ebs);
+ free(lib->dev_dev);
+ free(lib->ubi_version);
+ free(lib->ubi_dev);
+ free(lib->sysfs_ubi);
+ free(lib->sysfs);
+ free(lib);
+}
+
+int ubi_get_info(libubi_t desc, struct ubi_info *info)
+{
+ DIR *sysfs_ubi;
+ struct dirent *dirent;
+ struct libubi *lib = (struct libubi *)desc;
+
+ memset(info, '\0', sizeof(struct ubi_info));
+
+ /*
+ * We have to scan the UBI sysfs directory to identify how many UBI
+ * devices are present.
+ */
+ sysfs_ubi = opendir(lib->sysfs_ubi);
+ if (!sysfs_ubi)
+ return -1;
+
+ info->lowest_dev_num = INT_MAX;
+ while ((dirent = readdir(sysfs_ubi))) {
+ char *name = &dirent->d_name[0];
+ int dev_num, ret;
+
+ ret = sscanf(name, UBI_DEV_NAME_PATT, &dev_num);
+ if (ret == 1) {
+ info->dev_count += 1;
+ if (dev_num > info->highest_dev_num)
+ info->highest_dev_num = dev_num;
+ if (dev_num < info->lowest_dev_num)
+ info->lowest_dev_num = dev_num;
+ }
+ }
+
+ if (info->lowest_dev_num == INT_MAX)
+ info->lowest_dev_num = 0;
+
+ if (read_int(lib->ubi_version, &info->version))
+ goto close;
+
+ return closedir(sysfs_ubi);
+
+close:
+ closedir(sysfs_ubi);
+ return -1;
+}
+
+int ubi_mkvol(libubi_t desc, const char *node, struct ubi_mkvol_request *req)
+{
+ int fd, ret;
+ struct ubi_mkvol_req r;
+ size_t n;
+
+ desc = desc;
+ r.vol_id = req->vol_id;
+ r.alignment = req->alignment;
+ r.bytes = req->bytes;
+ r.vol_type = req->vol_type;
+
+ n = strlen(req->name);
+ if (n > UBI_MAX_VOLUME_NAME)
+ return -1;
+
+ strncpy(r.name, req->name, UBI_MAX_VOLUME_NAME + 1);
+ r.name_len = n;
+
+ fd = open(node, O_RDONLY);
+ if (fd == -1)
+ return -1;
+
+ ret = ioctl(fd, UBI_IOCMKVOL, &r);
+
+ if (!ret)
+ req->vol_id = r.vol_id;
+
+ close(fd);
+ return ret;
+}
+
+int ubi_rmvol(libubi_t desc, const char *node, int vol_id)
+{
+ int fd, ret;
+
+ desc = desc;
+ fd = open(node, O_RDONLY);
+ if (fd == -1)
+ return -1;
+
+ ret = ioctl(fd, UBI_IOCRMVOL, &vol_id);
+ close(fd);
+ return ret;
+}
+
+int ubi_rsvol(libubi_t desc, const char *node, int vol_id, long long bytes)
+{
+ int fd, ret;
+ struct ubi_rsvol_req req;
+
+ desc = desc;
+ fd = open(node, O_RDONLY);
+ if (fd == -1)
+ return -1;
+
+ req.bytes = bytes;
+ req.vol_id = vol_id;
+
+ ret = ioctl(fd, UBI_IOCRSVOL, &req);
+ close(fd);
+ return ret;
+}
+
+int ubi_update_start(libubi_t desc, int fd, long long bytes)
+{
+ desc = desc;
+ if (ioctl(fd, UBI_IOCVOLUP, &bytes))
+ return -1;
+ return 0;
+}
+
+int ubi_get_dev_info(libubi_t desc, const char *node, struct ubi_dev_info *info)
+{
+ int dev_num;
+ struct libubi *lib = (struct libubi *)desc;
+
+ dev_num = find_dev_num(lib, node);
+ if (dev_num == -1)
+ return -1;
+
+ return ubi_get_dev_info1(desc, dev_num, info);
+}
+
+int ubi_get_dev_info1(libubi_t desc, int dev_num, struct ubi_dev_info *info)
+{
+ DIR *sysfs_ubi;
+ struct dirent *dirent;
+ struct libubi *lib = (struct libubi *)desc;
+
+ memset(info, '\0', sizeof(struct ubi_dev_info));
+ info->dev_num = dev_num;
+
+ sysfs_ubi = opendir(lib->sysfs_ubi);
+ if (!sysfs_ubi)
+ return -1;
+
+ info->lowest_vol_num = INT_MAX;
+ while ((dirent = readdir(sysfs_ubi))) {
+ char *name = &dirent->d_name[0];
+ int vol_id, ret, devno;
+
+ ret = sscanf(name, UBI_VOL_NAME_PATT, &devno, &vol_id);
+ if (ret == 2 && devno == dev_num) {
+ info->vol_count += 1;
+ if (vol_id > info->highest_vol_num)
+ info->highest_vol_num = vol_id;
+ if (vol_id < info->lowest_vol_num)
+ info->lowest_vol_num = vol_id;
+ }
+ }
+
+ closedir(sysfs_ubi);
+
+ if (info->lowest_vol_num == INT_MAX)
+ info->lowest_vol_num = 0;
+
+ if (dev_read_int(lib->dev_avail_ebs, dev_num, &info->avail_ebs))
+ return -1;
+ if (dev_read_int(lib->dev_total_ebs, dev_num, &info->total_ebs))
+ return -1;
+ if (dev_read_int(lib->dev_bad_count, dev_num, &info->bad_count))
+ return -1;
+ if (dev_read_int(lib->dev_eb_size, dev_num, &info->eb_size))
+ return -1;
+ if (dev_read_int(lib->dev_bad_rsvd, dev_num, &info->bad_rsvd))
+ return -1;
+ if (dev_read_ll(lib->dev_max_ec, dev_num, &info->max_ec))
+ return -1;
+ if (dev_read_int(lib->dev_max_vols, dev_num, &info->max_vol_count))
+ return -1;
+ if (dev_read_int(lib->dev_min_io_size, dev_num, &info->min_io_size))
+ return -1;
+
+ info->avail_bytes = info->avail_ebs * info->eb_size;
+ info->total_bytes = info->total_ebs * info->eb_size;
+
+ return 0;
+}
+
+int ubi_get_vol_info(libubi_t desc, const char *node, struct ubi_vol_info *info)
+{
+ int vol_id, dev_num;
+ struct libubi *lib = (struct libubi *)desc;
+
+ dev_num = find_dev_num_vol(lib, node);
+ if (dev_num == -1)
+ return -1;
+
+ vol_id = find_vol_num(lib, dev_num, node);
+ if (vol_id == -1)
+ return -1;
+
+ return ubi_get_vol_info1(desc, dev_num, vol_id, info);
+}
+
+int ubi_get_vol_info1(libubi_t desc, int dev_num, int vol_id,
+ struct ubi_vol_info *info)
+{
+ int ret;
+ struct libubi *lib = (struct libubi *)desc;
+ char buf[50];
+
+ memset(info, '\0', sizeof(struct ubi_vol_info));
+ info->dev_num = dev_num;
+ info->vol_id = vol_id;
+
+ ret = vol_read_data(lib->vol_type, dev_num, vol_id, &buf[0], 50);
+ if (ret < 0)
+ return -1;
+
+ if (strncmp(&buf[0], "static\n", ret) == 0)
+ info->type = UBI_STATIC_VOLUME;
+ else if (strncmp(&buf[0], "dynamic\n", ret) == 0)
+ info->type = UBI_DYNAMIC_VOLUME;
+ else {
+ fprintf(stderr, "LIBUBI: bad value at sysfs file\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ ret = vol_read_int(lib->vol_alignment, dev_num, vol_id,
+ &info->alignment);
+ if (ret)
+ return -1;
+ ret = vol_read_ll(lib->vol_data_bytes, dev_num, vol_id,
+ &info->data_bytes);
+ if (ret)
+ return -1;
+ ret = vol_read_int(lib->vol_rsvd_ebs, dev_num, vol_id, &info->rsvd_ebs);
+ if (ret)
+ return -1;
+ ret = vol_read_int(lib->vol_eb_size, dev_num, vol_id, &info->eb_size);
+ if (ret)
+ return -1;
+ ret = vol_read_int(lib->vol_corrupted, dev_num, vol_id,
+ &info->corrupted);
+ if (ret)
+ return -1;
+ info->rsvd_bytes = info->eb_size * info->rsvd_ebs;
+
+ ret = vol_read_data(lib->vol_name, dev_num, vol_id, &info->name,
+ UBI_VOL_NAME_MAX + 2);
+ if (ret < 0)
+ return -1;
+
+ info->name[ret - 1] = '\0';
+
+ return 0;
+}
+
+/**
+ * read_int - read an 'int' value from a file.
+ *
+ * @file the file to read from
+ * @value the result is stored here
+ *
+ * This function returns %0 in case of success and %-1 in case of failure.
+ */
+static int read_int(const char *file, int *value)
+{
+ int fd, rd;
+ char buf[50];
+
+ fd = open(file, O_RDONLY);
+ if (fd == -1)
+ return -1;
+
+ rd = read(fd, &buf[0], 50);
+ if (rd == -1)
+ goto error;
+
+ if (sscanf(&buf[0], "%d\n", value) != 1) {
+ /* This must be a UBI bug */
+ fprintf(stderr, "LIBUBI: bad value at sysfs file\n");
+ errno = EINVAL;
+ goto error;
+ }
+
+ close(fd);
+ return 0;
+
+error:
+ close(fd);
+ return -1;
+}
+
+/**
+ * dev_read_int - read an 'int' value from an UBI device's sysfs file.
+ *
+ * @patt the file pattern to read from
+ * @dev_num UBI device number
+ * @value the result is stored here
+ *
+ * This function returns %0 in case of success and %-1 in case of failure.
+ */
+static int dev_read_int(const char *patt, int dev_num, int *value)
+{
+ int fd, rd;
+ char buf[50];
+ char file[strlen(patt) + 50];
+
+ sprintf(&file[0], patt, dev_num);
+ fd = open(&file[0], O_RDONLY);
+ if (fd == -1)
+ return -1;
+
+ rd = read(fd, &buf[0], 50);
+ if (rd == -1)
+ goto error;
+
+ if (sscanf(&buf[0], "%d\n", value) != 1) {
+ /* This must be a UBI bug */
+ fprintf(stderr, "LIBUBI: bad value at sysfs file\n");
+ errno = EINVAL;
+ goto error;
+ }
+
+ close(fd);
+ return 0;
+
+error:
+ close(fd);
+ return -1;
+}
+
+/**
+ * dev_read_ll - read a 'long long' value from an UBI device's sysfs file.
+ *
+ * @patt the file pattern to read from
+ * @dev_num UBI device number
+ * @value the result is stored here
+ *
+ * This function returns %0 in case of success and %-1 in case of failure.
+ */
+static int dev_read_ll(const char *patt, int dev_num, long long *value)
+{
+ int fd, rd;
+ char buf[50];
+ char file[strlen(patt) + 50];
+
+ sprintf(&file[0], patt, dev_num);
+ fd = open(&file[0], O_RDONLY);
+ if (fd == -1)
+ return -1;
+
+ rd = read(fd, &buf[0], 50);
+ if (rd == -1)
+ goto error;
+
+ if (sscanf(&buf[0], "%lld\n", value) != 1) {
+ /* This must be a UBI bug */
+ fprintf(stderr, "LIBUBI: bad value at sysfs file\n");
+ errno = EINVAL;
+ goto error;
+ }
+
+ close(fd);
+ return 0;
+
+error:
+ close(fd);
+ return -1;
+}
+
+/**
+ * dev_read_data - read data from an UBI device's sysfs file.
+ *
+ * @patt the file pattern to read from
+ * @dev_num UBI device number
+ * @buf buffer to read data to
+ * @buf_len buffer length
+ *
+ * This function returns number of read bytes in case of success and %-1 in
+ * case of failure.
+ */
+static int dev_read_data(const char *patt, int dev_num, void *buf, int buf_len)
+{
+ int fd, rd;
+ char file[strlen(patt) + 50];
+
+ sprintf(&file[0], patt, dev_num);
+ fd = open(&file[0], O_RDONLY);
+ if (fd == -1)
+ return -1;
+
+ rd = read(fd, buf, buf_len);
+ if (rd == -1) {
+ close(fd);
+ return -1;
+ }
+
+ close(fd);
+ return rd;
+}
+
+/**
+ * vol_read_int - read an 'int' value from an UBI volume's sysfs file.
+ *
+ * @patt the file pattern to read from
+ * @dev_num UBI device number
+ * @vol_id volume identifier
+ * @value the result is stored here
+ *
+ * This function returns %0 in case of success and %-1 in case of failure.
+ */
+static int vol_read_int(const char *patt, int dev_num, int vol_id, int *value)
+{
+ int fd, rd;
+ char buf[50];
+ char file[strlen(patt) + 100];
+
+ sprintf(&file[0], patt, dev_num, vol_id);
+ fd = open(&file[0], O_RDONLY);
+ if (fd == -1)
+ return -1;
+
+ rd = read(fd, &buf[0], 50);
+ if (rd == -1)
+ goto error;
+
+ if (sscanf(&buf[0], "%d\n", value) != 1) {
+ /* This must be a UBI bug */
+ fprintf(stderr, "LIBUBI: bad value at sysfs file\n");
+ errno = EINVAL;
+ goto error;
+ }
+
+ close(fd);
+ return 0;
+
+error:
+ close(fd);
+ return -1;
+}
+
+/**
+ * vol_read_ll - read a 'long long' value from an UBI volume's sysfs file.
+ *
+ * @patt the file pattern to read from
+ * @dev_num UBI device number
+ * @vol_id volume identifier
+ * @value the result is stored here
+ *
+ * This function returns %0 in case of success and %-1 in case of failure.
+ */
+static int vol_read_ll(const char *patt, int dev_num, int vol_id,
+ long long *value)
+{
+ int fd, rd;
+ char buf[50];
+ char file[strlen(patt) + 100];
+
+ sprintf(&file[0], patt, dev_num, vol_id);
+ fd = open(&file[0], O_RDONLY);
+ if (fd == -1)
+ return -1;
+
+ rd = read(fd, &buf[0], 50);
+ if (rd == -1)
+ goto error;
+
+ if (sscanf(&buf[0], "%lld\n", value) != 1) {
+ /* This must be a UBI bug */
+ fprintf(stderr, "LIBUBI: bad value at sysfs file\n");
+ errno = EINVAL;
+ goto error;
+ }
+
+ close(fd);
+ return 0;
+
+error:
+ close(fd);
+ return -1;
+}
+
+/**
+ * vol_read_data - read data from an UBI volume's sysfs file.
+ *
+ * @patt the file pattern to read from
+ * @dev_num UBI device number
+ * @vol_id volume identifier
+ * @buf buffer to read to
+ * @buf_len buffer length
+ *
+ * This function returns number of read bytes in case of success and %-1 in
+ * case of failure.
+ */
+static int vol_read_data(const char *patt, int dev_num, int vol_id, void *buf,
+ int buf_len)
+{
+ int fd, rd;
+ char file[strlen(patt) + 100];
+
+ sprintf(&file[0], patt, dev_num, vol_id);
+ fd = open(&file[0], O_RDONLY);
+ if (fd == -1)
+ return -1;
+
+ rd = read(fd, buf, buf_len);
+ if (rd == -1) {
+ close(fd);
+ return -1;
+ }
+
+ close(fd);
+ return rd;
+}
+
+/**
+ * mkpath - compose full path from 2 given components.
+ *
+ * @path first component
+ * @name second component
+ *
+ * This function returns the resulting path in case of success and %NULL in
+ * case of failure.
+ */
+static char *mkpath(const char *path, const char *name)
+{
+ char *n;
+ int len1 = strlen(path);
+ int len2 = strlen(name);
+
+ n = malloc(len1 + len2 + 2);
+ if (!n)
+ return NULL;
+
+ memcpy(n, path, len1);
+ if (n[len1 - 1] != '/')
+ n[len1++] = '/';
+
+ memcpy(n + len1, name, len2 + 1);
+ return n;
+}
+
+/**
+ * find_dev_num - find UBI device number by its character device node.
+ *
+ * @lib UBI library descriptor
+ * @node UBI character device node name
+ *
+ * This function returns positive UBI device number in case of success and %-1
+ * in case of failure.
+ */
+static int find_dev_num(struct libubi *lib, const char *node)
+{
+ struct stat stat;
+ struct ubi_info info;
+ int i, major, minor;
+
+ if (lstat(node, &stat))
+ return -1;
+
+ if (!S_ISCHR(stat.st_mode)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ major = major(stat.st_rdev);
+ minor = minor(stat.st_rdev);
+
+ if (minor != 0) {
+ errno = -EINVAL;
+ return -1;
+ }
+
+ if (ubi_get_info((libubi_t *)lib, &info))
+ return -1;
+
+ for (i = info.lowest_dev_num; i <= info.highest_dev_num; i++) {
+ int major1, minor1, ret;
+ char buf[50];
+
+ ret = dev_read_data(lib->dev_dev, i, &buf[0], 50);
+ if (ret < 0)
+ return -1;
+
+ ret = sscanf(&buf[0], "%d:%d\n", &major1, &minor1);
+ if (ret != 2) {
+ fprintf(stderr, "LIBUBI: bad value at sysfs file\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (minor1 == minor && major1 == major)
+ return i;
+ }
+
+ errno = ENOENT;
+ return -1;
+}
+
+/**
+ * find_dev_num_vol - find UBI device number by volume character device node.
+ *
+ * @lib UBI library descriptor
+ * @node UBI character device node name
+ *
+ * This function returns positive UBI device number in case of success and %-1
+ * in case of failure.
+ */
+static int find_dev_num_vol(struct libubi *lib, const char *node)
+{
+ struct stat stat;
+ struct ubi_info info;
+ int i, major;
+
+ if (lstat(node, &stat))
+ return -1;
+
+ if (!S_ISCHR(stat.st_mode)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ major = major(stat.st_rdev);
+
+ if (minor(stat.st_rdev) == 0) {
+ errno = -EINVAL;
+ return -1;
+ }
+
+ if (ubi_get_info((libubi_t *)lib, &info))
+ return -1;
+
+ for (i = info.lowest_dev_num; i <= info.highest_dev_num; i++) {
+ int major1, minor1, ret;
+ char buf[50];
+
+ ret = dev_read_data(lib->dev_dev, i, &buf[0], 50);
+ if (ret < 0)
+ return -1;
+
+ ret = sscanf(&buf[0], "%d:%d\n", &major1, &minor1);
+ if (ret != 2) {
+ fprintf(stderr, "LIBUBI: bad value at sysfs file\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (major1 == major)
+ return i;
+ }
+
+ errno = ENOENT;
+ return -1;
+}
+
+/**
+ * find_vol_num - find UBI volume number by its character device node.
+ *
+ * @lib UBI library descriptor
+ * @dev_num UBI device number
+ * @node UBI volume character device node name
+ *
+ * This function returns positive UBI volume number in case of success and %-1
+ * in case of failure.
+ */
+static int find_vol_num(struct libubi *lib, int dev_num, const char *node)
+{
+ struct stat stat;
+ struct ubi_dev_info info;
+ int i, major, minor;
+
+ if (lstat(node, &stat))
+ return -1;
+
+ if (!S_ISCHR(stat.st_mode)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ major = major(stat.st_rdev);
+ minor = minor(stat.st_rdev);
+
+ if (minor == 0) {
+ errno = -EINVAL;
+ return -1;
+ }
+
+ if (ubi_get_dev_info1((libubi_t *)lib, dev_num, &info))
+ return -1;
+
+ for (i = info.lowest_vol_num; i <= info.highest_vol_num; i++) {
+ int major1, minor1, ret;
+ char buf[50];
+
+ ret = vol_read_data(lib->vol_dev, dev_num, i, &buf[0], 50);
+ if (ret < 0)
+ return -1;
+
+ ret = sscanf(&buf[0], "%d:%d\n", &major1, &minor1);
+ if (ret != 2) {
+ fprintf(stderr, "LIBUBI: bad value at sysfs file\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (minor1 == minor && major1 == major)
+ return i;
+ }
+
+ errno = ENOENT;
+ return -1;
+}
diff --git a/ubi-utils/src/libubi_int.h b/ubi-utils/src/libubi_int.h
new file mode 100644
index 0000000..e68b791
--- /dev/null
+++ b/ubi-utils/src/libubi_int.h
@@ -0,0 +1,129 @@
+/*
+ * 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.
+ *
+ * Author: Artem B. Bityutskiy
+ *
+ * UBI (Unsorted Block Images) library.
+ */
+
+#ifndef __LIBUBI_INT_H__
+#define __LIBUBI_INT_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * UBI heavily makes use of the sysfs file system to interact with users-pace.
+ * The below are pre-define UBI file and directory names.
+ */
+
+#define SYSFS_UBI "class/ubi"
+#define UBI_DEV_NAME_PATT "ubi%d"
+#define UBI_VER "version"
+#define DEV_DEV "dev"
+#define UBI_VOL_NAME_PATT "ubi%d_%d"
+#define DEV_AVAIL_EBS "avail_eraseblocks"
+#define DEV_TOTAL_EBS "total_eraseblocks"
+#define DEV_BAD_COUNT "bad_peb_count"
+#define DEV_EB_SIZE "eraseblock_size"
+#define DEV_MAX_EC "max_ec"
+#define DEV_MAX_RSVD "reserved_for_bad"
+#define DEV_MAX_VOLS "max_vol_count"
+#define DEV_MIN_IO_SIZE "min_io_size"
+#define VOL_TYPE "type"
+#define VOL_DEV "dev"
+#define VOL_ALIGNMENT "alignment"
+#define VOL_DATA_BYTES "data_bytes"
+#define VOL_RSVD_EBS "reserved_ebs"
+#define VOL_EB_SIZE "usable_eb_size"
+#define VOL_CORRUPTED "corrupted"
+#define VOL_NAME "name"
+
+/**
+ * libubi - UBI library description data structure.
+ *
+ * @sysfs sysfs file system path
+ * @sysfs_ubi UBI directory in sysfs
+ * @ubi_dev UBI device sysfs directory pattern
+ * @ubi_version UBI version file sysfs path
+ * @dev_dev UBI device's major/minor numbers file pattern
+ * @dev_avail_ebs count of available eraseblocks sysfs path pattern
+ * @dev_total_ebs total eraseblocks count sysfs path pattern
+ * @dev_bad_count count of bad eraseblocks sysfs path pattern
+ * @dev_eb_size size of UBI device's eraseblocks sysfs path pattern
+ * @dev_max_ec maximum erase counter sysfs path pattern
+ * @dev_bad_rsvd count of physical eraseblock reserved for bad eraseblocks
+ * handling
+ * @dev_max_vols maximum volumes number count sysfs path pattern
+ * @dev_min_io_size minimum I/O unit size sysfs path pattern
+ * @ubi_vol UBI volume sysfs directory pattern
+ * @vol_type volume type sysfs path pattern
+ * @vol_dev volume's major/minor numbers file pattern
+ * @vol_alignment volume alignment sysfs path pattern
+ * @vol_data_bytes volume data size sysfs path pattern
+ * @vol_rsvd_ebs volume reserved size sysfs path pattern
+ * @vol_eb_size volume eraseblock size sysfs path pattern
+ * @vol_corrupted volume corruption flag sysfs path pattern
+ * @vol_name volume name sysfs path pattern
+ */
+struct libubi
+{
+ char *sysfs;
+ char *sysfs_ubi;
+ char *ubi_dev;
+ char *ubi_version;
+ char *dev_dev;
+ char *dev_avail_ebs;
+ char *dev_total_ebs;
+ char *dev_bad_count;
+ char *dev_eb_size;
+ char *dev_max_ec;
+ char *dev_bad_rsvd;
+ char *dev_max_vols;
+ char *dev_min_io_size;
+ char *ubi_vol;
+ char *vol_type;
+ char *vol_dev;
+ char *vol_alignment;
+ char *vol_data_bytes;
+ char *vol_rsvd_ebs;
+ char *vol_eb_size;
+ char *vol_corrupted;
+ char *vol_name;
+ char *vol_max_count;
+};
+
+static int read_int(const char *file, int *value);
+static int dev_read_int(const char *patt, int dev_num, int *value);
+static int dev_read_ll(const char *patt, int dev_num, long long *value);
+static int dev_read_data(const char *patt, int dev_num, void *buf, int buf_len);
+static int vol_read_int(const char *patt, int dev_num, int vol_id, int *value);
+static int vol_read_ll(const char *patt, int dev_num, int vol_id,
+ long long *value);
+static int vol_read_data(const char *patt, int dev_num, int vol_id, void *buf,
+ int buf_len);
+static char *mkpath(const char *path, const char *name);
+static int find_dev_num(struct libubi *lib, const char *node);
+static int find_dev_num_vol(struct libubi *lib, const char *node);
+static int find_vol_num(struct libubi *lib, int dev_num, const char *node);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !__LIBUBI_INT_H__ */
diff --git a/ubi-utils/src/libubigen.c b/ubi-utils/src/libubigen.c
new file mode 100644
index 0000000..258e555
--- /dev/null
+++ b/ubi-utils/src/libubigen.c
@@ -0,0 +1,486 @@
+/*
+ * 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.
+ *
+ * Author: Oliver Lohmann
+ *
+ * Add UBI headers to binary data.
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <mtd/ubi-header.h>
+
+#include "config.h"
+#include "ubigen.h"
+#include "crc32.h"
+
+#define UBI_NAME_SIZE 256
+#define DEFAULT_VID_OFFSET ((DEFAULT_PAGESIZE) - (UBI_VID_HDR_SIZE))
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+
+static uint32_t crc32_table[256];
+
+struct ubi_info {
+ struct ubi_vid_hdr* v; /* Volume ID header */
+ struct ubi_ec_hdr* ec; /* Erase count header */
+
+ FILE* fp_in; /* Input Stream */
+ FILE* fp_out; /* Output stream */
+
+ size_t eb_size; /* Physical EB size in bytes */
+ size_t leb_size; /* Size of a logical EB in a physical EB */
+ size_t leb_total; /* Total input size in logical EB */
+ size_t alignment; /* Block alignment */
+ size_t data_pad; /* Size of padding in each physical EB */
+
+ size_t bytes_total; /* Total input size in bytes */
+ size_t bytes_read; /* Nymber of read bytes (total) */
+
+ uint32_t blks_written; /* Number of written logical EB */
+
+ uint8_t* buf; /* Allocated buffer */
+ uint8_t* ptr_ec_hdr; /* Pointer to EC hdr in buf */
+ uint8_t* ptr_vid_hdr; /* Pointer to VID hdr in buf */
+ uint8_t* ptr_data; /* Pointer to data region in buf */
+};
+
+
+static uint32_t
+byte_to_blk(uint64_t byte, uint32_t eb_size)
+{
+ return (byte % eb_size) == 0
+ ? (byte / eb_size)
+ : (byte / eb_size) + 1;
+}
+
+static int
+validate_ubi_info(ubi_info_t u)
+{
+ if ((u->v->vol_type != UBI_VID_DYNAMIC) &&
+ (u->v->vol_type != UBI_VID_STATIC)) {
+ return EUBIGEN_INVALID_TYPE;
+ }
+
+ if (ubi32_to_cpu(u->ec->vid_hdr_offset) < UBI_VID_HDR_SIZE) {
+ return EUBIGEN_INVALID_HDR_OFFSET;
+ }
+
+ return 0;
+}
+
+static int
+skip_blks(ubi_info_t u, uint32_t blks)
+{
+ uint32_t i;
+ size_t read = 0, to_read = 0;
+
+ /* Step to a maximum of leb_total - 1 to keep the
+ restrictions. */
+ for (i = 0; i < MIN(blks, u->leb_total-1); i++) {
+ /* Read in data */
+ to_read = MIN(u->leb_size,
+ (u->bytes_total - u->bytes_read));
+ read = fread(u->ptr_data, 1, to_read, u->fp_in);
+ if (read != to_read) {
+ return -EIO;
+ }
+ u->bytes_read += read;
+ u->blks_written++;
+ }
+
+ return 0;
+}
+
+static void
+clear_buf(ubi_info_t u)
+{
+ memset(u->buf, 0xff, u->eb_size);
+}
+
+static void
+write_ec_hdr(ubi_info_t u)
+{
+ memcpy(u->ptr_ec_hdr, u->ec, UBI_EC_HDR_SIZE);
+}
+
+static int
+fill_data_buffer_from_file(ubi_info_t u, size_t* read)
+{
+ size_t to_read = 0;
+
+ if (u-> fp_in == NULL)
+ return -EIO;
+
+ to_read = MIN(u->leb_size, (u->bytes_total - u->bytes_read));
+ *read = fread(u->ptr_data, 1, to_read, u->fp_in);
+ if (*read != to_read) {
+ return -EIO;
+ }
+ return 0;
+}
+
+static void
+add_static_info(ubi_info_t u, size_t data_size, ubigen_action_t action)
+{
+ uint32_t crc = clc_crc32(crc32_table, UBI_CRC32_INIT,
+ u->ptr_data, data_size);
+
+ u->v->data_size = cpu_to_ubi32(data_size);
+ u->v->data_crc = cpu_to_ubi32(crc);
+
+ if (action & BROKEN_DATA_CRC) {
+ u->v->data_crc =
+ cpu_to_ubi32(ubi32_to_cpu(u->v->data_crc) + 1);
+ }
+ if (action & BROKEN_DATA_SIZE) {
+ u->v->data_size =
+ cpu_to_ubi32(ubi32_to_cpu(u->v->data_size) + 1);
+ }
+}
+
+static void
+write_vid_hdr(ubi_info_t u, ubigen_action_t action)
+{
+ uint32_t crc = clc_crc32(crc32_table, UBI_CRC32_INIT,
+ u->v, UBI_VID_HDR_SIZE_CRC);
+ /* Write VID header */
+ u->v->hdr_crc = cpu_to_ubi32(crc);
+ if (action & BROKEN_HDR_CRC) {
+ u->v->hdr_crc = cpu_to_ubi32(ubi32_to_cpu(u->v->hdr_crc) + 1);
+ }
+ memcpy(u->ptr_vid_hdr, u->v, UBI_VID_HDR_SIZE);
+}
+
+static int
+write_to_output_stream(ubi_info_t u)
+{
+ size_t written;
+
+ written = fwrite(u->buf, 1, u->eb_size, u->fp_out);
+ if (written != u->eb_size) {
+ return -EIO;
+ }
+ return 0;
+}
+
+int
+ubigen_write_leb(ubi_info_t u, ubigen_action_t action)
+{
+ int rc = 0;
+ size_t read = 0;
+
+ clear_buf(u);
+ write_ec_hdr(u);
+
+ rc = fill_data_buffer_from_file(u, &read);
+ if (rc != 0)
+ return rc;
+
+ if (u->v->vol_type == UBI_VID_STATIC) {
+ add_static_info(u, read, action);
+ }
+
+ u->v->lnum = cpu_to_ubi32(u->blks_written);
+
+ if (action & MARK_AS_UPDATE) {
+ u->v->copy_flag = (u->v->copy_flag)++;
+ }
+
+ write_vid_hdr(u, action);
+ rc = write_to_output_stream(u);
+ if (rc != 0)
+ return rc;
+
+ /* Update current handle */
+ u->bytes_read += read;
+ u->blks_written++;
+ return 0;
+}
+
+int
+ubigen_write_complete(ubi_info_t u)
+{
+ size_t i;
+ int rc = 0;
+
+ for (i = 0; i < u->leb_total; i++) {
+ rc = ubigen_write_leb(u, NO_ERROR);
+ if (rc != 0)
+ return rc;
+ }
+
+ return 0;
+}
+
+int
+ubigen_write_broken_update(ubi_info_t u, uint32_t blk)
+{
+ int rc = 0;
+
+ rc = skip_blks(u, blk);
+ if (rc != 0)
+ return rc;
+
+ rc = ubigen_write_leb(u, MARK_AS_UPDATE | BROKEN_DATA_CRC);
+ if (rc != 0)
+ return rc;
+
+
+ return 0;
+}
+
+void
+dump_info(ubi_info_t u __unused)
+{
+#ifdef DEBUG
+ int err = 0;
+ if (!u) {
+ fprintf(stderr, "<empty>");
+ return;
+ }
+ if (!u->ec) {
+ fprintf(stderr, "<ec-empty>");
+ err = 1;
+ }
+ if (!u->v) {
+ fprintf(stderr, "<v-empty>");
+ err = 1;
+ }
+ if (err) return;
+
+ fprintf(stderr, "ubi volume\n");
+ fprintf(stderr, "version : %8d\n", u->v->version);
+ fprintf(stderr, "vol_id : %8d\n", ubi32_to_cpu(u->v->vol_id));
+ fprintf(stderr, "vol_type : %8s\n",
+ u->v->vol_type == UBI_VID_STATIC ?
+ "static" : "dynamic");
+ fprintf(stderr, "used_ebs : %8d\n",
+ ubi32_to_cpu(u->v->used_ebs));
+ fprintf(stderr, "eb_size : 0x%08x\n", u->eb_size);
+ fprintf(stderr, "leb_size : 0x%08x\n", u->leb_size);
+ fprintf(stderr, "data_pad : 0x%08x\n",
+ ubi32_to_cpu(u->v->data_pad));
+ fprintf(stderr, "leb_total : %8d\n", u->leb_total);
+ fprintf(stderr, "header offs : 0x%08x\n",
+ ubi32_to_cpu(u->ec->vid_hdr_offset));
+ fprintf(stderr, "bytes_total : %8d\n", u->bytes_total);
+ fprintf(stderr, " + in MiB : %8.2f M\n",
+ ((float)(u->bytes_total)) / 1024 / 1024);
+ fprintf(stderr, "-------------------------------\n\n");
+#else
+ return;
+#endif
+}
+
+int
+ubigen_destroy(ubi_info_t *u)
+{
+ if (u == NULL)
+ return -EINVAL;
+
+ ubi_info_t tmp = *u;
+
+ if (tmp) {
+ if (tmp->v)
+ free(tmp->v);
+ if (tmp->ec)
+ free(tmp->ec);
+ if (tmp->buf)
+ free(tmp->buf);
+ free(tmp);
+ }
+ *u = NULL;
+ return 0;
+}
+
+void
+ubigen_init(void)
+{
+ init_crc32_table(crc32_table);
+}
+
+int
+ubigen_create(ubi_info_t* u, uint32_t vol_id, uint8_t vol_type,
+ uint32_t eb_size, uint64_t ec, uint32_t alignment,
+ uint8_t version, uint32_t vid_hdr_offset, uint8_t compat_flag,
+ size_t data_size, FILE* fp_in, FILE* fp_out)
+{
+ int rc = 0;
+ ubi_info_t res = NULL;
+ uint32_t crc;
+ uint32_t data_offset;
+
+ if (alignment == 0) {
+ rc = EUBIGEN_INVALID_ALIGNMENT;
+ goto ubigen_create_err;
+ }
+ if ((fp_in == NULL) || (fp_out == NULL)) {
+ rc = -EINVAL;
+ goto ubigen_create_err;
+ }
+
+ res = (ubi_info_t) calloc(1, sizeof(struct ubi_info));
+ if (res == NULL) {
+ rc = -ENOMEM;
+ goto ubigen_create_err;
+ }
+
+ res->v = (struct ubi_vid_hdr*) calloc(1, sizeof(struct ubi_vid_hdr));
+ if (res->v == NULL) {
+ rc = -ENOMEM;
+ goto ubigen_create_err;
+ }
+
+ res->ec = (struct ubi_ec_hdr*) calloc(1, sizeof(struct ubi_ec_hdr));
+ if (res->ec == NULL) {
+ rc = -ENOMEM;
+ goto ubigen_create_err;
+ }
+
+ /* data which is needed in the general process */
+ vid_hdr_offset = vid_hdr_offset ? vid_hdr_offset : DEFAULT_VID_OFFSET;
+ data_offset = vid_hdr_offset + UBI_VID_HDR_SIZE;
+ res->bytes_total = data_size;
+ res->eb_size = eb_size ? eb_size : DEFAULT_BLOCKSIZE;
+ res->data_pad = (res->eb_size - data_offset) % alignment;
+ res->leb_size = res->eb_size - data_offset - res->data_pad;
+ res->leb_total = byte_to_blk(data_size, res->leb_size);
+ res->alignment = alignment;
+
+ if ((res->eb_size < (vid_hdr_offset + UBI_VID_HDR_SIZE))) {
+ rc = EUBIGEN_TOO_SMALL_EB;
+ goto ubigen_create_err;
+ }
+ res->fp_in = fp_in;
+ res->fp_out = fp_out;
+
+ /* vid hdr data which doesn't change */
+ res->v->magic = cpu_to_ubi32(UBI_VID_HDR_MAGIC);
+ res->v->version = version ? version : UBI_VERSION;
+ res->v->vol_type = vol_type;
+ res->v->vol_id = cpu_to_ubi32(vol_id);
+ res->v->compat = compat_flag;
+ res->v->data_pad = cpu_to_ubi32(res->data_pad);
+
+ /* static only: used_ebs */
+ if (res->v->vol_type == UBI_VID_STATIC) {
+ res->v->used_ebs = cpu_to_ubi32(byte_to_blk
+ (res->bytes_total,
+ res->leb_size));
+ }
+
+ /* ec hdr (fixed, doesn't change) */
+ res->ec->magic = cpu_to_ubi32(UBI_EC_HDR_MAGIC);
+ res->ec->version = version ? version : UBI_VERSION;
+ res->ec->ec = cpu_to_ubi64(ec);
+ res->ec->vid_hdr_offset = cpu_to_ubi32(vid_hdr_offset);
+
+ res->ec->data_offset = cpu_to_ubi32(data_offset);
+
+ crc = clc_crc32(crc32_table, UBI_CRC32_INIT, res->ec,
+ UBI_EC_HDR_SIZE_CRC);
+ res->ec->hdr_crc = cpu_to_ubi32(crc);
+
+ /* prepare a read buffer */
+ res->buf = (uint8_t*) malloc (res->eb_size * sizeof(uint8_t));
+ if (res->buf == NULL) {
+ rc = -ENOMEM;
+ goto ubigen_create_err;
+ }
+
+ /* point to distinct regions within the buffer */
+ res->ptr_ec_hdr = res->buf;
+ res->ptr_vid_hdr = res->buf + ubi32_to_cpu(res->ec->vid_hdr_offset);
+ res->ptr_data = res->buf + ubi32_to_cpu(res->ec->vid_hdr_offset)
+ + UBI_VID_HDR_SIZE;
+
+ rc = validate_ubi_info(res);
+ if (rc != 0) {
+ fprintf(stderr, "Volume validation failed: %d\n", rc);
+ goto ubigen_create_err;
+ }
+
+ dump_info(res);
+ *u = res;
+ return rc;
+
+ ubigen_create_err:
+ if (res) {
+ if (res->v)
+ free(res->v);
+ if (res->ec)
+ free(res->ec);
+ if (res->buf)
+ free(res->buf);
+ free(res);
+ }
+ *u = NULL;
+ return rc;
+}
+
+int
+ubigen_get_leb_size(ubi_info_t u, size_t* size)
+{
+ if (u == NULL)
+ return -EINVAL;
+
+ *size = u->leb_size;
+ return 0;
+}
+
+
+int
+ubigen_get_leb_total(ubi_info_t u, size_t* total)
+{
+ if (u == NULL)
+ return -EINVAL;
+
+ *total = u->leb_total;
+ return 0;
+}
+
+int
+ubigen_set_lvol_rec(ubi_info_t u, size_t reserved_bytes,
+ const char* vol_name, struct ubi_vol_tbl_record *lvol_rec)
+{
+ uint32_t crc;
+
+ if ((u == NULL) || (vol_name == NULL))
+ return -EINVAL;
+
+ memset(lvol_rec, 0x0, UBI_VTBL_RECORD_SIZE);
+
+ lvol_rec->reserved_pebs =
+ cpu_to_ubi32(byte_to_blk(reserved_bytes, u->leb_size));
+ lvol_rec->alignment = cpu_to_ubi32(u->alignment);
+ lvol_rec->data_pad = u->v->data_pad;
+ lvol_rec->vol_type = u->v->vol_type;
+
+ lvol_rec->name_len =
+ cpu_to_ubi16((uint16_t)strlen((const char*)vol_name));
+
+ memcpy(lvol_rec->name, vol_name, UBI_VOL_NAME_MAX + 1);
+
+ crc = clc_crc32(crc32_table, UBI_CRC32_INIT,
+ lvol_rec, UBI_VTBL_RECORD_SIZE_CRC);
+ lvol_rec->crc = cpu_to_ubi32(crc);
+
+ return 0;
+}
diff --git a/ubi-utils/src/libubimirror.c b/ubi-utils/src/libubimirror.c
new file mode 100644
index 0000000..d8ea548
--- /dev/null
+++ b/ubi-utils/src/libubimirror.c
@@ -0,0 +1,217 @@
+/*
+ * 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.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <memory.h>
+#include <fcntl.h>
+
+#include <libubiold.h>
+#include "ubimirror.h"
+
+#define COMPARE_BUF_SIZE (128 * 1024)
+
+#define EBUF(fmt...) do { \
+ snprintf(err_buf, err_buf_size, fmt); \
+} while (0)
+
+enum {
+ compare_error = -1,
+ seek_error = -2,
+ write_error = -3,
+ read_error = -4,
+ update_error = -5,
+ ubi_error = -6,
+ open_error = -7,
+ close_error = -8,
+ compare_equal = 0,
+ compare_different = 1
+};
+
+/*
+ * Read len number of bytes from fd.
+ * Return 0 on EOF, -1 on error.
+ */
+static ssize_t fill_buffer(int fd, unsigned char *buf, ssize_t len)
+{
+ ssize_t got, have = 0;
+
+ do {
+ got = read(fd, buf + have, len - have);
+ if( got == -1 && errno != EINTR )
+ return -1;
+ have += got;
+ } while ( got > 0 && have < len);
+ return have;
+}
+
+/*
+ * Write len number of bytes to fd.
+ * Return bytes written (>= 0), -1 on error.
+ */
+static ssize_t flush_buffer(int fd, unsigned char *buf, ssize_t len)
+{
+ ssize_t done, have = 0;
+
+ do {
+ done = write(fd, buf + have, len - have);
+ if( done == -1 && errno != EINTR )
+ return -1;
+ have += done;
+ } while ( done > 0 && have < len);
+ return have;
+}
+
+/*
+ * Compare two files. Return 0, 1, or -1, depending on whether the
+ * files are equal, different, or an error occured.
+ * Return compare-different when target volume can not be read. Might be
+ * an interrupted volume update and then the target device returns -EIO but
+ * can be updated.
+ *
+ * fd_a is source
+ * fd_b is destination
+ */
+static int
+compare_files(int fd_a, int fd_b)
+{
+ unsigned char buf_a[COMPARE_BUF_SIZE], buf_b[COMPARE_BUF_SIZE];
+ ssize_t len_a, len_b;
+ int rc;
+
+ for (;;) {
+ len_a = fill_buffer(fd_a, buf_a, sizeof(buf_a));
+ if (len_a == -1){
+ rc = compare_error;
+ break;
+ }
+ len_b = fill_buffer(fd_b, buf_b, sizeof(buf_b));
+ if (len_b == -1){
+ rc = compare_different;
+ break;
+ }
+ if( len_a != len_b ){
+ rc = compare_different;
+ break;
+ }
+ if( len_a == 0 ){ /* Size on both filies equal and EOF */
+ rc = compare_equal;
+ break;
+ }
+ if( memcmp(buf_a, buf_b, len_a) != 0 ){
+ rc = compare_different;
+ break;
+ }
+ }
+ /* Position both files at the beginning */
+ if( lseek(fd_a, 0, SEEK_SET) == -1 ||
+ lseek(fd_b, 0, SEEK_SET) == -1 )
+ rc = seek_error;
+ return rc;
+}
+
+static int
+copy_files(int fd_in, int fd_out)
+{
+ unsigned char buf_a[COMPARE_BUF_SIZE];
+ ssize_t len_a, len_b;
+ unsigned long long update_size, copied;
+
+ if( ubi_vol_get_used_bytes(fd_in, &update_size) == -1 ||
+ ubi_vol_update(fd_out, update_size) == -1 )
+ return update_error;
+ for( copied = 0; copied < update_size; copied += len_b ){
+ len_a = fill_buffer(fd_in, buf_a, sizeof(buf_a));
+ if (len_a == -1)
+ return read_error;
+ if (len_a == 0) /* Reach EOF */
+ return 0;
+ len_b = flush_buffer(fd_out, buf_a, len_a);
+ if( len_b != len_a )
+ return write_error;
+ }
+ return 0;
+}
+
+int
+ubimirror(uint32_t devno, int seqnum, uint32_t *ids, ssize_t ids_size,
+ char *err_buf, size_t err_buf_size)
+{
+ int rc = 0;
+ uint32_t src_id;
+ ubi_lib_t ulib = NULL;
+ int fd_in = -1, i = 0, fd_out = -1;
+
+ if (ids_size == 0)
+ return 0;
+ else {
+ if ((seqnum < 0) || (seqnum > (ids_size - 1))) {
+ EBUF("volume id %d out of range", seqnum);
+ return EUBIMIRROR_NO_SRC;
+ }
+ src_id = ids[seqnum];
+ }
+
+ rc = ubi_open(&ulib);
+ if (rc != 0)
+ return ubi_error;
+
+ fd_in = ubi_vol_open(ulib, devno, src_id, O_RDONLY);
+ if (fd_in == -1){
+ EBUF("open error source volume %d", ids[i]);
+ rc = open_error;
+ goto err;
+ }
+
+ for (i = 0; i < ids_size; i++) {
+ if (ids[i] == src_id) /* skip self-mirror */
+ continue;
+
+ fd_out = ubi_vol_open(ulib, devno, ids[i], O_RDWR);
+ if (fd_out == -1){
+ EBUF("open error destination volume %d", ids[i]);
+ rc = open_error;
+ goto err;
+ }
+ rc = compare_files(fd_in, fd_out);
+ if (rc < 0 ){
+ EBUF("compare error volume %d and %d", src_id, ids[i]);
+ goto err;
+ }
+ rc = copy_files(fd_in, fd_out);
+ if (rc != 0) {
+ EBUF("mirror error volume %d to %d", src_id, ids[i]);
+ goto err;
+ }
+ if( (rc = ubi_vol_close(fd_out)) == -1 ){
+ EBUF("close error volume %d", ids[i]);
+ rc = close_error;
+ goto err;
+ }else
+ fd_out = -1;
+ }
+err:
+ if (fd_out != -1)
+ ubi_vol_close(fd_out);
+ if (fd_in != -1)
+ ubi_vol_close(fd_in);
+ if (ulib != NULL)
+ ubi_close(&ulib);
+ return rc;
+}
diff --git a/ubi-utils/src/libubiold.c b/ubi-utils/src/libubiold.c
new file mode 100644
index 0000000..0ff8bae
--- /dev/null
+++ b/ubi-utils/src/libubiold.c
@@ -0,0 +1,768 @@
+/*
+ * 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.
+ */
+
+/*
+ * UBI (Unsorted Block Images) library.
+ *
+ * Author: Artem B. Bityutskiy
+ * Oliver Lohmann
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <stdint.h>
+#include <mtd/ubi-user.h>
+#include <mtd/ubi-header.h>
+
+#include "libubiold.h"
+#include "libubiold_int.h"
+#include "libubiold_sysfs.h"
+
+/**
+ * struct ubi_lib - UBI library descriptor.
+ *
+ * @ubi general UBI information
+ *
+ * @sysfs_root sysfs root directory
+ * @ubi_root UBI root directory in sysfs
+ *
+ * @version full path to the "UBI version" sysfs file
+ *
+ * @cdev_path path pattern to UBI character devices
+ * @cdev_path_len maximum length of the @cdev_path string after substitution
+ * @udev_path path to sysfs directories corresponding to UBI devices
+ * @wear_path path to sysfs file containing UBI wear information
+ * @vol_count_path path to sysfs file containing the number of volumes in an
+ * UBI device
+ * @tot_ebs_path path to sysfs file containing the total number of
+ * eraseblock on an UBI device
+ * @avail_ebs_path path to sysfs file containing the number of unused
+ * eraseblocks on an UBI device, available for new volumes
+ * @eb_size_path path to sysfs file containing size of UBI eraseblocks
+ * @nums_path path to sysfs file containing major and minor number of an
+ * UBI device
+ * @vol_cdev_path path to UBI volume character devices
+ * @vdev_path path to sysfs directories corresponding to UBI volume
+ * devices
+ * @vol_nums_path path to sysfs file containing major and minor number of an
+ * UBI volume device
+ * @vol_bytes_path path to sysfs file containing size of an UBI volume device
+ * in bytes
+ * @vol_ebs_path path to sysfs file containing the number of eraseblocks in
+ * an UBI volume device
+ * @vol_type_path path to sysfs file containing type of an UBI volume
+ * @vol_name_path @FIXME: Describe me.
+ *
+ * This structure is created and initialized by 'ubi_init()' and is passed to
+ * all UBI library calls.
+ */
+struct ubi_lib
+{
+ struct ubi_info ubi;
+
+ char *sysfs_root;
+ char *ubi_root;
+
+ char *version;
+ char *cdev_path;
+ int cdev_path_len;
+ char *udev_path;
+ char *wear_path;
+ char *vol_count_path;
+ char *tot_ebs_path;
+ char *avail_ebs_path;
+ char *eb_size_path;
+ char *nums_path;
+ int vol_cdev_path_len;
+ char *vol_cdev_path;
+ char *vdev_path;
+ char *vol_nums_path;
+ char *vol_bytes_path;
+ char *vol_ebs_path;
+ char *vol_type_path;
+ char *vol_name_path;
+};
+
+
+/**
+ * mkpath - compose full path from 2 given components.
+ *
+ * @path first component @name second component
+ *
+ * Returns the resulting path in case of success and %NULL in case of failure.
+ * Callers have to take care the resulting path is freed.
+ */
+static char*
+mkpath(const char *path, const char *name)
+{
+ char *n;
+ int len1 = strlen(path);
+ int len2 = strlen(name);
+
+ n = malloc(len1 + len2 + 2);
+ if (!n)
+ return NULL;
+
+ memcpy(n, path, len1);
+ if (n[len1 - 1] != '/')
+ n[len1++] = '/';
+
+ memcpy(n + len1, name, len2 + 1);
+ return n;
+}
+
+
+static int
+get_ubi_info(ubi_lib_t desc, struct ubi_info *ubi)
+{
+ int err;
+ int dev_count = 0;
+ char *path;
+ struct stat stat;
+
+ err = sysfs_read_int(desc->version, (int*) &ubi->version);
+ if (err)
+ return -1;
+
+ /* Calculate number of UBI devices */
+ while (!err) {
+ char dir[20];
+
+ sprintf(&dir[0], "ubi%d", dev_count);
+ path = mkpath(desc->ubi_root, dir);
+ if (!path)
+ return ENOMEM;
+
+ err = lstat(path, &stat);
+ if (err == 0)
+ dev_count += 1;
+ free(path);
+ }
+
+ if (errno != ENOENT)
+ return -1;
+
+ if (dev_count == 0) {
+ ubi_err("no UBI devices found");
+ errno = EINVAL;
+ return -1;
+ }
+
+ errno = 0;
+ ubi->dev_count = dev_count;
+ return 0;
+}
+
+void
+ubi_dump_handler(ubi_lib_t desc)
+{
+ ubi_lib_t d = desc;
+ printf( "UBI Library Descriptor:\n"
+ "ubi_root: %s\n"
+ "version: %s\n"
+ "cdev_path: %s\n"
+ "udev_path: %s\n"
+ "wear_path: %s\n"
+ "vol_count_path: %s\n"
+ "tot_ebs_path: %s\n"
+ "avail_ebs_path: %s\n"
+ "eb_size_path: %s\n"
+ "nums_path: %s\n"
+ "vol_cdev_path: %s\n"
+ "vdev_path: %s\n"
+ "vol_nums_path: %s\n"
+ "vol_bytes_path: %s\n"
+ "vol_ebs_path: %s\n"
+ "vol_type_path: %s\n"
+ "vol_name_path: %s\n"
+ "cdev_path_len: %d\n\n",
+ d->ubi_root, d->version, d->cdev_path, d->udev_path,
+ d->wear_path, d->vol_count_path, d->tot_ebs_path,
+ d->avail_ebs_path, d->eb_size_path, d->nums_path,
+ d->vol_cdev_path, d->vdev_path, d->vol_nums_path,
+ d->vol_bytes_path, d->vol_ebs_path, d->vol_type_path,
+ d->vol_name_path, d->cdev_path_len);
+}
+
+int
+ubi_set_cdev_pattern(ubi_lib_t desc, const char *pattern)
+{
+ char *patt;
+
+ patt = strdup(pattern);
+ if (!patt) {
+ ubi_err("cannot allocate memory");
+ return -1;
+ }
+
+ if (desc->cdev_path)
+ free(desc->cdev_path);
+
+ desc->cdev_path = patt;
+ desc->cdev_path_len = strlen(patt) + 1 + UBI_MAX_ID_SIZE;
+
+ ubi_dbg("ubi dev pattern is now \"%s\"", patt);
+
+ return 0;
+}
+
+int
+ubi_set_vol_cdev_pattern(ubi_lib_t desc, const char *pattern)
+{
+ char *patt;
+
+ patt = strdup(pattern);
+ if (!patt) {
+ ubi_err("cannot allocate memory");
+ return -1;
+ }
+
+ free(desc->vol_cdev_path);
+ desc->vol_cdev_path = patt;
+ desc->vol_cdev_path_len = strlen(patt) + 1 + 2 * UBI_MAX_ID_SIZE;
+
+ ubi_dbg("ubi volume dev pattern is now \"%s\"", patt);
+
+ return 0;
+}
+
+int
+ubi_open(ubi_lib_t *desc)
+{
+ int err = -1;
+ ubi_lib_t res;
+ struct stat stat;
+
+ res = calloc(1, sizeof(struct ubi_lib));
+ if (!res) {
+ ubi_err("cannot allocate memory");
+ return -1;
+ }
+
+ res->cdev_path = NULL;
+ err = ubi_set_cdev_pattern(res, UBI_CDEV_PATH);
+ if (err)
+ goto error;
+
+ /* TODO: this actually has to be discovered */
+ res->sysfs_root = strdup(UBI_SYSFS_ROOT);
+ if (!res->sysfs_root)
+ goto error;
+
+ res->ubi_root = mkpath(res->sysfs_root, UBI_ROOT);
+ if (!res->ubi_root)
+ goto error;
+
+ res->version = mkpath(res->ubi_root, UBI_VER);
+ if (!res->version)
+ goto error;
+
+ res->udev_path = mkpath(res->ubi_root, "ubi%d/");
+ if (!res->udev_path)
+ goto error;
+
+ res->wear_path = mkpath(res->udev_path, UBI_WEAR);
+ if (!res->wear_path)
+ goto error;
+
+ res->vol_count_path = mkpath(res->udev_path, UBI_VOL_COUNT);
+ if (!res->vol_count_path)
+ goto error;
+
+ res->tot_ebs_path = mkpath(res->udev_path, UBI_AVAIL_EBS);
+ if (!res->tot_ebs_path)
+ goto error;
+
+ res->avail_ebs_path = mkpath(res->udev_path, UBI_TOT_EBS);
+ if (!res->avail_ebs_path)
+ goto error;
+
+ res->eb_size_path = mkpath(res->udev_path, UBI_EB_SIZE);
+ if (!res->eb_size_path)
+ goto error;
+
+ res->nums_path = mkpath(res->udev_path, UBI_NUMS);
+ if (!res->nums_path)
+ goto error;
+
+ err = ubi_set_vol_cdev_pattern(res, UBI_VOL_CDEV_PATH);
+ if (err)
+ goto error;
+
+ res->vdev_path = mkpath(res->ubi_root, "ubi%d_%d/");
+ if (!res->vdev_path)
+ goto error;
+
+ res->vol_nums_path = mkpath(res->vdev_path, UBI_NUMS);
+ if (!res->vol_nums_path)
+ goto error;
+
+ res->vol_bytes_path = mkpath(res->vdev_path, UBI_VBYTES);
+ if (!res->vol_bytes_path)
+ goto error;
+
+ res->vol_ebs_path = mkpath(res->vdev_path, UBI_VEBS);
+ if (!res->vol_ebs_path)
+ goto error;
+
+ res->vol_type_path = mkpath(res->vdev_path, UBI_VTYPE);
+ if (!res->vol_type_path)
+ goto error;
+
+ res->vol_name_path = mkpath(res->vdev_path, UBI_VNAME);
+ if (!res->vol_name_path)
+ goto error;
+
+ /* Check if UBI exists in the system */
+ err = lstat(res->ubi_root, &stat);
+ if (err) {
+ perror("lstat");
+ fprintf(stderr, "%s\n", res->ubi_root);
+ err = UBI_ENOTFOUND;
+ goto error;
+ }
+
+ err = get_ubi_info(res, &res->ubi);
+ if (err)
+ goto error;
+
+ *desc = res;
+
+ ubi_dbg("opened library successfully.");
+
+ return 0;
+
+error:
+ ubi_close(&res);
+
+ if (err == -1 && errno == ENOMEM)
+ ubi_err("Cannot allocate memory");
+
+ return err;
+}
+
+int
+ubi_close(ubi_lib_t *desc)
+{
+ ubi_lib_t tmp = *desc;
+
+ free(tmp->vol_name_path);
+ free(tmp->vol_type_path);
+ free(tmp->vol_ebs_path);
+ free(tmp->vol_bytes_path);
+ free(tmp->vol_nums_path);
+ free(tmp->vdev_path);
+ free(tmp->vol_cdev_path);
+ free(tmp->nums_path);
+ free(tmp->eb_size_path);
+ free(tmp->avail_ebs_path);
+ free(tmp->tot_ebs_path);
+ free(tmp->vol_count_path);
+ free(tmp->wear_path);
+ free(tmp->udev_path);
+ free(tmp->cdev_path);
+ free(tmp->version);
+ free(tmp->ubi_root);
+ free(tmp->sysfs_root);
+ free(tmp);
+
+ *desc = NULL;
+
+ return 0;
+}
+
+void
+ubi_perror(const char *prefix, int code)
+{
+ if (code == 0)
+ return;
+
+ fprintf(stderr, "%s: ", prefix);
+
+ switch (code) {
+ case UBI_ENOTFOUND:
+ fprintf(stderr, "UBI was not found in system\n");
+ break;
+ case UBI_EBUG:
+ fprintf(stderr, "an UBI or UBI library bug\n");
+ break;
+ case UBI_EINVAL:
+ fprintf(stderr, "invalid parameter\n");
+ break;
+ case -1:
+ perror(prefix);
+ break;
+ default:
+ ubi_err("unknown error code %d", code);
+ break;
+ }
+}
+
+int
+ubi_get_dev_info(ubi_lib_t desc, unsigned int devn, struct ubi_dev_info *di)
+{
+ int err;
+
+ if (devn >= desc->ubi.dev_count) {
+ ubi_err("bad device number, max is %d\n",
+ desc->ubi.dev_count - 1);
+ return UBI_EINVAL;
+ }
+
+ err = sysfs_read_dev_subst(desc->nums_path, &di->major,
+ &di->minor, 1, devn);
+ if (err)
+ return -1;
+
+ err = sysfs_read_ull_subst(desc->wear_path, &di->wear, 1, devn);
+ if (err)
+ return -1;
+
+ err = sysfs_read_uint_subst(desc->vol_count_path,
+ &di->vol_count, 1, devn);
+ if (err)
+ return -1;
+
+ err = sysfs_read_uint_subst(desc->eb_size_path, &di->eb_size, 1, devn);
+ if (err)
+ return -1;
+
+ err = sysfs_read_uint_subst(desc->tot_ebs_path, &di->total_ebs, 1, devn);
+ if (err)
+ return -1;
+
+ err = sysfs_read_uint_subst(desc->avail_ebs_path,
+ &di->avail_ebs, 1, devn);
+ if (err)
+ return -1;
+
+#if 0
+ ubi_dbg("major:minor %d:%d, wear %llu, EB size %d, "
+ "vol. count %d, tot. EBs %d, avail. EBs %d",
+ di->major, di->minor, di->wear, di->eb_size,
+ di->vol_count, di->total_ebs, di->avail_ebs);
+#endif
+
+ return err;
+}
+
+int
+ubi_get_vol_info(ubi_lib_t desc, unsigned int devn, unsigned int vol_id,
+ struct ubi_vol_info *req)
+{
+ int err;
+ int len;
+ char buf1[10];
+ char buf2[UBI_MAX_VOLUME_NAME];
+
+ err = sysfs_read_dev_subst(desc->vol_nums_path, &req->major,
+ &req->minor, 2, devn, vol_id);
+ if (err)
+ return -1;
+
+ err = sysfs_read_ull_subst(desc->vol_bytes_path,
+ &req->bytes, 2, devn, vol_id);
+ if (err)
+ return -1;
+
+ err = sysfs_read_uint_subst(desc->vol_ebs_path,
+ &req->eraseblocks, 2, devn, vol_id);
+ if (err)
+ return -1;
+
+ len = sysfs_read_data_subst(desc->vol_type_path, &buf1[0],
+ 10, 2, devn, vol_id);
+ if (len == -1)
+ return -1;
+
+ if (buf1[len - 1] != '\n') {
+ ubi_err("bad volume type");
+ return UBI_EBUG;
+ }
+
+ if (!strncmp(&buf1[0], "static", sizeof("static") - 1)) {
+ req->type = UBI_STATIC_VOLUME;
+ } else if (!strncmp(&buf1[0], "dynamic", sizeof("dynamic") - 1)) {
+ req->type = UBI_DYNAMIC_VOLUME;
+ } else {
+ ubi_err("bad type %s", &buf1[0]);
+ return -1;
+ }
+
+ len = sysfs_read_data_subst(desc->vol_name_path, &buf2[0],
+ UBI_MAX_VOLUME_NAME, 2, devn, vol_id);
+ if (len == -1)
+ return -1;
+
+ if (buf2[len - 1] != '\n') {
+ ubi_err("bad volume name");
+ return UBI_EBUG;
+ }
+
+ req->name = malloc(len);
+ if (!req->name) {
+ ubi_err("cannot allocate memory");
+ return -1;
+ }
+
+ memcpy(req->name, &buf2[0], len - 1);
+ req->name[len - 1] = '\0';
+
+ return 0;
+}
+
+/**
+ * ubi_cdev_open - open a UBI device
+ *
+ * @desc UBI library descriptor
+ * @devn Number of UBI device to open
+ * @flags Flags to pass to open()
+ *
+ * This function opens a UBI device by number and returns a file
+ * descriptor. In case of an error %-1 is returned and errno is set
+ * appropriately.
+ */
+static int
+ubi_cdev_open(ubi_lib_t desc, int devn, int flags)
+{
+ char *buf;
+ int fd;
+
+ ubi_dbg("desc=%p, devn=%d, flags=%08x\n", desc, devn, flags);
+
+ if (desc == NULL) {
+ ubi_err("desc is NULL\n");
+ return -1;
+ }
+ if (desc->vol_cdev_path_len == 0) {
+ ubi_err("path_len == 0\n");
+ return -1;
+ }
+ buf = malloc(desc->cdev_path_len);
+
+ sprintf(buf, desc->cdev_path, devn);
+
+ fd = open(buf, flags);
+ if (fd == -1)
+ ubi_dbg("cannot open %s", buf);
+
+ free(buf);
+ return fd;
+}
+
+/**
+ * ubi_cdev_close - close a UBI device
+ *
+ * @dev_fd file descriptor of UBI device to close
+ *
+ * This function closes the given UBI device.
+ */
+static int
+ubi_cdev_close(int dev_fd)
+{
+ return close(dev_fd);
+}
+
+/**
+ * @size is now in bytes.
+ */
+int
+ubi_mkvol(ubi_lib_t desc, int devn, int vol_id, int vol_type,
+ long long bytes, int alignment, const char *name)
+{
+ int fd;
+ int err;
+ struct ubi_mkvol_req req;
+ size_t n;
+
+ n = strlen(name);
+ if (n > UBI_MAX_VOLUME_NAME)
+ return -1;
+
+ if ((fd = ubi_cdev_open(desc, devn, O_RDWR)) == -1)
+ return -1;
+
+ req.vol_id = vol_id;
+ req.bytes = bytes;
+ req.vol_type = vol_type;
+ req.alignment = alignment;
+
+ strncpy(req.name, name, UBI_MAX_VOLUME_NAME + 1);
+ req.name_len = n;
+
+ /* printf("DBG: %s(vol_id=%d, bytes=%lld, type=%d, alig=%d, nlen=%d, "
+ "name=%s)\n", __func__, vol_id, bytes, vol_type, alignment,
+ strlen(name), name);*/
+
+ err = ioctl(fd, UBI_IOCMKVOL, &req);
+ if (err < 0) {
+ ubi_err("ioctl returned %d errno=%d\n", err, errno);
+ goto out_close;
+ }
+
+ ubi_dbg("created volume %d, size %lld, name \"%s\" "
+ "at UBI dev %d\n", vol_id, bytes, name, devn);
+
+ close(fd);
+ return err;
+ out_close:
+ ubi_cdev_close(fd);
+ return err;
+}
+
+int
+ubi_rmvol(ubi_lib_t desc, int devn, int vol_id)
+{
+ int fd;
+ int err;
+
+ if ((fd = ubi_cdev_open(desc, devn, O_RDWR)) == -1)
+ return -1;
+
+ err = ioctl(fd, UBI_IOCRMVOL, &vol_id);
+ if (err < 0)
+ goto out_close;
+
+ ubi_dbg("removed volume %d", vol_id);
+
+ out_close:
+ ubi_cdev_close(fd);
+ return err;
+}
+
+int
+ubi_get_info(ubi_lib_t desc, struct ubi_info *ubi)
+{
+ memcpy(ubi, &desc->ubi, sizeof(struct ubi_info));
+ return 0;
+}
+
+
+int
+ubi_vol_open(ubi_lib_t desc, int devn, int vol_id, int flags)
+{
+ char *buf;
+ int fd;
+
+ ubi_dbg("desc=%p, devn=%d, vol_id=%d, flags=%08x\n",
+ desc, devn, vol_id, flags);
+
+ if (desc == NULL) {
+ ubi_err("desc is NULL\n");
+ return -1;
+ }
+ if (desc->vol_cdev_path_len == 0) {
+ ubi_err("path_len == 0\n");
+ return -1;
+ }
+ buf = malloc(desc->cdev_path_len);
+
+ sprintf(buf, desc->vol_cdev_path, devn, vol_id);
+
+ fd = open(buf, flags);
+ if (fd == -1)
+ ubi_dbg("cannot open %s", buf);
+
+ free(buf);
+ return fd;
+}
+
+int
+ubi_vol_close(int vol_fd)
+{
+ return close(vol_fd);
+}
+
+
+int
+ubi_vol_update(int vol_fd, unsigned long long bytes)
+{
+ int err;
+
+ err = ioctl(vol_fd, UBI_IOCVOLUP, &bytes);
+ if (err) {
+ ubi_err("%s failure calling update ioctl\n"
+ " IOCTL(%08lx) err=%d errno=%d\n",
+ __func__, (long unsigned int)UBI_IOCVOLUP, err, errno);
+ }
+ return err;
+}
+
+FILE *
+ubi_vol_fopen_read(ubi_lib_t desc, int devn, uint32_t vol_id)
+{
+ FILE *fp;
+ int fd;
+
+ fd = ubi_vol_open(desc, devn, vol_id, O_RDONLY);
+ if (fd == -1)
+ return NULL;
+
+ fp = fdopen(fd, "r");
+ if (fp == NULL)
+ ubi_vol_close(fd);
+
+ return fp;
+}
+
+FILE *
+ubi_vol_fopen_update(ubi_lib_t desc, int devn, uint32_t vol_id,
+ unsigned long long bytes)
+{
+ FILE *fp;
+ int fd;
+ int err;
+
+ fd = ubi_vol_open(desc, devn, vol_id, O_RDWR);
+ if (fd == -1)
+ return NULL;
+
+ fp = fdopen(fd, "r+");
+ if (fp == NULL) {
+ printf("DBG: %s(errno=%d)\n", __func__, errno);
+ ubi_vol_close(fd);
+ return NULL;
+ }
+ err = ubi_vol_update(fd, bytes);
+ if (err < 0) {
+ printf("DBG: %s() fd=%d err=%d\n", __func__, fd, err);
+ fclose(fp);
+ return NULL;
+ }
+ return fp;
+}
+
+int
+ubi_vol_get_used_bytes(int vol_fd, unsigned long long *bytes)
+{
+ off_t res;
+
+ res = lseek(vol_fd, 0, SEEK_END);
+ if (res == (off_t)-1)
+ return -1;
+ *bytes = (unsigned long long) res;
+ res = lseek(vol_fd, 0, SEEK_SET);
+ return res == (off_t)-1 ? -1 : 0;
+}
diff --git a/ubi-utils/src/libubiold_int.h b/ubi-utils/src/libubiold_int.h
new file mode 100644
index 0000000..830a682
--- /dev/null
+++ b/ubi-utils/src/libubiold_int.h
@@ -0,0 +1,119 @@
+/*
+ * 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.
+ */
+
+/*
+ * UBI (Unsorted Block Images) library.
+ *
+ * Author: Artem B. Bityutskiy
+ */
+
+#ifndef __UBI_INT_H__
+#define __UBI_INT_H__
+/*
+ * Enable/disable UBI library debugging messages.
+ */
+#undef UBILIB_DEBUG
+
+/*
+ * UBI library error message.
+ */
+#define ubi_err(fmt, ...) do { \
+ fprintf(stderr, "UBI Library Error at %s: ", __func__); \
+ fprintf(stderr, fmt, ##__VA_ARGS__); \
+ fprintf(stderr, "\n"); \
+ } while (0)
+
+#ifdef UBILIB_DEBUG
+#define ubi_dbg(fmt, ...) do { \
+ fprintf(stderr, "UBI Debug: %s: ", __func__); \
+ fprintf(stderr, fmt, ##__VA_ARGS__); \
+ fprintf(stderr, "\n"); \
+ } while (0)
+
+#else
+#define ubi_dbg(fmt, ...) do { } while (0)
+#endif
+
+/**
+ * SYSFS Entries.
+ *
+ * @def UBI_ROOT
+ * @brief Name of the root UBI directory in sysfs.
+ *
+ * @def UBI_NLEN_MAX
+ * @brief Name of syfs file containing the maximum UBI volume name length.
+ *
+ * @def UBI_VER
+ * @brief Name of sysfs file containing UBI version.
+ *
+ * @def UBI_WEAR
+ * @brief Name of sysfs file containing wear level of an UBI device.
+ *
+ * @def UBI_VOL_COUNT
+ * @brief Name of sysfs file contaning the of volume on an UBI device
+ *
+ * @def UBI_TOT_EBS
+ * @brief Name of sysfs file contaning the total number of
+ * eraseblocks on an UBI device.
+ *
+ * @def UBI_AVAIL_EBS
+ * @brief Name of sysfs file contaning the number of unused eraseblocks on
+ * an UBI device.
+ *
+ * @def UBI_EB_SIZE
+ * @brief Name of sysfs file containing size of UBI eraseblocks.
+ *
+ * @def UBI_NUMS
+ * @brief Name of sysfs file containing major and minor numbers
+ * of an UBI device or an UBI volume device.
+ *
+ * @def UBI_VBYTES
+ * @brief Name of sysfs file containing size of an UBI volume device in
+ * bytes.
+ *
+ * @def UBI_VEBS
+ * @brief Name of sysfs file containing size of an UBI volume device in
+ * eraseblocks.
+ *
+ * @def UBI_VTYPE
+ * @brief Name of sysfs file containing type of an UBI volume device.
+ *
+ * @def UBI_VNAME
+ * @brief Name of sysfs file containing name of an UBI volume device.
+ **/
+#define UBI_ROOT "ubi"
+#define UBI_NLEN_MAX "volume_name_max"
+#define UBI_VER "version"
+#define UBI_WEAR "wear"
+#define UBI_VOL_COUNT "volumes_count"
+#define UBI_TOT_EBS "total_eraseblocks"
+#define UBI_AVAIL_EBS "avail_eraseblocks"
+#define UBI_EB_SIZE "eraseblock_size"
+#define UBI_NUMS "dev"
+#define UBI_VBYTES "bytes"
+#define UBI_VEBS "eraseblocks"
+#define UBI_VTYPE "type"
+#define UBI_VNAME "name"
+
+#define UBI_CDEV_PATH "/dev/ubi%d"
+#define UBI_VOL_CDEV_PATH "/dev/ubi%d_%d"
+#define UBI_SYSFS_ROOT "/sys/class"
+
+#define UBI_MAX_ID_SIZE 9
+
+#endif /* !__UBI_INT_H__ */
diff --git a/ubi-utils/src/libubiold_sysfs.c b/ubi-utils/src/libubiold_sysfs.c
new file mode 100644
index 0000000..c4860f6
--- /dev/null
+++ b/ubi-utils/src/libubiold_sysfs.c
@@ -0,0 +1,232 @@
+/*
+ * 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.
+ */
+
+/*
+ * UBI (Unsorted Block Images) library.
+ *
+ * Author: Artem B. Bityutskiy
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <limits.h>
+#include <errno.h>
+#include <stdarg.h>
+
+#include "config.h"
+#include "libubiold_int.h"
+
+int
+sysfs_read_data(const char *file, void *buf, int len)
+{
+ int fd;
+ ssize_t rd;
+
+ fd = open(file, O_RDONLY);
+ if (fd == -1) {
+ ubi_err("cannot open file %s", file);
+ return -1;
+ }
+
+ rd = read(fd, buf, len);
+ if (rd == -1)
+ ubi_err("cannot read file %s", file);
+
+ close(fd);
+
+ return rd;
+}
+
+int
+sysfs_read_data_subst(const char *patt, void *buf, int len, int n, ...)
+{
+ va_list args;
+ char buf1[strlen(patt) + 20 * n];
+
+ va_start(args, n);
+ vsprintf(&buf1[0], patt, args);
+ va_end(args);
+
+ return sysfs_read_data(&buf1[0], buf, len);
+}
+
+int
+sysfs_read_dev(const char *file, unsigned int *major, unsigned int *minor)
+{
+ int fd;
+ int ret;
+ ssize_t rd;
+ int err = -1;
+ char buf[40];
+
+ fd = open(file, O_RDONLY);
+ if (fd == -1) {
+ ubi_err("cannot open file %s", file);
+ return -1;
+ }
+
+ rd = read(fd, &buf[0], 20);
+ if (rd == -1) {
+ ubi_err("cannot read file %s", file);
+ goto error;
+ }
+ if (rd < 4) {
+ ubi_err("bad contents of file %s:", file);
+ goto error;
+ }
+
+ err = -1;
+ if (buf[rd -1] != '\n') {
+ ubi_err("bad contents of file %s", file);
+ goto error;
+ }
+
+ ret = sscanf(&buf[0], "%d:%d\n", major, minor);
+ if (ret != 2) {
+ ubi_err("bad contents of file %s", file);
+ goto error;
+ }
+
+ err = 0;
+
+error:
+ close(fd);
+
+ return err;
+}
+
+int
+sysfs_read_dev_subst(const char *patt, unsigned int *major,
+ unsigned int *minor, int n, ...)
+{
+ va_list args;
+ char buf[strlen(patt) + 20 * n];
+
+ va_start(args, n);
+ vsprintf(&buf[0], patt, args);
+ va_end(args);
+
+ return sysfs_read_dev(&buf[0], major, minor);
+}
+
+static int
+sysfs_read_ull(const char *file __unused, unsigned long long *num __unused)
+{
+ return 0;
+}
+
+int
+sysfs_read_ull_subst(const char *patt, unsigned long long *num, int n, ...)
+{
+ va_list args;
+ char buf[strlen(patt) + 20 * n];
+
+ va_start(args, n);
+ vsprintf(&buf[0], patt, args);
+ va_end(args);
+
+ return sysfs_read_ull(&buf[0], num);
+}
+
+static int
+sysfs_read_uint(const char *file __unused, unsigned int *num __unused)
+{
+ return 0;
+}
+
+int
+sysfs_read_uint_subst(const char *patt, unsigned int *num, int n, ...)
+{
+ va_list args;
+ char buf[strlen(patt) + 20 * n];
+
+ va_start(args, n);
+ vsprintf(&buf[0], patt, args);
+ va_end(args);
+
+ return sysfs_read_uint(&buf[0], num);
+}
+
+int
+sysfs_read_ll(const char *file, long long *num)
+{
+ int fd;
+ ssize_t rd;
+ int err = -1;
+ char buf[20];
+ char *endptr;
+
+ fd = open(file, O_RDONLY);
+ if (fd == -1)
+ return -1;
+
+ rd = read(fd, &buf[0], 20);
+ if (rd == -1)
+ goto out;
+
+ if (rd < 2) {
+ ubi_err("bad contents in file %s: \"%c%c...\"",
+ file, buf[0], buf[1]);
+ goto out_errno;
+ }
+
+ *num = strtoll(&buf[0], &endptr, 10);
+ if (endptr == &buf[0] || *endptr != '\n') {
+ ubi_err("bad contents in file %s: \"%c%c...\"",
+ file, buf[0], buf[1]);
+ goto out_errno;
+ }
+
+ if (*num < 0) {
+ ubi_err("bad number in file %s: %lld", file, *num);
+ goto out_errno;
+ }
+
+ err = 0;
+
+out_errno:
+ errno = EINVAL;
+
+out:
+ close(fd);
+ return err;
+}
+
+int
+sysfs_read_int(const char *file, int *num)
+{
+ int err;
+ long long res = 0;
+
+ err = sysfs_read_ll(file, &res);
+ if (err)
+ return err;
+
+ if (res < 0 || res > INT_MAX) {
+ ubi_err("bad number in file %s: %lld", file, res);
+ errno = EINVAL;
+ return -1;
+ }
+
+ *num = res;
+ return 0;
+}
diff --git a/ubi-utils/src/libubiold_sysfs.h b/ubi-utils/src/libubiold_sysfs.h
new file mode 100644
index 0000000..2fb6072
--- /dev/null
+++ b/ubi-utils/src/libubiold_sysfs.h
@@ -0,0 +1,109 @@
+/*
+ * 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.
+ */
+
+/*
+ * UBI (Unsorted Block Images) library.
+ *
+ * Author: Artem B. Bityutskiy
+ */
+
+/**
+ * sysfs_read_data - read data from a sysfs file.
+ *
+ * @file path to the file to read from
+ * @buf furrer where to store read data
+ * @len length of provided buffer @buf
+ *
+ * This function returns the number of read bytes or -1 in case of error.
+ */
+int sysfs_read_data(const char *file, void *buf, int len);
+
+/**
+ * sysfs_read_data_subst - form path to a sysfs file and read data from it.
+ *
+ * @patt path to the file to read from
+ * @buf furrer where to store read data
+ * @len length of provided buffer @buf
+ * @n number of parameters to substitute to @patt
+ *
+ * This function forms path to a sysfs file by means of substituting parameters
+ * to @patt and then reads @len bytes from this file and stores the read data
+ * to @buf. This function returns the number of read bytes or -1 in case of
+ * error.
+ */
+int sysfs_read_data_subst(const char *patt, void *buf, int len, int n, ...);
+
+/**
+ * sysfs_read_dev - read major and minor number from a sysfs file.
+ *
+ * @file path to the file to read from
+ * @major major number is returned here
+ * @minor minor number is returned here
+ */
+int sysfs_read_dev(const char *file, unsigned int *major,
+ unsigned int *minor);
+/**
+ * sysfs_read_dev_subst - for path to a file and read major and minor number
+ * from it.
+ *
+ * @patt pattern of the path to the file to read from
+ * @major major number is returned here
+ * @minor minor number is returned here
+ * @n number of arguments to substitute
+ *
+ * This function substitures arguments to the @patt file path pattern and reads
+ * major and minor numbers from the resulting file.
+ */
+int sysfs_read_dev_subst(const char *patt, unsigned int *major,
+ unsigned int *minor, int n, ...);
+
+/**
+ * sysfs_read_ull_subst - form path to a sysfs file and read an unsigned long
+ * long value from there.
+ *
+ * @patt pattern of file path
+ * @num the read value is returned here
+ * @n number of parameters to substitute
+ *
+ *
+ * This function first forms the path to a sysfs file by means of substituting
+ * passed parameters to the @patt string, and then read an 'unsigned long long'
+ * value from this file.
+ */
+int sysfs_read_ull_subst(const char *patt, unsigned long long *num,
+ int n, ...);
+
+/**
+ * sysfs_read_uint_subst - the same as 'sysfs_read_uint_subst()' but reads an
+ * unsigned int value.
+ */
+int sysfs_read_uint_subst(const char *patt, unsigned int *num,
+ int n, ...);
+
+/**
+ * sysfs_read_ll - read a long long integer from an UBI sysfs file.
+ *
+ * @file file name from where to read
+ * @num the result is returned here
+ */
+int sysfs_read_ll(const char *file, long long *num);
+
+/**
+ * sysfs_read_int - the same as 'sysfs_read_ll()' but reads an 'int' value.
+ */
+int sysfs_read_int(const char *file, int *num);
diff --git a/ubi-utils/src/list.c b/ubi-utils/src/list.c
new file mode 100644
index 0000000..6eb716b
--- /dev/null
+++ b/ubi-utils/src/list.c
@@ -0,0 +1,149 @@
+/*
+ * 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.
+ *
+ * Author: Oliver Lohmann
+ */
+
+#include <stdlib.h>
+#include <assert.h>
+#include <stdio.h>
+
+#include "list.h"
+
+list_t
+mk_empty(void)
+{
+ return (list_t) NULL;
+}
+
+int
+is_empty(list_t l)
+{
+ return l == NULL;
+}
+
+info_t
+head(list_t l)
+{
+ assert(!is_empty(l));
+ return l->info;
+}
+
+list_t
+tail(list_t l)
+{
+ assert(!is_empty(l));
+ return l->next;
+}
+
+list_t
+remove_head(list_t l)
+{
+ list_t res;
+ assert(!is_empty(l));
+
+ res = l->next;
+ free(l);
+ return res;
+}
+
+list_t
+cons(info_t e, list_t l)
+{
+ list_t res = malloc(sizeof(*l));
+ if (!res)
+ return NULL;
+ res->info = e;
+ res->next = l;
+
+ return res;
+}
+
+list_t
+prepend_elem(info_t e, list_t l)
+{
+ return cons(e,l);
+}
+
+list_t
+append_elem(info_t e, list_t l)
+{
+ if (is_empty(l)) {
+ return cons(e,l);
+ }
+ l->next = append_elem(e, l->next);
+
+ return l;
+}
+
+list_t
+insert_sorted(cmp_func_t cmp, info_t e, list_t l)
+{
+ if (is_empty(l))
+ return cons(e, l);
+
+ switch (cmp(e, l->info)) {
+ case -1:
+ case 0:
+ return l;
+ break;
+ case 1:
+ l->next = insert_sorted(cmp, e, l);
+ break;
+ default:
+ break;
+ }
+
+ /* never reached */
+ return NULL;
+}
+
+list_t
+remove_all(free_func_t free_func, list_t l)
+{
+ if (is_empty(l))
+ return l;
+ list_t lnext = l->next;
+
+ if (free_func && l->info) {
+ free_func(&(l->info));
+ }
+ free(l);
+
+ return remove_all(free_func, lnext);
+}
+
+
+info_t
+is_in(cmp_func_t cmp, info_t e, list_t l)
+{
+ return
+ (is_empty(l))
+ ? NULL
+ : (cmp(e, l->info)) == 0 ? l->info : is_in(cmp, e, l->next);
+}
+
+
+void
+apply(process_func_t process_func, list_t l)
+{
+ list_t ptr;
+ void *i;
+ foreach(i, ptr, l) {
+ process_func(i);
+ }
+}
diff --git a/ubi-utils/src/list.h b/ubi-utils/src/list.h
new file mode 100644
index 0000000..e8452a2
--- /dev/null
+++ b/ubi-utils/src/list.h
@@ -0,0 +1,56 @@
+#ifndef __LIST_H__
+#define __LIST_H__
+/*
+ * 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.
+ *
+ * Author: Oliver Lohmann
+ */
+
+#include <stdint.h>
+
+#define foreach(elem, ptr, list) \
+ for (elem = list != NULL ? (typeof(elem)) head(list) \
+ : NULL, ptr = list; \
+ ptr != NULL; \
+ ptr = tail(ptr), \
+ elem = (typeof(elem)) ptr ? head(ptr) : NULL)
+
+typedef struct node* list_t;
+typedef void* info_t;
+typedef int (*free_func_t)(info_t*);
+typedef int (*cmp_func_t)(info_t, info_t);
+typedef void (*process_func_t)(info_t);
+
+struct node {
+ list_t next;
+ info_t info;
+};
+
+list_t mk_empty(void);
+int is_empty(list_t l);
+info_t is_in(cmp_func_t cmp, info_t e, list_t l);
+info_t head(list_t l);
+list_t tail(list_t l);
+list_t remove_head(list_t l);
+list_t cons(info_t e, list_t l);
+list_t prepend_elem(info_t e, list_t);
+list_t append_elem(info_t e, list_t);
+list_t remove_all(free_func_t free_func, list_t l);
+list_t insert_sorted(cmp_func_t cmp_func, info_t e, list_t l);
+void apply(process_func_t process_func, list_t l);
+
+#endif /* __LIST_H__ */
diff --git a/ubi-utils/src/mkbootenv.c b/ubi-utils/src/mkbootenv.c
new file mode 100644
index 0000000..4a8cc6a
--- /dev/null
+++ b/ubi-utils/src/mkbootenv.c
@@ -0,0 +1,170 @@
+/*
+ * 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.
+ *
+ * Author: Oliver Lohmann
+ *
+ * Create boot-parameter/pdd data from an ASCII-text input file.
+ *
+ * 1.2 Removed argp because we want to use uClibc.
+ * 1.3 Minor cleanup
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <errno.h>
+#include <mtd/ubi-header.h>
+
+#include "config.h"
+#include "bootenv.h"
+#include "error.h"
+
+#define PROGRAM_VERSION "1.3"
+
+static char doc[] = "\nVersion: " PROGRAM_VERSION "\n\tBuilt on "
+ BUILD_CPU" "BUILD_OS" at "__DATE__" "__TIME__"\n"
+ "\n"
+ "mkbootenv - processes bootenv text files and convertes "
+ "them into a binary format.\n";
+
+static const char copyright [] __attribute__((unused)) =
+ "Copyright (c) International Business Machines Corp., 2006";
+
+static const char *optionsstr =
+" -c, --copyright Print copyright informatoin.\n"
+" -o, --output=<fname> Write the output data to <output> instead of\n"
+" stdout.\n"
+" -?, --help Give this help list\n"
+" --usage Give a short usage message\n"
+" -V, --version Print program version\n";
+
+static const char *usage =
+"Usage: mkbootenv [-c?V] [-o <output>] [--copyright] [--output=<output>]\n"
+" [--help] [--usage] [--version] [bootenv-txt-file]\n";
+
+struct option long_options[] = {
+ { .name = "copyright", .has_arg = 0, .flag = NULL, .val = 'c' },
+ { .name = "output", .has_arg = 1, .flag = NULL, .val = 'o' },
+ { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' },
+ { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 },
+ { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' },
+ { NULL, 0, NULL, 0}
+};
+
+typedef struct myargs {
+ FILE* fp_in;
+ FILE* fp_out;
+
+ char *arg1;
+ char **options; /* [STRING...] */
+} myargs;
+
+static int
+parse_opt(int argc, char **argv, myargs *args)
+{
+ while (1) {
+ int key;
+
+ key = getopt_long(argc, argv, "co:?V", long_options, NULL);
+ if (key == -1)
+ break;
+
+ switch (key) {
+ case 'c':
+ fprintf(stderr, "%s\n", copyright);
+ exit(0);
+ break;
+ case 'o':
+ args->fp_out = fopen(optarg, "wb");
+ if ((args->fp_out) == NULL) {
+ fprintf(stderr, "Cannot open file %s "
+ "for output\n", optarg);
+ exit(1);
+ }
+ break;
+ case '?': /* help */
+ printf("%s", doc);
+ printf("%s", optionsstr);
+ printf("\nReport bugs to %s\n",
+ PACKAGE_BUGREPORT);
+ exit(0);
+ break;
+ case 'V':
+ printf("%s\n", PROGRAM_VERSION);
+ exit(0);
+ break;
+ default:
+ printf("%s", usage);
+ exit(-1);
+ }
+ }
+
+ if (optind < argc) {
+ args->fp_in = fopen(argv[optind++], "rb");
+ if ((args->fp_in) == NULL) {
+ fprintf(stderr, "Cannot open file %s for input\n",
+ argv[optind]);
+ exit(1);
+ }
+ }
+
+ return 0;
+}
+
+int
+main(int argc, char **argv) {
+ int rc = 0;
+ bootenv_t env;
+
+ myargs args = {
+ .fp_in = stdin,
+ .fp_out = stdout,
+ .arg1 = NULL,
+ .options = NULL,
+ };
+
+ parse_opt(argc, argv, &args);
+
+ rc = bootenv_create(&env);
+ if (rc != 0) {
+ err_msg("Cannot create bootenv handle.");
+ goto err;
+ }
+ rc = bootenv_read_txt(args.fp_in, env);
+ if (rc != 0) {
+ err_msg("Cannot read bootenv from input file.");
+ goto err;
+ }
+ rc = bootenv_write(args.fp_out, env);
+ if (rc != 0) {
+ err_msg("Cannot write bootenv to output file.");
+ goto err;
+ }
+
+ if (args.fp_in != stdin) {
+ fclose(args.fp_in);
+ }
+ if (args.fp_out != stdout) {
+ fclose(args.fp_out);
+ }
+
+err:
+ bootenv_destroy(&env);
+ return rc;
+}
diff --git a/ubi-utils/src/nand2bin.c b/ubi-utils/src/nand2bin.c
new file mode 100644
index 0000000..b8e4ea3
--- /dev/null
+++ b/ubi-utils/src/nand2bin.c
@@ -0,0 +1,312 @@
+/*
+ * 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.
+ *
+ * Author: Frank Haverkamp
+ *
+ * An utility to decompose NAND images and strip OOB off. Not yet finished ...
+ *
+ * 1.2 Removed argp because we want to use uClibc.
+ * 1.3 Minor cleanup
+ */
+
+#include <config.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <getopt.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "config.h"
+#include "nandecc.h"
+
+#define PROGRAM_VERSION "1.3"
+
+#define MAXPATH 1024
+#define MIN(x,y) ((x)<(y)?(x):(y))
+
+struct args {
+ const char *oob_file;
+ const char *output_file;
+ size_t pagesize;
+
+ /* special stuff needed to get additional arguments */
+ char *arg1;
+ char **options; /* [STRING...] */
+};
+
+static struct args myargs = {
+ .output_file = "data.bin",
+ .oob_file = "oob.bin",
+ .pagesize = 2048,
+ .arg1 = NULL,
+ .options = NULL,
+};
+
+static char doc[] = "\nVersion: " PROGRAM_VERSION "\n\t"
+ BUILD_OS" "BUILD_CPU" at "__DATE__" "__TIME__"\n"
+ "\nSplit data and OOB.\n";
+
+static const char *optionsstr =
+" -o, --output=<output> Data output file\n"
+" -O, --oob=<oob> OOB output file\n"
+" -p, --pagesize=<pagesize> NAND pagesize\n"
+" -?, --help Give this help list\n"
+" --usage Give a short usage message\n";
+
+static const char *usage =
+"Usage: nand2bin [-?] [-o <output>] [-O <oob>] [-p <pagesize>]\n"
+" [--output=<output>] [--oob=<oob>] [--pagesize=<pagesize>] [--help]\n"
+" [--usage] input.mif\n";
+
+struct option long_options[] = {
+ { .name = "output", .has_arg = 1, .flag = NULL, .val = 'o' },
+ { .name = "oob", .has_arg = 1, .flag = NULL, .val = 'O' },
+ { .name = "pagesize", .has_arg = 1, .flag = NULL, .val = 'p' },
+ { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' },
+ { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 },
+ { NULL, 0, NULL, 0}
+};
+
+/*
+ * str_to_num - Convert string into number and cope with endings like
+ * k, K, kib, KiB for kilobyte
+ * m, M, mib, MiB for megabyte
+ */
+uint32_t str_to_num(char *str)
+{
+ char *s = str;
+ ulong num = strtoul(s, &s, 0);
+
+ if (*s != '\0') {
+ if (strcmp(s, "KiB") == 0)
+ num *= 1024;
+ else if (strcmp(s, "MiB") == 0)
+ num *= 1024*1024;
+ else {
+ fprintf(stderr, "WARNING: Wrong number format "
+ "\"%s\", check your paramters!\n", str);
+ }
+ }
+ return num;
+}
+
+/*
+ * @brief Parse the arguments passed into the test case.
+ *
+ * @param argc The number of arguments
+ * @param argv The argument list
+ * @param args Pointer to program args structure
+ *
+ * @return error
+ *
+ */
+static int
+parse_opt(int argc, char **argv, struct args *args)
+{
+ while (1) {
+ int key;
+
+ key = getopt_long(argc, argv, "o:O:p:?", long_options, NULL);
+ if (key == -1)
+ break;
+
+ switch (key) {
+ case 'p': /* --pagesize<pagesize> */
+ args->pagesize = str_to_num(optarg);
+ break;
+
+ case 'o': /* --output=<output.bin> */
+ args->output_file = optarg;
+ break;
+
+ case 'O': /* --oob=<oob.bin> */
+ args->output_file = optarg;
+ break;
+
+ case '?': /* help */
+ printf("Usage: nand2bin [OPTION...] input.mif\n");
+ printf("%s", doc);
+ printf("%s", optionsstr);
+ printf("\nReport bugs to %s\n",
+ PACKAGE_BUGREPORT);
+ exit(0);
+ break;
+
+ case 'V':
+ printf("%s\n", PROGRAM_VERSION);
+ exit(0);
+ break;
+
+ default:
+ printf("%s", usage);
+ exit(-1);
+ }
+ }
+
+ if (optind < argc)
+ args->arg1 = argv[optind++];
+
+ return 0;
+}
+
+static int calc_oobsize(size_t pagesize)
+{
+ switch (pagesize) {
+ case 512: return 16;
+ case 2048: return 64;
+ default:
+ exit(EXIT_FAILURE);
+ }
+ return 0;
+}
+
+static inline void
+hexdump(FILE *fp, const uint8_t *buf, ssize_t size)
+{
+ int k;
+
+ for (k = 0; k < size; k++) {
+ fprintf(fp, "%02x ", buf[k]);
+ if ((k & 15) == 15)
+ fprintf(fp, "\n");
+ }
+}
+
+static int
+process_page(uint8_t* buf, uint8_t *oobbuf, size_t pagesize)
+{
+ int eccpoi, oobsize;
+ size_t i;
+
+ switch (pagesize) {
+ case 2048: oobsize = 64; eccpoi = 64 / 2; break;
+ case 512: oobsize = 16; eccpoi = 16 / 2; break;
+ default:
+ fprintf(stderr, "Unsupported page size: %zd\n", pagesize);
+ return -EINVAL;
+ }
+ memset(oobbuf, 0xff, oobsize);
+
+ for (i = 0; i < pagesize; i += 256, eccpoi += 3) {
+ oobbuf[eccpoi++] = 0x0;
+ /* Calculate ECC */
+ nand_calculate_ecc(&buf[i], &oobbuf[eccpoi]);
+ }
+ return 0;
+}
+
+static int decompose_image(FILE *in_fp, FILE *bin_fp, FILE *oob_fp,
+ size_t pagesize)
+{
+ int read, rc, page = 0;
+ size_t oobsize = calc_oobsize(pagesize);
+ uint8_t *buf = malloc(pagesize);
+ uint8_t *oob = malloc(oobsize);
+ uint8_t *calc_oob = malloc(oobsize);
+ uint8_t *calc_buf = malloc(pagesize);
+
+ if (!buf)
+ exit(EXIT_FAILURE);
+ if (!oob)
+ exit(EXIT_FAILURE);
+ if (!calc_oob)
+ exit(EXIT_FAILURE);
+ if (!calc_buf)
+ exit(EXIT_FAILURE);
+
+ while (!feof(in_fp)) {
+ read = fread(buf, 1, pagesize, in_fp);
+ if (ferror(in_fp)) {
+ fprintf(stderr, "I/O Error.");
+ exit(EXIT_FAILURE);
+ }
+ rc = fwrite(buf, 1, pagesize, bin_fp);
+ if (ferror(bin_fp)) {
+ fprintf(stderr, "I/O Error.");
+ exit(EXIT_FAILURE);
+ }
+ read = fread(oob, 1, oobsize, in_fp);
+ if (ferror(in_fp)) {
+ fprintf(stderr, "I/O Error.");
+ exit(EXIT_FAILURE);
+ }
+ rc = fwrite(oob, 1, oobsize, oob_fp);
+ if (ferror(bin_fp)) {
+ fprintf(stderr, "I/O Error.");
+ exit(EXIT_FAILURE);
+ }
+ process_page(buf, calc_oob, pagesize);
+
+ memcpy(calc_buf, buf, pagesize);
+
+ rc = nand_correct_data(calc_buf, oob);
+ if ((rc != -1) || (memcmp(buf, calc_buf, pagesize) != 0)) {
+ fprintf(stdout, "Page %d: data does not match!\n",
+ page);
+ }
+ page++;
+ }
+ free(calc_buf);
+ free(calc_oob);
+ free(oob);
+ free(buf);
+ return 0;
+}
+
+int
+main(int argc, char *argv[])
+{
+ FILE *in, *bin, *oob;
+
+ parse_opt(argc, argv, &myargs);
+
+ if (!myargs.arg1) {
+ fprintf(stderr, "Please specify input file!\n");
+ exit(EXIT_FAILURE);
+ }
+
+ in = fopen(myargs.arg1, "r");
+ if (!in) {
+ perror("Cannot open file");
+ exit(EXIT_FAILURE);
+ }
+ bin = fopen(myargs.output_file, "w+");
+ if (!bin) {
+ perror("Cannot open file");
+ exit(EXIT_FAILURE);
+ }
+ oob = fopen(myargs.oob_file, "w+");
+ if (!oob) {
+ perror("Cannot open file");
+ exit(EXIT_FAILURE);
+ }
+
+ decompose_image(in, bin, oob, myargs.pagesize);
+
+ fclose(in);
+ fclose(bin);
+ fclose(oob);
+
+ exit(EXIT_SUCCESS);
+}
diff --git a/ubi-utils/src/nandcorr.c b/ubi-utils/src/nandcorr.c
new file mode 100644
index 0000000..7f1a547
--- /dev/null
+++ b/ubi-utils/src/nandcorr.c
@@ -0,0 +1,85 @@
+/*
+ * 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.
+ */
+
+/*
+ * ECC algorithm for NAND FLASH. Detects and corrects 1 bit errors in
+ * a 256 bytes of data.
+ *
+ * Reimplement by Thomas Gleixner after staring long enough at the
+ * mess in drivers/mtd/nand/nandecc.c
+ *
+ */
+
+#include "nandecc.h"
+
+static int countbits(uint32_t byte)
+{
+ int res = 0;
+
+ for (;byte; byte >>= 1)
+ res += byte & 0x01;
+ return res;
+}
+
+int nand_correct_data(uint8_t *dat, const uint8_t *fail_ecc)
+
+{
+ uint8_t s0, s1, s2;
+
+ /*
+ * Do error detection
+ *
+ * Be careful, the index magic is due to a pointer to a
+ * uint32_t.
+ */
+ s0 = fail_ecc[1];
+ s1 = fail_ecc[2];
+ s2 = fail_ecc[3];
+
+ /* Check for a single bit error */
+ if( ((s0 ^ (s0 >> 1)) & 0x55) == 0x55 &&
+ ((s1 ^ (s1 >> 1)) & 0x55) == 0x55 &&
+ ((s2 ^ (s2 >> 1)) & 0x54) == 0x54) {
+
+ uint32_t byteoffs, bitnum;
+
+ byteoffs = (s1 << 0) & 0x80;
+ byteoffs |= (s1 << 1) & 0x40;
+ byteoffs |= (s1 << 2) & 0x20;
+ byteoffs |= (s1 << 3) & 0x10;
+
+ byteoffs |= (s0 >> 4) & 0x08;
+ byteoffs |= (s0 >> 3) & 0x04;
+ byteoffs |= (s0 >> 2) & 0x02;
+ byteoffs |= (s0 >> 1) & 0x01;
+
+ bitnum = (s2 >> 5) & 0x04;
+ bitnum |= (s2 >> 4) & 0x02;
+ bitnum |= (s2 >> 3) & 0x01;
+
+ dat[byteoffs] ^= (1 << bitnum);
+
+ return 1;
+ }
+
+ if(countbits(s0 | ((uint32_t)s1 << 8) | ((uint32_t)s2 <<16)) == 1)
+ return 1;
+
+ return -1;
+}
+
diff --git a/ubi-utils/src/nandecc.c b/ubi-utils/src/nandecc.c
new file mode 100644
index 0000000..71660ef
--- /dev/null
+++ b/ubi-utils/src/nandecc.c
@@ -0,0 +1,159 @@
+/*
+ * This file contains an ECC algorithm from Toshiba that detects and
+ * corrects 1 bit errors in a 256 byte block of data.
+ *
+ * drivers/mtd/nand/nand_ecc.c
+ *
+ * Copyright (C) 2000-2004 Steven J. Hill (sjhill@realitydiluted.com)
+ * Toshiba America Electronics Components, Inc.
+ *
+ * This file 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 or (at your option) any
+ * later version.
+ *
+ * This file 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 file; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * As a special exception, if other files instantiate templates or use
+ * macros or inline functions from these files, or you compile these
+ * files and link them with other works to produce a work based on these
+ * files, these files do not by themselves cause the resulting work to be
+ * covered by the GNU General Public License. However the source code for
+ * these files must still be made available in accordance with section (3)
+ * of the GNU General Public License.
+ *
+ * This exception does not invalidate any other reasons why a work based on
+ * this file might be covered by the GNU General Public License.
+ */
+
+#include "nandecc.h"
+
+/*
+ * Pre-calculated 256-way 1 byte column parity
+ */
+static const uint8_t nand_ecc_precalc_table[] = {
+ 0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a,
+ 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00,
+ 0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f,
+ 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
+ 0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c,
+ 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66,
+ 0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59,
+ 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03,
+ 0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33,
+ 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
+ 0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56,
+ 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c,
+ 0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55,
+ 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
+ 0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30,
+ 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a,
+ 0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30,
+ 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a,
+ 0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55,
+ 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
+ 0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56,
+ 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c,
+ 0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33,
+ 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
+ 0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59,
+ 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03,
+ 0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c,
+ 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66,
+ 0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f,
+ 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
+ 0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a,
+ 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00
+};
+
+/**
+ * nand_trans_result - [GENERIC] create non-inverted ECC
+ * @reg2: line parity reg 2
+ * @reg3: line parity reg 3
+ * @ecc_code: ecc
+ *
+ * Creates non-inverted ECC code from line parity
+ */
+static void nand_trans_result(uint8_t reg2, uint8_t reg3,
+ uint8_t *ecc_code)
+{
+ uint8_t a, b, i, tmp1, tmp2;
+
+ /* Initialize variables */
+ a = b = 0x80;
+ tmp1 = tmp2 = 0;
+
+ /* Calculate first ECC byte */
+ for (i = 0; i < 4; i++) {
+ if (reg3 & a) /* LP15,13,11,9 --> ecc_code[0] */
+ tmp1 |= b;
+ b >>= 1;
+ if (reg2 & a) /* LP14,12,10,8 --> ecc_code[0] */
+ tmp1 |= b;
+ b >>= 1;
+ a >>= 1;
+ }
+
+ /* Calculate second ECC byte */
+ b = 0x80;
+ for (i = 0; i < 4; i++) {
+ if (reg3 & a) /* LP7,5,3,1 --> ecc_code[1] */
+ tmp2 |= b;
+ b >>= 1;
+ if (reg2 & a) /* LP6,4,2,0 --> ecc_code[1] */
+ tmp2 |= b;
+ b >>= 1;
+ a >>= 1;
+ }
+
+ /* Store two of the ECC bytes */
+ ecc_code[1] = tmp1;
+ ecc_code[0] = tmp2;
+}
+
+/**
+ * nand_calculate_ecc - [NAND Interface] Calculate 3 byte ECC code for
+ * 256 byte block
+ *
+ * @dat: raw data
+ * @ecc_code: buffer for ECC
+ */
+int nand_calculate_ecc(const uint8_t *dat, uint8_t *ecc_code)
+{
+ uint8_t idx, reg1, reg2, reg3;
+ int j;
+
+ /* Initialize variables */
+ reg1 = reg2 = reg3 = 0;
+ ecc_code[0] = ecc_code[1] = ecc_code[2] = 0;
+
+ /* Build up column parity */
+ for(j = 0; j < 256; j++) {
+
+ /* Get CP0 - CP5 from table */
+ idx = nand_ecc_precalc_table[dat[j]];
+ reg1 ^= (idx & 0x3f);
+
+ /* All bit XOR = 1 ? */
+ if (idx & 0x40) {
+ reg3 ^= (uint8_t) j;
+ reg2 ^= ~((uint8_t) j);
+ }
+ }
+
+ /* Create non-inverted ECC code from line parity */
+ nand_trans_result(reg2, reg3, ecc_code);
+
+ /* Calculate final ECC code */
+ ecc_code[0] = ~ecc_code[0];
+ ecc_code[1] = ~ecc_code[1];
+ ecc_code[2] = ((~reg1) << 2) | 0x03;
+ return 0;
+}
diff --git a/ubi-utils/src/nandecc.h b/ubi-utils/src/nandecc.h
new file mode 100644
index 0000000..fb5d529
--- /dev/null
+++ b/ubi-utils/src/nandecc.h
@@ -0,0 +1,28 @@
+#ifndef _NAND_ECC_H
+#define _NAND_ECC_H
+/*
+ * 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.
+ *
+ * NAND ecc functions
+ */
+
+#include <stdint.h>
+
+extern int nand_calculate_ecc(const uint8_t *dat, uint8_t *ecc_code);
+extern int nand_correct_data(uint8_t *dat, const uint8_t *fail_ecc);
+
+#endif
diff --git a/ubi-utils/src/pddcustomize.c b/ubi-utils/src/pddcustomize.c
new file mode 100644
index 0000000..a86e942
--- /dev/null
+++ b/ubi-utils/src/pddcustomize.c
@@ -0,0 +1,500 @@
+/*
+ * 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.
+ *
+ * Author: Oliver Lohmann
+ *
+ * PDD (platform description data) contains a set of system specific
+ * boot-parameters. Some of those parameters need to be handled
+ * special on updates, e.g. the MAC addresses. They must also be kept
+ * if the system is updated and one must be able to modify them when
+ * the system has booted the first time. This tool is intended to do
+ * PDD modification.
+ *
+ * 1.3 Removed argp because we want to use uClibc.
+ * 1.4 Minor cleanups
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <errno.h>
+#include <mtd/ubi-header.h>
+
+#include "config.h"
+#include "bootenv.h"
+#include "error.h"
+#include "example_ubi.h"
+#include "libubiold.h"
+#include "ubimirror.h"
+
+#define PROGRAM_VERSION "1.4"
+
+typedef enum action_t {
+ ACT_NORMAL = 0,
+ ACT_LIST,
+ ACT_ARGP_ABORT,
+ ACT_ARGP_ERR,
+} action_t;
+
+#define ABORT_ARGP do { \
+ args->action = ACT_ARGP_ABORT; \
+} while (0)
+
+#define ERR_ARGP do { \
+ args->action = ACT_ARGP_ERR; \
+} while (0)
+
+static char doc[] = "\nVersion: " PROGRAM_VERSION "\n\tBuilt on "
+ BUILD_CPU" "BUILD_OS" at "__DATE__" "__TIME__"\n"
+ "\n"
+ "pddcustomize - customize bootenv and pdd values.\n";
+
+static const char *optionsstr =
+" -b, --both Mirror updated PDD to redundand copy.\n"
+" -c, --copyright Print copyright information.\n"
+" -i, --input=<input> Binary input file. For debug purposes.\n"
+" -l, --list List card bootenv/pdd values.\n"
+" -o, --output=<output> Binary output file. For debug purposes.\n"
+" -s, --side=<seqnum> The side/seqnum to update.\n"
+" -x, --host use x86 platform for debugging.\n"
+" -?, --help Give this help list\n"
+" --usage Give a short usage message\n"
+" -V, --version Print program version\n";
+
+static const char *usage =
+"Usage: pddcustomize [-bclx?V] [-i <input>] [-o <output>] [-s <seqnum>]\n"
+" [--both] [--copyright] [--input=<input>] [--list]\n"
+" [--output=<output>] [--side=<seqnum>] [--host] [--help] [--usage]\n"
+" [--version] [key=value] [...]\n";
+
+struct option long_options[] = {
+ { .name = "both", .has_arg = 0, .flag = NULL, .val = 'b' },
+ { .name = "copyright", .has_arg = 0, .flag = NULL, .val = 'c' },
+ { .name = "input", .has_arg = 1, .flag = NULL, .val = 'i' },
+ { .name = "list", .has_arg = 0, .flag = NULL, .val = 'l' },
+ { .name = "output", .has_arg = 1, .flag = NULL, .val = 'o' },
+ { .name = "side", .has_arg = 1, .flag = NULL, .val = 's' },
+ { .name = "host", .has_arg = 0, .flag = NULL, .val = 'x' },
+ { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' },
+ { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 },
+ { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' },
+ { NULL, 0, NULL, 0}
+};
+
+static const char copyright [] __attribute__((unused)) =
+ "Copyright IBM Corp 2006";
+
+typedef struct myargs {
+ action_t action;
+ const char* file_in;
+ const char* file_out;
+ int both;
+ int side;
+ int x86; /* X86 host, use files for testing */
+ bootenv_t env_in;
+
+ char *arg1;
+ char **options; /* [STRING...] */
+} myargs;
+
+static int
+get_update_side(const char* str)
+{
+ uint32_t i = strtoul(str, NULL, 0);
+
+ if ((i != 0) && (i != 1)) {
+ return -1;
+ }
+
+ return i;
+}
+
+static int
+extract_pair(bootenv_t env, const char* str)
+{
+ int rc = 0;
+ char* key;
+ char* val;
+
+ key = strdup(str);
+ if (key == NULL)
+ return -ENOMEM;
+
+ val = strstr(key, "=");
+ if (val == NULL) {
+ err_msg("Wrong argument: %s\n"
+ "Expecting key=value pair.\n", str);
+ rc = -1;
+ goto err;
+ }
+
+ *val = '\0'; /* split strings */
+ val++;
+ rc = bootenv_set(env, key, val);
+
+err:
+ free(key);
+ return rc;
+}
+
+static int
+parse_opt(int argc, char **argv, myargs *args)
+{
+ int rc = 0;
+
+ while (1) {
+ int key;
+
+ key = getopt_long(argc, argv, "clbxs:i:o:?V",
+ long_options, NULL);
+ if (key == -1)
+ break;
+
+ switch (key) {
+ case 'c':
+ err_msg("%s\n", copyright);
+ ABORT_ARGP;
+ break;
+ case 'l':
+ args->action = ACT_LIST;
+ break;
+ case 'b':
+ args->both = 1;
+ break;
+ case 'x':
+ args->x86 = 1;
+ break;
+ case 's':
+ args->side = get_update_side(optarg);
+ if (args->side < 0) {
+ err_msg("Unsupported seqnum: %d.\n"
+ "Supported seqnums are "
+ "'0' and '1'\n",
+ args->side, optarg);
+ ERR_ARGP;
+ }
+ break;
+ case 'i':
+ args->file_in = optarg;
+ break;
+ case 'o':
+ args->file_out = optarg;
+ break;
+ case '?': /* help */
+ err_msg("Usage: pddcustomize [OPTION...] "
+ "[key=value] [...]");
+ err_msg("%s", doc);
+ err_msg("%s", optionsstr);
+ err_msg("\nReport bugs to %s",
+ PACKAGE_BUGREPORT);
+ exit(0);
+ break;
+ case 'V':
+ err_msg("%s", PROGRAM_VERSION);
+ exit(0);
+ break;
+ default:
+ err_msg("%s", usage);
+ exit(-1);
+ }
+ }
+
+ if (optind < argc) {
+ rc = extract_pair(args->env_in, argv[optind++]);
+ if (rc != 0)
+ ERR_ARGP;
+ }
+
+ return 0;
+}
+
+static int
+list_bootenv(bootenv_t env)
+{
+ int rc = 0;
+ rc = bootenv_write_txt(stdout, env);
+ if (rc != 0) {
+ err_msg("Cannot list bootenv/pdd. rc: %d\n", rc);
+ goto err;
+ }
+err:
+ return rc;
+}
+
+static int
+process_key_value(bootenv_t env_in, bootenv_t env)
+{
+ int rc = 0;
+ size_t size, i;
+ const char* tmp;
+ const char** key_vec = NULL;
+
+ rc = bootenv_get_key_vector(env_in, &size, 0, &key_vec);
+ if (rc != 0)
+ goto err;
+
+ for (i = 0; i < size; i++) {
+ rc = bootenv_get(env_in, key_vec[i], &tmp);
+ if (rc != 0) {
+ err_msg("Cannot read value to input key: %s. rc: %d\n",
+ key_vec[i], rc);
+ goto err;
+ }
+ rc = bootenv_set(env, key_vec[i], tmp);
+ if (rc != 0) {
+ err_msg("Cannot set value key: %s. rc: %d\n",
+ key_vec[i], rc);
+ goto err;
+ }
+ }
+
+err:
+ if (key_vec != NULL)
+ free(key_vec);
+ return rc;
+}
+
+static int
+read_bootenv(const char* file, bootenv_t env)
+{
+ int rc = 0;
+ FILE* fp_in = NULL;
+
+ fp_in = fopen(file, "rb");
+ if (fp_in == NULL) {
+ err_msg("Cannot open file: %s\n", file);
+ return -EIO;
+ }
+
+ rc = bootenv_read(fp_in, env, BOOTENV_MAXSIZE);
+ if (rc != 0) {
+ err_msg("Cannot read bootenv from file %s. rc: %d\n",
+ file, rc);
+ goto err;
+ }
+
+err:
+ fclose(fp_in);
+ return rc;
+}
+
+/*
+ * Read bootenv from ubi volume
+ */
+static int
+ubi_read_bootenv(uint32_t devno, uint32_t id, bootenv_t env)
+{
+ ubi_lib_t ulib = NULL;
+ int rc = 0;
+ FILE* fp_in = NULL;
+
+ rc = ubi_open(&ulib);
+ if( rc ){
+ err_msg("Cannot allocate ubi structure\n");
+ return rc;
+ }
+
+ fp_in = ubi_vol_fopen_read(ulib, devno, id);
+ if (fp_in == NULL) {
+ err_msg("Cannot open volume:%d number:%d\n", devno, id);
+ goto err;
+ }
+
+ rc = bootenv_read(fp_in, env, BOOTENV_MAXSIZE);
+ if (rc != 0) {
+ err_msg("Cannot read volume:%d number:%d\n", devno, id);
+ goto err;
+ }
+
+err:
+ if( fp_in )
+ fclose(fp_in);
+ ubi_close(&ulib);
+ return rc;
+}
+
+static int
+write_bootenv(const char* file, bootenv_t env)
+{
+ int rc = 0;
+ FILE* fp_out;
+
+ fp_out = fopen(file, "wb");
+ if (fp_out == NULL) {
+ err_msg("Cannot open file: %s\n", file);
+ return -EIO;
+ }
+
+ rc = bootenv_write(fp_out, env);
+ if (rc != 0) {
+ err_msg("Cannot write bootenv to file %s. rc: %d\n", file, rc);
+ goto err;
+ }
+
+err:
+ fclose(fp_out);
+ return rc;
+}
+
+/*
+ * Read bootenv from ubi volume
+ */
+static int
+ubi_write_bootenv(uint32_t devno, uint32_t id, bootenv_t env)
+{
+ ubi_lib_t ulib = NULL;
+ int rc = 0;
+ FILE* fp_out;
+ size_t nbytes ;
+
+ rc = bootenv_size(env, &nbytes);
+ if( rc ){
+ err_msg("Cannot determine size of bootenv structure\n");
+ return rc;
+ }
+ rc = ubi_open(&ulib);
+ if( rc ){
+ err_msg("Cannot allocate ubi structure\n");
+ return rc;
+ }
+ fp_out = ubi_vol_fopen_update(ulib, devno, id,
+ (unsigned long long)nbytes);
+ if (fp_out == NULL) {
+ err_msg("Cannot open volume:%d number:%d\n", devno, id);
+ goto err;
+ }
+
+ rc = bootenv_write(fp_out, env);
+ if (rc != 0) {
+ err_msg("Cannot write bootenv to volume %d number:%d\n",
+ devno, id);
+ goto err;
+ }
+
+err:
+ if( fp_out )
+ fclose(fp_out);
+ ubi_close(&ulib);
+ return rc;
+}
+
+static int
+do_mirror(int volno)
+{
+ char errbuf[1024];
+ uint32_t ids[2];
+ int rc;
+ int src_volno_idx = 0;
+
+ ids[0] = EXAMPLE_BOOTENV_VOL_ID_1;
+ ids[1] = EXAMPLE_BOOTENV_VOL_ID_2;
+
+ if (volno == EXAMPLE_BOOTENV_VOL_ID_2)
+ src_volno_idx = 1;
+
+ rc = ubimirror(EXAMPLE_UBI_DEVICE, src_volno_idx, ids, 2, errbuf,
+ sizeof errbuf);
+ if( rc )
+ err_msg(errbuf);
+ return rc;
+}
+
+int
+main(int argc, char **argv) {
+ int rc = 0;
+ bootenv_t env = NULL;
+ uint32_t boot_volno;
+ myargs args = {
+ .action = ACT_NORMAL,
+ .file_in = NULL,
+ .file_out = NULL,
+ .side = -1,
+ .x86 = 0,
+ .both = 0,
+ .env_in = NULL,
+
+ .arg1 = NULL,
+ .options = NULL,
+ };
+
+ rc = bootenv_create(&env);
+ if (rc != 0) {
+ err_msg("Cannot create bootenv handle. rc: %d", rc);
+ goto err;
+ }
+
+ rc = bootenv_create(&(args.env_in));
+ if (rc != 0) {
+ err_msg("Cannot create bootenv handle. rc: %d", rc);
+ goto err;
+ }
+
+ parse_opt(argc, argv, &args);
+ if (args.action == ACT_ARGP_ERR) {
+ rc = -1;
+ goto err;
+ }
+ if (args.action == ACT_ARGP_ABORT) {
+ rc = 0;
+ goto out;
+ }
+
+ if ((args.side == 0) || (args.side == -1))
+ boot_volno = EXAMPLE_BOOTENV_VOL_ID_1;
+ else
+ boot_volno = EXAMPLE_BOOTENV_VOL_ID_2;
+
+ if( args.x86 )
+ rc = read_bootenv(args.file_in, env);
+ else
+ rc = ubi_read_bootenv(EXAMPLE_UBI_DEVICE, boot_volno, env);
+ if (rc != 0) {
+ goto err;
+ }
+
+ if (args.action == ACT_LIST) {
+ rc = list_bootenv(env);
+ if (rc != 0) {
+ goto err;
+ }
+ goto out;
+ }
+
+ rc = process_key_value(args.env_in, env);
+ if (rc != 0) {
+ goto err;
+ }
+
+ if( args.x86 )
+ rc = write_bootenv(args.file_in, env);
+ else
+ rc = ubi_write_bootenv(EXAMPLE_UBI_DEVICE, boot_volno, env);
+ if (rc != 0) {
+ goto err;
+ }
+ if( args.both ) /* No side specified, update both */
+ rc = do_mirror(boot_volno);
+
+ out:
+ err:
+ bootenv_destroy(&env);
+ bootenv_destroy(&(args.env_in));
+ return rc;
+}
diff --git a/ubi-utils/src/peb.c b/ubi-utils/src/peb.c
new file mode 100644
index 0000000..08b770f
--- /dev/null
+++ b/ubi-utils/src/peb.c
@@ -0,0 +1,116 @@
+/*
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "peb.h"
+
+int
+peb_cmp(peb_t eb_1, peb_t eb_2)
+{
+ assert(eb_1);
+ assert(eb_2);
+
+ return eb_1->num == eb_2->num ? 0
+ : eb_1->num > eb_2->num ? 1 : -1;
+}
+
+int
+peb_new(uint32_t eb_num, uint32_t eb_size, peb_t *peb)
+{
+ int rc = 0;
+
+ peb_t res = (peb_t) malloc(sizeof(struct peb));
+ if (!res) {
+ rc = -ENOMEM;
+ goto err;
+ }
+
+ res->num = eb_num;
+ res->size = eb_size;
+ res->data = (uint8_t*) malloc(res->size * sizeof(uint8_t));
+ if (!res->data) {
+ rc = -ENOMEM;
+ goto err;
+ }
+ memset(res->data, 0xff, res->size);
+
+ *peb = res;
+ return 0;
+err:
+ if (res) {
+ if (res->data)
+ free(res->data);
+ free(res);
+ }
+ *peb = NULL;
+ return rc;
+}
+
+int
+peb_fill(peb_t peb, uint8_t* buf, size_t buf_size)
+{
+ if (!peb)
+ return -EINVAL;
+
+ if (buf_size > peb->size)
+ return -EINVAL;
+
+ memcpy(peb->data, buf, buf_size);
+ return 0;
+}
+
+int
+peb_write(FILE* fp_out, peb_t peb)
+{
+ size_t written = 0;
+
+ if (peb == NULL)
+ return -EINVAL;
+
+ written = fwrite(peb->data, 1, peb->size, fp_out);
+
+ if (written != peb->size)
+ return -EIO;
+
+ return 0;
+}
+
+int
+peb_free(peb_t* peb)
+{
+ peb_t tmp = *peb;
+ if (tmp) {
+ if (tmp->data)
+ free(tmp->data);
+ free(tmp);
+ }
+ *peb = NULL;
+
+ return 0;
+}
+
+void peb_dump(FILE* fp_out, peb_t peb)
+{
+ fprintf(fp_out, "num: %08d\tsize: 0x%08x\n", peb->num, peb->size);
+}
diff --git a/ubi-utils/src/peb.h b/ubi-utils/src/peb.h
new file mode 100644
index 0000000..246bce8
--- /dev/null
+++ b/ubi-utils/src/peb.h
@@ -0,0 +1,41 @@
+#ifndef __RAW_BLOCK_H__
+#define __RAW_BLOCK_H__
+/*
+ * 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.
+ *
+ * Author: Oliver Lohmann
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+
+typedef struct peb *peb_t;
+struct peb {
+ uint32_t num; /* Physical eraseblock number
+ * in the RAW file. */
+ uint32_t size; /* Data Size (equals physical
+ * erase block size) */
+ uint8_t* data; /* Data buffer */
+};
+
+int peb_new(uint32_t peb_num, uint32_t peb_size, peb_t* peb);
+int peb_free(peb_t* peb);
+int peb_cmp(peb_t peb_1, peb_t peb_2);
+int peb_write(FILE* fp_out, peb_t peb);
+void peb_dump(FILE* fp_out, peb_t peb);
+
+#endif /* __RAW_BLOCK_H__ */
diff --git a/ubi-utils/src/pfi.c b/ubi-utils/src/pfi.c
new file mode 100644
index 0000000..fa835e2
--- /dev/null
+++ b/ubi-utils/src/pfi.c
@@ -0,0 +1,458 @@
+/*
+ * 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.
+ */
+
+/*
+ * @file pfi.c
+ *
+ * @author Oliver Lohmann
+ * Andreas Arnez
+ * Joern Engel
+ * Frank Haverkamp
+ *
+ * @brief libpfi holds all code to create and process pfi files.
+ *
+ * <oliloh@de.ibm.com> Wed Feb 8 11:38:22 CET 2006: Initial creation.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <syslog.h>
+#include <stdarg.h>
+#include <errno.h>
+
+#include "pfi.h"
+
+#define PFI_MAGIC "PFI!\n"
+#define PFI_DATA "DATA\n" /* The same size as PFI_MAGIC */
+#define PFI_MAGIC_LEN 5
+
+static const char copyright [] __attribute__((unused)) =
+ "Copyright (c) International Business Machines Corp., 2006";
+
+enum key_id {
+ /* version 1 */
+ key_version, /* must be index position 0! */
+ key_mode,
+ key_size,
+ key_crc,
+ key_label,
+ key_flags,
+ key_ubi_ids,
+ key_ubi_size,
+ key_ubi_type,
+ key_ubi_names,
+ key_ubi_alignment,
+ key_raw_starts,
+ key_raw_total_size,
+ num_keys,
+};
+
+struct pfi_header {
+ char defined[num_keys]; /* reserve all possible keys even if
+ version does not require this. */
+ int mode_no; /* current mode no. -> can only increase */
+ union {
+ char *str;
+ uint32_t num;
+ } value[num_keys];
+};
+
+
+#define PFI_MANDATORY 0x0001
+#define PFI_STRING 0x0002
+#define PFI_LISTVALUE 0x0004 /* comma seperated list of nums */
+#define PFI_MANDATORY_UBI 0x0008
+#define PFI_MANDATORY_RAW 0x0010
+
+struct key_descriptor {
+ enum key_id id;
+ const char *name;
+ uint32_t flags;
+};
+
+static const struct key_descriptor key_desc_v1[] = {
+ { key_version, "version", PFI_MANDATORY },
+ { key_mode, "mode", PFI_MANDATORY | PFI_STRING },
+ { key_size, "size", PFI_MANDATORY },
+ { key_crc, "crc", PFI_MANDATORY },
+ { key_label, "label", PFI_MANDATORY | PFI_STRING },
+ { key_flags, "flags", PFI_MANDATORY },
+ { key_ubi_ids, "ubi_ids", PFI_MANDATORY_UBI | PFI_STRING },
+ { key_ubi_size, "ubi_size", PFI_MANDATORY_UBI },
+ { key_ubi_type, "ubi_type", PFI_MANDATORY_UBI | PFI_STRING },
+ { key_ubi_names, "ubi_names", PFI_MANDATORY_UBI | PFI_STRING },
+ { key_ubi_alignment, "ubi_alignment", PFI_MANDATORY_UBI },
+ { key_raw_starts, "raw_starts", PFI_MANDATORY_RAW | PFI_STRING },
+ { key_raw_total_size, "raw_total_size", PFI_MANDATORY_RAW },
+};
+
+static const struct key_descriptor *key_descriptors[] = {
+ NULL,
+ key_desc_v1, /* version 1 */
+};
+
+static const int key_descriptors_max[] = {
+ 0, /* version 0 */
+ sizeof(key_desc_v1)/sizeof(struct key_descriptor), /* version 1 */
+};
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+
+static const char* modes[] = {"raw", "ubi"}; /* order isn't arbitrary! */
+
+/* latest version contains all possible keys */
+static const struct key_descriptor *key_desc = key_desc_v1;
+
+#define PFI_IS_UBI(mode) \
+ (((mode) != NULL) && (strcmp("ubi", (mode)) == 0))
+
+#define PFI_IS_RAW(mode) \
+ (((mode) != NULL) && (strcmp("raw", (mode)) == 0))
+
+/**
+ * @return <0 On Error.
+ * >=0 Mode no.
+ */
+static int
+get_mode_no(const char* mode)
+{
+ int i;
+
+ for (i = 0; i < (int)ARRAY_SIZE(modes); i++)
+ if (strcmp(mode, modes[i]) == 0)
+ return i;
+ return -1;
+}
+
+static int
+find_key_by_name (const char *name)
+{
+ int i;
+
+ for (i = 0; i < num_keys; i++) {
+ if (strcmp(name, key_desc[i].name) == 0)
+ return i;
+ }
+ return -1;
+}
+
+static int
+check_valid (pfi_header head)
+{
+ int i;
+ int max_keys;
+ uint32_t version;
+ const char *mode;
+ const struct key_descriptor *desc;
+ uint32_t to_check = PFI_MANDATORY;
+
+ /*
+ * For the validity check the list of possible keys depends on
+ * the version of the PFI file used.
+ */
+ version = head->value[key_version].num;
+ if (version > PFI_HDRVERSION)
+ return PFI_ENOHEADER;
+
+ max_keys = key_descriptors_max[version];
+ desc = key_descriptors[version];
+
+ if (!desc)
+ return PFI_ENOVERSION;
+
+ mode = head->value[key_mode].str;
+ if (PFI_IS_UBI(mode)) {
+ to_check |= PFI_MANDATORY_UBI;
+ }
+ else if (PFI_IS_RAW(mode)) {
+ to_check |= PFI_MANDATORY_RAW;
+ }
+ else { /* neither UBI nor RAW == ERR */
+ return PFI_EINSUFF;
+ }
+
+ for (i = 0; i < max_keys; i++) {
+ if ((desc[i].flags & to_check) && !head->defined[i]) {
+ fprintf(stderr, "libpfi: %s missing\n", desc[i].name);
+ return PFI_EINSUFF;
+ }
+ }
+
+ return 0;
+}
+
+int pfi_header_init (pfi_header *head)
+{
+ int i;
+ pfi_header self = (pfi_header) malloc(sizeof(*self));
+
+ *head = self;
+ if (self == NULL)
+ return PFI_ENOMEM;
+
+ /* initialize maximum number of possible keys */
+ for (i = 0; i < num_keys; i++) {
+ memset(self, 0, sizeof(*self));
+ self->defined[i] = 0;
+ }
+
+ return 0;
+}
+
+int pfi_header_destroy (pfi_header *head)
+{
+ int i;
+ pfi_header self = *head;
+
+ for (i = 0; i < num_keys; i++) {
+ if (self->defined[i] && (key_desc[i].flags & PFI_STRING) &&
+ self->value[i].str) {
+ free(self->value[i].str);
+ }
+ }
+ free(*head);
+ *head = NULL;
+ return 0;
+}
+
+int pfi_header_setnumber (pfi_header head,
+ const char *key, uint32_t value)
+{
+ int key_id = find_key_by_name(key);
+
+ if (key_id < 0)
+ return PFI_EUNDEF;
+
+ if (key_desc[key_id].flags & PFI_STRING)
+ return PFI_EBADTYPE;
+
+ head->value[key_id].num = value;
+ head->defined[key_id] = 1;
+ return 0;
+}
+
+int pfi_header_setvalue (pfi_header head,
+ const char *key, const char *value)
+{
+ int key_id = find_key_by_name(key);
+
+ if (value == NULL)
+ return PFI_EINSUFF;
+
+ if ((key_id < 0) || (key_id >= num_keys))
+ return PFI_EUNDEF;
+
+ if (key_desc[key_id].flags & PFI_STRING) {
+ /*
+ * The value is a string. Copy to a newly allocated
+ * buffer. Delete the old value, if already set.
+ */
+ size_t len = strlen(value) + 1;
+ char *old_str = NULL;
+ char *str;
+
+ old_str = head->value[key_id].str;
+ if (old_str != NULL)
+ free(old_str);
+
+ str = head->value[key_id].str = (char *) malloc(len);
+ if (str == NULL)
+ return PFI_ENOMEM;
+
+ strcpy(str, value);
+ } else {
+ int len;
+ int ret;
+ /* FIXME: here we assume that the value is always
+ given in hex and starts with '0x'. */
+ ret = sscanf(value, "0x%x%n", &head->value[key_id].num, &len);
+ if (ret < 1 || value[len] != '\0')
+ return PFI_EBADTYPE;
+ }
+ head->defined[key_id] = 1;
+ return 0;
+}
+
+int pfi_header_getnumber (pfi_header head,
+ const char *key, uint32_t *value)
+{
+ int key_id = find_key_by_name(key);
+
+ if (key_id < 0)
+ return PFI_EUNDEF;
+
+ if (key_desc[key_id].flags & PFI_STRING)
+ return PFI_EBADTYPE;
+
+ if (!head->defined[key_id])
+ return PFI_EUNDEF;
+
+ *value = head->value[key_id].num;
+ return 0;
+}
+
+int pfi_header_getstring (pfi_header head,
+ const char *key, char *value, size_t size)
+{
+ int key_id = find_key_by_name(key);
+
+ if (key_id < 0)
+ return PFI_EUNDEF;
+
+ if (!(key_desc[key_id].flags & PFI_STRING))
+ return PFI_EBADTYPE;
+
+ if (!head->defined[key_id])
+ return PFI_EUNDEF;
+
+ strncpy(value, head->value[key_id].str, size-1);
+ value[size-1] = '\0';
+ return 0;
+}
+
+int pfi_header_write (FILE *out, pfi_header head)
+{
+ int i;
+ int ret;
+
+ pfi_header_setnumber(head, "version", PFI_HDRVERSION);
+
+ if ((ret = check_valid(head)) != 0)
+ return ret;
+
+ /* OK. Now write the header. */
+
+ ret = fwrite(PFI_MAGIC, 1, PFI_MAGIC_LEN, out);
+ if (ret < PFI_MAGIC_LEN)
+ return ret;
+
+
+ for (i = 0; i < num_keys; i++) {
+ if (!head->defined[i])
+ continue;
+
+ ret = fprintf(out, "%s=", key_desc[i].name);
+ if (ret < 0)
+ return PFI_EFILE;
+
+ if (key_desc[i].flags & PFI_STRING) {
+ ret = fprintf(out, "%s", head->value[i].str);
+ if (ret < 0)
+ return PFI_EFILE;
+ } else {
+ ret = fprintf(out, "0x%8x", head->value[i].num);
+ if (ret < 0)
+ return PFI_EFILE;
+
+ }
+ ret = fprintf(out, "\n");
+ if (ret < 0)
+ return PFI_EFILE;
+ }
+ ret = fprintf(out, "\n");
+ if (ret < 0)
+ return PFI_EFILE;
+
+ ret = fflush(out);
+ if (ret != 0)
+ return PFI_EFILE;
+
+ return 0;
+}
+
+int pfi_header_read (FILE *in, pfi_header head)
+{
+ char magic[PFI_MAGIC_LEN];
+ char mode[PFI_KEYWORD_LEN];
+ char buf[256];
+
+ if (PFI_MAGIC_LEN != fread(magic, 1, PFI_MAGIC_LEN, in))
+ return PFI_EFILE;
+ if (memcmp(magic, PFI_MAGIC, PFI_MAGIC_LEN) != 0) {
+ if (memcmp(magic, PFI_DATA, PFI_MAGIC_LEN) == 0) {
+ return PFI_DATA_START;
+ }
+ return PFI_ENOHEADER;
+ }
+
+ while (fgets(buf, sizeof(buf), in) != NULL && buf[0] != '\n') {
+ char *value;
+ char *end;
+ value = strchr(buf, '=');
+ if (value == NULL)
+ return PFI_ENOHEADER;
+
+ *value = '\0';
+ value++;
+ end = strchr(value, '\n');
+ if (end)
+ *end = '\0';
+
+ if (pfi_header_setvalue(head, buf, value))
+ return PFI_ENOHEADER;
+ }
+
+ if (check_valid(head) != 0)
+ return PFI_ENOHEADER;
+
+ /* set current mode no. in head */
+ pfi_header_getstring(head, "mode", mode, PFI_KEYWORD_LEN);
+ if (head->mode_no > get_mode_no(mode)) {
+ return PFI_EMODE;
+ }
+ head->mode_no = get_mode_no(mode);
+ return 0;
+}
+
+int pfi_header_dump (FILE *out, pfi_header head __attribute__((__unused__)))
+{
+ fprintf(out, "Sorry not implemented yet. Write mail to "
+ "Andreas Arnez and complain!\n");
+ return 0;
+}
+
+int pfi_read (FILE *in, pfi_read_func func, void *priv_data)
+{
+ int rc;
+ pfi_header header;
+
+ rc = pfi_header_init (&header);
+ if (0 != rc)
+ return rc;
+ if (!func)
+ return PFI_EINVAL;
+
+ while ((0 == rc) && !feof(in)) {
+ /*
+ * Read header and check consistency of the fields.
+ */
+ rc = pfi_header_read( in, header );
+ if (0 != rc)
+ break;
+ if (func) {
+ rc = func(in, header, priv_data);
+ if (rc != 0)
+ break;
+ }
+ }
+
+ pfi_header_destroy(&header);
+ return rc;
+}
diff --git a/ubi-utils/src/pfi.h b/ubi-utils/src/pfi.h
new file mode 100644
index 0000000..8c5cc07
--- /dev/null
+++ b/ubi-utils/src/pfi.h
@@ -0,0 +1,244 @@
+#ifndef __pfi_h
+#define __pfi_h
+/*
+ * 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.
+ */
+
+/**
+ * @file pfi.h
+ *
+ * @author Oliver Lohmann <oliloh@de.ibm.com>
+ * Andreas Arnez <arnez@de.ibm.com>
+ * Joern Engel <engeljoe@de.ibm.com>
+ * Frank Haverkamp <haverkam@de.ibm.com>
+ *
+ * @brief libpfi will hold all code to create and process pfi
+ * images. Definitions made in this file are equaly usable for the
+ * development host and the target system.
+ *
+ * @note This header additionally holds the official definitions for
+ * the pfi headers.
+ */
+
+#include <stdio.h> /* FILE */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Definitions. */
+
+#define PFI_HDRVERSION 1 /* current header version */
+
+#define PFI_ENOVERSION 1 /* unknown version */
+#define PFI_ENOHEADER 2 /* not a pfi header */
+#define PFI_EINSUFF 3 /* insufficient information */
+#define PFI_EUNDEF 4 /* key not defined */
+#define PFI_ENOMEM 5 /* out of memory */
+#define PFI_EBADTYPE 6 /* bad data type */
+#define PFI_EFILE 7 /* file I/O error: see errno */
+#define PFI_EFILEINVAL 8 /* file format not valid */
+#define PFI_EINVAL 9 /* invalid parameter */
+#define PFI_ERANGE 10 /* invalid range */
+#define PFI_EMODE 11 /* expecting other mode in this header */
+#define PFI_DATA_START 12 /* data section starts */
+#define PFI_EMAX 13 /* should be always larger as the largest
+ error code */
+
+#define PFI_LABEL_LEN 64 /* This is the maximum length for a
+ PFI header label */
+#define PFI_KEYWORD_LEN 32 /* This is the maximum length for an
+ entry in the mode and type fields */
+
+#define PFI_UBI_MAX_VOLUMES 128
+#define PFI_UBI_VOL_NAME_LEN 127
+
+/**
+ * @brief The pfi header allows to set flags which influence the flashing
+ * behaviour.
+ */
+#define PFI_FLAG_PROTECTED 0x00000001
+
+
+/**
+ * @brief Handle to pfi header. Used in most of the functions associated
+ * with pfi file handling.
+ */
+typedef struct pfi_header *pfi_header;
+
+
+/**
+ * @brief Initialize a pfi header object.
+ *
+ * @param head Pointer to handle. This function allocates memory
+ * for this data structure.
+ * @return 0 on success, otherwise:
+ * PFI_ENOMEM : no memory available for the handle.
+ */
+int pfi_header_init (pfi_header *head);
+
+
+/**
+ * @brief Destroy a pfi header object.
+ *
+ * @param head handle. head is invalid after calling this function.
+ * @return 0 always.
+ */
+int pfi_header_destroy (pfi_header *head);
+
+
+/**
+ * @brief Add a key/value pair to a pfi header object.
+ *
+ * @param head handle.
+ * @param key pointer to key string. Must be 0 terminated.
+ * @param value pointer to value string. Must be 0 terminated.
+ * @return 0 on success, otherwise:
+ * PFI_EUNDEF : key was not found.
+ * PFI_ENOMEM : no memory available for the handle.
+ * PFI_EBADTYPE : value is not an hex string. This happens
+ * when the key stores an integer and the
+ * new value is not convertable e.g. not in
+ * 0xXXXXXXXX format.
+ */
+int pfi_header_setvalue (pfi_header head,
+ const char *key, const char *value);
+
+
+/**
+ * @brief Add a key/value pair to a pfi header object. Provide the
+ * value as a number.
+ *
+ * @param head handle.
+ * @param key pointer to key string. Must be 0 terminated.
+ * @param value value to set.
+ * @return 0 on success, otherwise:
+ * PFI_EUNDEF : key was not found.
+ * PFI_EBADTYPE : value is not a string. This happens
+ * when the key stores a string.
+ */
+int pfi_header_setnumber (pfi_header head,
+ const char *key, uint32_t value);
+
+
+/**
+ * @brief For a given key, return the numerical value stored in a
+ * pfi header object.
+ *
+ * @param head handle.
+ * @param key pointer to key string. Must be 0 terminated.
+ * @param value pointer to value.
+ * @return 0 on success, otherwise:
+ * PFI_EUNDEF : key was not found.
+ * PFI_EBADTYPE : stored value is not an integer but a string.
+ */
+int pfi_header_getnumber (pfi_header head,
+ const char *key, uint32_t *value);
+
+
+static inline uint32_t
+pfi_getnumber(pfi_header head, const char *key)
+{
+ uint32_t value;
+ pfi_header_getnumber(head, key, &value);
+ return value;
+}
+
+/**
+ * @brief For a given key, return the string value stored in a pfi
+ * header object.
+ *
+ * @param head handle.
+ * @param key pointer to key string. Must be 0 terminated.
+ * @param value pointer to value string. Memory must be allocated by the user.
+ * @return 0 on success, otherwise:
+ * PFI_EUNDEF : key was not found.
+ * PFI_EBADTYPE : stored value is not a string but an integer.
+ */
+int pfi_header_getstring (pfi_header head,
+ const char *key, char *value, size_t size);
+
+
+/**
+ * @brief Write a pfi header object into a given file.
+ *
+ * @param out output stream.
+ * @param head handle.
+ * @return 0 on success, error values otherwise:
+ * PFI_EINSUFF : not all mandatory fields are filled.
+ * PFI_ENOHEADER : wrong header version or magic number.
+ * -E* : see <asm/errno.h>.
+ */
+int pfi_header_write (FILE *out, pfi_header head);
+
+
+/**
+ * @brief Read a pfi header object from a given file.
+ *
+ * @param in input stream.
+ * @param head handle.
+ * @return 0 on success, error values otherwise:
+ * PFI_ENOVERSION: unknown header version.
+ * PFI_EFILE : cannot read enough data.
+ * PFI_ENOHEADER : wrong header version or magic number.
+ * -E* : see <asm/errno.h>.
+ *
+ * If the header verification returned success the user can assume that
+ * all mandatory fields for a particular version are accessible. Checking
+ * the return code when calling the get-function for those keys is not
+ * required in those cases. For optional fields the checking must still be
+ * done.
+ */
+int pfi_header_read (FILE *in, pfi_header head);
+
+
+/**
+ * @brief Display a pfi header in human-readable form.
+ *
+ * @param out output stream.
+ * @param head handle.
+ * @return always 0.
+ *
+ * @note Prints out that it is not implemented and whom you should
+ * contact if you need it urgently!.
+ */
+int pfi_header_dump (FILE *out, pfi_header head);
+
+
+/*
+ * @brief Iterates over a stream of pfi files. The iterator function
+ * must advance the file pointer in FILE *in to the next pfi
+ * header. Function exists on feof(in).
+ *
+ * @param in input file descriptor, must be open and valid.
+ * @param func iterator function called when pfi header could be
+ * read and was validated. The function must return 0 on
+ * success.
+ * @return See pfi_header_init and pfi_header_read.
+ * PFI_EINVAL : func is not valid
+ * 0 ok.
+ */
+typedef int (* pfi_read_func)(FILE *in, pfi_header hdr, void *priv_data);
+
+int pfi_read (FILE *in, pfi_read_func func, void *priv_data);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __pfi_h */
diff --git a/ubi-utils/src/pfi2bin.c b/ubi-utils/src/pfi2bin.c
new file mode 100644
index 0000000..57c4ea5
--- /dev/null
+++ b/ubi-utils/src/pfi2bin.c
@@ -0,0 +1,682 @@
+/*
+ * 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.
+ *
+ * Author: Oliver Lohmann
+ *
+ * Convert a PFI file (partial flash image) into a plain binary file.
+ * This tool can be used to prepare the data to be burned into flash
+ * chips in a manufacturing step where the flashes are written before
+ * being soldered onto the hardware. For NAND images another step is
+ * required to add the right OOB data to the binary image.
+ *
+ * 1.3 Removed argp because we want to use uClibc.
+ * 1.4 Minor cleanups
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+
+#include <ubigen.h>
+#include <mtd/ubi-header.h>
+
+#include "config.h"
+#include "list.h"
+#include "error.h"
+#include "reader.h"
+#include "peb.h"
+#include "crc32.h"
+
+#define PROGRAM_VERSION "1.4"
+
+#define MAX_FNAME 255
+#define DEFAULT_ERASE_COUNT 0 /* Hmmm.... Perhaps */
+#define ERR_BUF_SIZE 1024
+
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+
+static uint32_t crc32_table[256];
+static char err_buf[ERR_BUF_SIZE];
+
+/*
+ * Data used to buffer raw blocks which have to be
+ * located at a specific point inside the generated RAW file
+ */
+
+typedef enum action_t {
+ ACT_NOTHING = 0x00000000,
+ ACT_RAW = 0x00000001,
+} action_t;
+
+static const char copyright [] __attribute__((unused)) =
+ "(c) Copyright IBM Corp 2006\n";
+
+static char doc[] = "\nVersion: " PROGRAM_VERSION "\n\tBuilt on "
+ BUILD_CPU" "BUILD_OS" at "__DATE__" "__TIME__"\n"
+ "\n"
+ "pfi2bin - a tool to convert PFI files into binary images.\n";
+
+static const char *optionsstr =
+" Common settings:\n"
+" -c, --copyright\n"
+" -v, --verbose Print more information.\n"
+"\n"
+" Input:\n"
+" -j, --platform=pdd-file PDD information which contains the card settings.\n"
+"\n"
+" Output:\n"
+" -o, --output=filename Outputfile, default: stdout.\n"
+"\n"
+" -?, --help Give this help list\n"
+" --usage Give a short usage message\n"
+" -V, --version Print program version\n";
+
+static const char *usage =
+"Usage: pfi2bin [-cv?V] [-j pdd-file] [-o filename] [--copyright]\n"
+" [--verbose] [--platform=pdd-file] [--output=filename] [--help]\n"
+" [--usage] [--version] pfifile\n";
+
+struct option long_options[] = {
+ { .name = "copyright", .has_arg = 0, .flag = NULL, .val = 'c' },
+ { .name = "verbose", .has_arg = 0, .flag = NULL, .val = 'v' },
+ { .name = "platform", .has_arg = 1, .flag = NULL, .val = 'j' },
+ { .name = "output", .has_arg = 1, .flag = NULL, .val = 'o' },
+ { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' },
+ { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 },
+ { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' },
+ { NULL, 0, NULL, 0}
+};
+
+typedef struct io {
+ FILE* fp_pdd; /* a FilePointer to the PDD data */
+ FILE* fp_pfi; /* a FilePointer to the PFI input stream */
+ FILE* fp_out; /* a FilePointer to the output stream */
+} *io_t;
+
+typedef struct myargs {
+ /* common settings */
+ action_t action;
+ int verbose;
+ const char *f_in_pfi;
+ const char *f_in_pdd;
+ const char *f_out;
+
+ /* special stuff needed to get additional arguments */
+ char *arg1;
+ char **options; /* [STRING...] */
+} myargs;
+
+static int
+parse_opt(int argc, char **argv, myargs *args)
+{
+ while (1) {
+ int key;
+
+ key = getopt_long(argc, argv, "cvj:o:?V", long_options, NULL);
+ if (key == -1)
+ break;
+
+ switch (key) {
+ /* common settings */
+ case 'v': /* --verbose=<level> */
+ args->verbose = 1;
+ break;
+
+ case 'c': /* --copyright */
+ fprintf(stderr, "%s\n", copyright);
+ exit(0);
+ break;
+
+ case 'j': /* --platform */
+ args->f_in_pdd = optarg;
+ break;
+
+ case 'o': /* --output */
+ args->f_out = optarg;
+ break;
+
+ case '?': /* help */
+ printf("pfi2bin [OPTION...] pfifile\n");
+ printf("%s", doc);
+ printf("%s", optionsstr);
+ printf("\nReport bugs to %s\n",
+ PACKAGE_BUGREPORT);
+ exit(0);
+ break;
+
+ case 'V':
+ printf("%s\n", PROGRAM_VERSION);
+ exit(0);
+ break;
+
+ default:
+ printf("%s", usage);
+ exit(-1);
+ }
+ }
+
+ if (optind < argc)
+ args->f_in_pfi = argv[optind++];
+
+ return 0;
+}
+
+
+static size_t
+byte_to_blk(size_t byte, size_t blk_size)
+{
+ return (byte % blk_size) == 0
+ ? byte / blk_size
+ : byte / blk_size + 1;
+}
+
+
+
+
+/**
+ * @precondition IO: File stream points to first byte of RAW data.
+ * @postcondition IO: File stream points to first byte of next
+ * or EOF.
+ */
+static int
+memorize_raw_eb(pfi_raw_t pfi_raw, pdd_data_t pdd, list_t *raw_pebs,
+ io_t io)
+{
+ int rc = 0;
+ uint32_t i;
+
+ size_t read, to_read, eb_num;
+ size_t bytes_left;
+ list_t pebs = *raw_pebs;
+ peb_t peb = NULL;
+
+ long old_file_pos = ftell(io->fp_pfi);
+ for (i = 0; i < pfi_raw->starts_size; i++) {
+ bytes_left = pfi_raw->data_size;
+ rc = fseek(io->fp_pfi, old_file_pos, SEEK_SET);
+ if (rc != 0)
+ goto err;
+
+ eb_num = byte_to_blk(pfi_raw->starts[i], pdd->eb_size);
+ while (bytes_left) {
+ to_read = MIN(bytes_left, pdd->eb_size);
+ rc = peb_new(eb_num++, pdd->eb_size, &peb);
+ if (rc != 0)
+ goto err;
+ read = fread(peb->data, 1, to_read, io->fp_pfi);
+ if (read != to_read) {
+ rc = -EIO;
+ goto err;
+ }
+ pebs = append_elem(peb, pebs);
+ bytes_left -= read;
+ }
+
+ }
+ *raw_pebs = pebs;
+ return 0;
+err:
+ pebs = remove_all((free_func_t)&peb_free, pebs);
+ return rc;
+}
+
+static int
+convert_ubi_volume(pfi_ubi_t ubi, pdd_data_t pdd, list_t raw_pebs,
+ struct ubi_vol_tbl_record *vol_tab,
+ size_t *ebs_written, io_t io)
+{
+ int rc = 0;
+ uint32_t i, j;
+ peb_t raw_peb;
+ peb_t cmp_peb;
+ ubi_info_t u;
+ size_t leb_total = 0;
+ uint8_t vol_type;
+
+ switch (ubi->type) {
+ case pfi_ubi_static:
+ vol_type = UBI_VID_STATIC; break;
+ case pfi_ubi_dynamic:
+ vol_type = UBI_VID_DYNAMIC; break;
+ default:
+ vol_type = UBI_VID_DYNAMIC;
+ }
+
+ rc = peb_new(0, 0, &cmp_peb);
+ if (rc != 0)
+ goto err;
+
+ long old_file_pos = ftell(io->fp_pfi);
+ for (i = 0; i < ubi->ids_size; i++) {
+ rc = fseek(io->fp_pfi, old_file_pos, SEEK_SET);
+ if (rc != 0)
+ goto err;
+ rc = ubigen_create(&u, ubi->ids[i], vol_type,
+ pdd->eb_size, DEFAULT_ERASE_COUNT,
+ ubi->alignment, UBI_VERSION,
+ pdd->vid_hdr_offset, 0, ubi->data_size,
+ io->fp_pfi, io->fp_out);
+ if (rc != 0)
+ goto err;
+
+ rc = ubigen_get_leb_total(u, &leb_total);
+ if (rc != 0)
+ goto err;
+
+ j = 0;
+ while(j < leb_total) {
+ cmp_peb->num = *ebs_written;
+ raw_peb = is_in((cmp_func_t)peb_cmp, cmp_peb,
+ raw_pebs);
+ if (raw_peb) {
+ rc = peb_write(io->fp_out, raw_peb);
+ }
+ else {
+ rc = ubigen_write_leb(u, NO_ERROR);
+ j++;
+ }
+ if (rc != 0)
+ goto err;
+ (*ebs_written)++;
+ }
+ /* memorize volume table entry */
+ rc = ubigen_set_lvol_rec(u, ubi->size,
+ ubi->names[i],
+ (void*) &vol_tab[ubi->ids[i]]);
+ if (rc != 0)
+ goto err;
+ ubigen_destroy(&u);
+ }
+
+ peb_free(&cmp_peb);
+ return 0;
+
+err:
+ peb_free(&cmp_peb);
+ ubigen_destroy(&u);
+ return rc;
+}
+
+
+static FILE*
+my_fmemopen (void *buf, size_t size, const char *opentype)
+{
+ FILE* f;
+
+ assert(strcmp(opentype, "r") == 0);
+
+ f = tmpfile();
+ fwrite(buf, 1, size, f);
+ rewind(f);
+
+ return f;
+}
+
+/**
+ * @brief Builds a UBI volume table from a volume entry list.
+ * @return 0 On success.
+ * else Error.
+ */
+static int
+write_ubi_volume_table(pdd_data_t pdd, list_t raw_pebs,
+ struct ubi_vol_tbl_record *vol_tab, size_t vol_tab_size,
+ size_t *ebs_written, io_t io)
+{
+ int rc = 0;
+ ubi_info_t u;
+ peb_t raw_peb;
+ peb_t cmp_peb;
+ size_t leb_size, leb_total, j = 0;
+ uint8_t *ptr = NULL;
+ FILE* fp_leb = NULL;
+ int vt_slots;
+ size_t vol_tab_size_limit;
+
+ rc = peb_new(0, 0, &cmp_peb);
+ if (rc != 0)
+ goto err;
+
+ /* @FIXME: Artem creates one volume with 2 LEBs.
+ * IMO 2 volumes would be more convenient. In order
+ * to get 2 reserved LEBs from ubigen, I have to
+ * introduce this stupid mechanism. Until no final
+ * decision of the VTAB structure is made... Good enough.
+ */
+ rc = ubigen_create(&u, UBI_LAYOUT_VOL_ID, UBI_VID_DYNAMIC,
+ pdd->eb_size, DEFAULT_ERASE_COUNT,
+ 1, UBI_VERSION,
+ pdd->vid_hdr_offset, UBI_COMPAT_REJECT,
+ vol_tab_size, stdin, io->fp_out);
+ /* @FIXME stdin for fp_in is a hack */
+ if (rc != 0)
+ goto err;
+ rc = ubigen_get_leb_size(u, &leb_size);
+ if (rc != 0)
+ goto err;
+ ubigen_destroy(&u);
+
+ /*
+ * The number of supported volumes is restricted by the eraseblock size
+ * and by the UBI_MAX_VOLUMES constant.
+ */
+ vt_slots = leb_size / UBI_VTBL_RECORD_SIZE;
+ if (vt_slots > UBI_MAX_VOLUMES)
+ vt_slots = UBI_MAX_VOLUMES;
+ vol_tab_size_limit = vt_slots * UBI_VTBL_RECORD_SIZE;
+
+ ptr = (uint8_t*) malloc(leb_size * sizeof(uint8_t));
+ if (ptr == NULL)
+ goto err;
+
+ memset(ptr, 0xff, leb_size);
+ memcpy(ptr, vol_tab, vol_tab_size_limit);
+ fp_leb = my_fmemopen(ptr, leb_size, "r");
+
+ rc = ubigen_create(&u, UBI_LAYOUT_VOL_ID, UBI_VID_DYNAMIC,
+ pdd->eb_size, DEFAULT_ERASE_COUNT,
+ 1, UBI_VERSION, pdd->vid_hdr_offset,
+ UBI_COMPAT_REJECT, leb_size * UBI_LAYOUT_VOLUME_EBS,
+ fp_leb, io->fp_out);
+ if (rc != 0)
+ goto err;
+ rc = ubigen_get_leb_total(u, &leb_total);
+ if (rc != 0)
+ goto err;
+
+ long old_file_pos = ftell(fp_leb);
+ while(j < leb_total) {
+ rc = fseek(fp_leb, old_file_pos, SEEK_SET);
+ if (rc != 0)
+ goto err;
+
+ cmp_peb->num = *ebs_written;
+ raw_peb = is_in((cmp_func_t)peb_cmp, cmp_peb,
+ raw_pebs);
+ if (raw_peb) {
+ rc = peb_write(io->fp_out, raw_peb);
+ }
+ else {
+ rc = ubigen_write_leb(u, NO_ERROR);
+ j++;
+ }
+
+ if (rc != 0)
+ goto err;
+ (*ebs_written)++;
+ }
+
+err:
+ free(ptr);
+ peb_free(&cmp_peb);
+ ubigen_destroy(&u);
+ fclose(fp_leb);
+ return rc;
+}
+
+static int
+write_remaining_raw_ebs(pdd_data_t pdd, list_t raw_blocks, size_t *ebs_written,
+ FILE* fp_out)
+{
+ int rc = 0;
+ uint32_t j, delta;
+ list_t ptr;
+ peb_t empty_eb, peb;
+
+ /* create an empty 0xff EB (for padding) */
+ rc = peb_new(0, pdd->eb_size, &empty_eb);
+
+ foreach(peb, ptr, raw_blocks) {
+ if (peb->num < *ebs_written) {
+ continue; /* omit blocks which
+ are already passed */
+ }
+
+ if (peb->num < *ebs_written) {
+ err_msg("eb_num: %d\n", peb->num);
+ err_msg("Bug: This should never happen. %d %s",
+ __LINE__, __FILE__);
+ goto err;
+ }
+
+ delta = peb->num - *ebs_written;
+ if (((delta + *ebs_written) * pdd->eb_size) > pdd->flash_size) {
+ err_msg("RAW block outside of flash_size.");
+ goto err;
+ }
+ for (j = 0; j < delta; j++) {
+ rc = peb_write(fp_out, empty_eb);
+ if (rc != 0)
+ goto err;
+ (*ebs_written)++;
+ }
+ rc = peb_write(fp_out, peb);
+ if (rc != 0)
+ goto err;
+ (*ebs_written)++;
+ }
+
+err:
+ peb_free(&empty_eb);
+ return rc;
+}
+
+static int
+init_vol_tab(struct ubi_vol_tbl_record **vol_tab, size_t *vol_tab_size)
+{
+ uint32_t crc;
+ size_t i;
+ struct ubi_vol_tbl_record* res = NULL;
+
+ *vol_tab_size = UBI_MAX_VOLUMES * UBI_VTBL_RECORD_SIZE;
+
+ res = (struct ubi_vol_tbl_record*) calloc(1, *vol_tab_size);
+ if (vol_tab == NULL) {
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < UBI_MAX_VOLUMES; i++) {
+ crc = clc_crc32(crc32_table, UBI_CRC32_INIT,
+ &(res[i]), UBI_VTBL_RECORD_SIZE_CRC);
+ res[i].crc = cpu_to_ubi32(crc);
+ }
+
+ *vol_tab = res;
+ return 0;
+}
+
+static int
+create_raw(io_t io)
+{
+ int rc = 0;
+ size_t ebs_written = 0; /* eraseblocks written already... */
+ size_t vol_tab_size;
+ list_t ptr;
+
+ list_t pfi_raws = mk_empty(); /* list of raw sections from a pfi */
+ list_t pfi_ubis = mk_empty(); /* list of ubi sections from a pfi */
+ list_t raw_pebs = mk_empty(); /* list of raw eraseblocks */
+
+ struct ubi_vol_tbl_record *vol_tab = NULL;
+ pdd_data_t pdd = NULL;
+
+ rc = init_vol_tab (&vol_tab, &vol_tab_size);
+ if (rc != 0) {
+ err_msg("Cannot initialize volume table.");
+ goto err;
+ }
+
+ rc = read_pdd_data(io->fp_pdd, &pdd,
+ err_buf, ERR_BUF_SIZE);
+ if (rc != 0) {
+ err_msg("Cannot read necessary pdd_data: %s rc: %d",
+ err_buf, rc);
+ goto err;
+ }
+
+ rc = read_pfi_headers(&pfi_raws, &pfi_ubis, io->fp_pfi,
+ err_buf, ERR_BUF_SIZE);
+ if (rc != 0) {
+ err_msg("Cannot read pfi header: %s rc: %d",
+ err_buf, rc);
+ goto err;
+ }
+
+ pfi_raw_t pfi_raw;
+ foreach(pfi_raw, ptr, pfi_raws) {
+ rc = memorize_raw_eb(pfi_raw, pdd, &raw_pebs,
+ io);
+ if (rc != 0) {
+ err_msg("Cannot create raw_block in mem. rc: %d\n",
+ rc);
+ goto err;
+ }
+ }
+
+ pfi_ubi_t pfi_ubi;
+ foreach(pfi_ubi, ptr, pfi_ubis) {
+ rc = convert_ubi_volume(pfi_ubi, pdd, raw_pebs,
+ vol_tab, &ebs_written, io);
+ if (rc != 0) {
+ err_msg("Cannot convert UBI volume. rc: %d\n", rc);
+ goto err;
+ }
+ }
+
+ rc = write_ubi_volume_table(pdd, raw_pebs, vol_tab, vol_tab_size,
+ &ebs_written, io);
+ if (rc != 0) {
+ err_msg("Cannot write UBI volume table. rc: %d\n", rc);
+ goto err;
+ }
+
+ rc = write_remaining_raw_ebs(pdd, raw_pebs, &ebs_written, io->fp_out);
+ if (rc != 0)
+ goto err;
+
+ if (io->fp_out != stdout)
+ info_msg("Physical eraseblocks written: %8d\n", ebs_written);
+err:
+ free(vol_tab);
+ pfi_raws = remove_all((free_func_t)&free_pfi_raw, pfi_raws);
+ pfi_ubis = remove_all((free_func_t)&free_pfi_ubi, pfi_ubis);
+ raw_pebs = remove_all((free_func_t)&peb_free, raw_pebs);
+ free_pdd_data(&pdd);
+ return rc;
+}
+
+
+/* ------------------------------------------------------------------------- */
+static void
+open_io_handle(myargs *args, io_t io)
+{
+ /* set PDD input */
+ io->fp_pdd = fopen(args->f_in_pdd, "r");
+ if (io->fp_pdd == NULL) {
+ err_sys("Cannot open: %s", args->f_in_pdd);
+ }
+
+ /* set PFI input */
+ io->fp_pfi = fopen(args->f_in_pfi, "r");
+ if (io->fp_pfi == NULL) {
+ err_sys("Cannot open PFI input file: %s", args->f_in_pfi);
+ }
+
+ /* set output prefix */
+ if (strcmp(args->f_out,"") == 0)
+ io->fp_out = stdout;
+ else {
+ io->fp_out = fopen(args->f_out, "wb");
+ if (io->fp_out == NULL) {
+ err_sys("Cannot open output file: %s", args->f_out);
+ }
+ }
+}
+
+static void
+close_io_handle(io_t io)
+{
+ if (fclose(io->fp_pdd) != 0) {
+ err_sys("Cannot close PDD file.");
+ }
+ if (fclose(io->fp_pfi) != 0) {
+ err_sys("Cannot close PFI file.");
+ }
+ if (io->fp_out != stdout) {
+ if (fclose(io->fp_out) != 0) {
+ err_sys("Cannot close output file.");
+ }
+ }
+
+ io->fp_pdd = NULL;
+ io->fp_pfi = NULL;
+ io->fp_out = NULL;
+}
+
+int
+main(int argc, char *argv[])
+{
+ int rc = 0;
+
+ ubigen_init();
+ init_crc32_table(crc32_table);
+
+ struct io io = {NULL, NULL, NULL};
+ myargs args = {
+ .action = ACT_RAW,
+ .verbose = 0,
+
+ .f_in_pfi = "",
+ .f_in_pdd = "",
+ .f_out = "",
+
+ /* arguments */
+ .arg1 = NULL,
+ .options = NULL,
+ };
+
+ /* parse arguments */
+ parse_opt(argc, argv, &args);
+
+ if (strcmp(args.f_in_pfi, "") == 0) {
+ err_quit("No PFI input file specified!");
+ }
+
+ if (strcmp(args.f_in_pdd, "") == 0) {
+ err_quit("No PDD input file specified!");
+ }
+
+ open_io_handle(&args, &io);
+
+ info_msg("[ Creating RAW...");
+ rc = create_raw(&io);
+ if (rc != 0) {
+ err_msg("Creating RAW failed.");
+ goto err;
+ }
+
+err:
+ close_io_handle(&io);
+ if (rc != 0) {
+ remove(args.f_out);
+ }
+
+ return rc;
+}
diff --git a/ubi-utils/src/pfiflash.c b/ubi-utils/src/pfiflash.c
new file mode 100644
index 0000000..d5b5406
--- /dev/null
+++ b/ubi-utils/src/pfiflash.c
@@ -0,0 +1,263 @@
+/*
+ * 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.
+ *
+ * Author: Oliver Lohmann
+ * Frank Haverkamp
+ *
+ * Process a PFI (partial flash image) and write the data to the
+ * specified UBI volumes. This tool is intended to be used for system
+ * update using PFI files.
+ *
+ * 1.1 fixed output to stderr and stdout in logfile mode.
+ * 1.2 updated.
+ * 1.3 removed argp parsing to be able to use uClib.
+ * 1.4 Minor cleanups.
+ * 1.5 Forgot to delete raw block before updating it.
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <pfiflash.h>
+#undef DEBUG
+#include "error.h"
+#include "config.h"
+
+#define PROGRAM_VERSION "1.5"
+
+static char doc[] = "\nVersion: " PROGRAM_VERSION "\n\tBuilt on "
+ BUILD_CPU" "BUILD_OS" at "__DATE__" "__TIME__"\n"
+ "\n"
+ "pfiflash - a tool for updating a controller with PFI files.\n";
+
+static const char *optionsstr =
+" Standard options:\n"
+" -c, --copyright Print copyright information.\n"
+" -l, --logfile=<file> Write a logfile to <file>.\n"
+" -v, --verbose Be verbose during program execution.\n"
+"\n"
+" Process options:\n"
+" -C, --complete Execute a complete system update. Updates both\n"
+" sides.\n"
+" -p, --pdd-update=<type> Specify the pdd-update algorithm. <type> is either\n"
+" 'keep', 'merge' or 'overwrite'.\n"
+" -r, --raw-flash=<dev> Flash the raw data. Use the specified mtd device.\n"
+" -s, --side=<seqnum> Select the side which shall be updated.\n"
+"\n"
+" -?, --help Give this help list\n"
+" --usage Give a short usage message\n"
+" -V, --version Print program version\n";
+
+static const char *usage =
+"Usage: pfiflash [-cvC?V] [-l <file>] [-p <type>] [-r <dev>] [-s <seqnum>]\n"
+" [--copyright] [--logfile=<file>] [--verbose] [--complete]\n"
+" [--pdd-update=<type>] [--raw-flash=<dev>] [--side=<seqnum>]\n"
+" [--help] [--usage] [--version] [pfifile]\n";
+
+static const char copyright [] __attribute__((unused)) =
+ "Copyright IBM Corp 2006";
+
+struct option long_options[] = {
+ { .name = "copyright", .has_arg = 0, .flag = NULL, .val = 'c' },
+ { .name = "logfile", .has_arg = 1, .flag = NULL, .val = 'l' },
+ { .name = "verbose", .has_arg = 0, .flag = NULL, .val = 'v' },
+ { .name = "complete", .has_arg = 0, .flag = NULL, .val = 'C' },
+ { .name = "pdd-update", .has_arg = 1, .flag = NULL, .val = 'p' },
+ { .name = "raw-flash", .has_arg = 1, .flag = NULL, .val = 'r' },
+ { .name = "side", .has_arg = 1, .flag = NULL, .val = 's' },
+ { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' },
+ { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 },
+ { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' },
+ { NULL, 0, NULL, 0}
+};
+
+typedef struct myargs {
+ int verbose;
+ const char *logfile;
+ const char *raw_dev;
+
+ pdd_handling_t pdd_handling;
+ int seqnum;
+ int complete;
+
+ FILE* fp_in;
+
+ /* special stuff needed to get additional arguments */
+ char *arg1;
+ char **options; /* [STRING...] */
+} myargs;
+
+static pdd_handling_t
+get_pdd_handling(const char* str)
+{
+ if (strcmp(str, "keep") == 0) {
+ return PDD_KEEP;
+ }
+ if (strcmp(str, "merge") == 0) {
+ return PDD_MERGE;
+ }
+ if (strcmp(str, "overwrite") == 0) {
+ return PDD_OVERWRITE;
+ }
+
+ return -1;
+}
+
+static int
+get_update_seqnum(const char* str)
+{
+ uint32_t i = strtoul(str, NULL, 0);
+
+ if ((i != 0) && (i != 1)) {
+ return -1;
+ }
+
+ return i;
+}
+
+
+static int
+parse_opt(int argc, char **argv, myargs *args)
+{
+ while (1) {
+ int key;
+
+ key = getopt_long(argc, argv, "cl:vCp:r:s:?V",
+ long_options, NULL);
+ if (key == -1)
+ break;
+
+ switch (key) {
+ /* standard options */
+ case 'c':
+ err_msg("%s\n", copyright);
+ exit(0);
+ break;
+ case 'v':
+ args->verbose = 1;
+ break;
+ case 'l':
+ args->logfile = optarg;
+ break;
+ /* process options */
+ case 'C':
+ args->complete = 1;
+ break;
+ case 'p':
+ args->pdd_handling = get_pdd_handling(optarg);
+ if ((int)args->pdd_handling < 0) {
+ err_quit("Unknown PDD handling: %s.\n"
+ "Please use either "
+ "'keep', 'merge' or"
+ "'overwrite'.\n'");
+ }
+ break;
+ case 's':
+ args->seqnum = get_update_seqnum(optarg);
+ if (args->seqnum < 0) {
+ err_quit("Unsupported side: %s.\n"
+ "Supported sides are '0' "
+ "and '1'\n", optarg);
+ }
+ break;
+ case 'r':
+ args->raw_dev = optarg;
+ break;
+ case '?': /* help */
+ err_msg("Usage: pfiflash [OPTION...] [pfifile]");
+ err_msg("%s", doc);
+ err_msg("%s", optionsstr);
+ err_msg("\nReport bugs to %s\n",
+ PACKAGE_BUGREPORT);
+ exit(0);
+ break;
+ case 'V':
+ err_msg("%s", PROGRAM_VERSION);
+ exit(0);
+ break;
+ default:
+ err_msg("%s", usage);
+ exit(-1);
+
+ }
+ }
+
+ if (optind < argc) {
+ args->fp_in = fopen(argv[optind++], "r");
+ if ((args->fp_in) == NULL) {
+ err_sys("Cannot open PFI file %s for input",
+ argv[optind]);
+ }
+ }
+
+ return 0;
+}
+
+int main (int argc, char** argv)
+{
+ int rc = 0;
+ char err_buf[PFIFLASH_MAX_ERR_BUF_SIZE];
+ memset(err_buf, '\0', PFIFLASH_MAX_ERR_BUF_SIZE);
+
+ myargs args = {
+ .verbose = 0,
+ .seqnum = -1,
+ .complete = 0,
+ .logfile = NULL, /* "/tmp/pfiflash.log", */
+ .pdd_handling = PDD_KEEP,
+ .fp_in = stdin,
+ .raw_dev = NULL,
+ };
+
+ parse_opt(argc, argv, &args);
+ error_initlog(args.logfile);
+
+ if (!args.fp_in) {
+ rc = -1;
+ snprintf(err_buf, PFIFLASH_MAX_ERR_BUF_SIZE,
+ "No PFI input file specified!\n");
+ goto err;
+ }
+
+ if (!args.raw_dev) {
+ rc = pfiflash(args.fp_in, args.complete, args.seqnum,
+ args.pdd_handling, err_buf,
+ PFIFLASH_MAX_ERR_BUF_SIZE);
+ } else {
+ rc = pfiflash_with_raw(args.fp_in, args.complete, args.seqnum,
+ args.pdd_handling, args.raw_dev, err_buf,
+ PFIFLASH_MAX_ERR_BUF_SIZE);
+ }
+
+ if (rc != 0) {
+ goto err_fp;
+ }
+
+ err_fp:
+ if (args.fp_in != stdin)
+ fclose(args.fp_in);
+ err:
+ if (rc != 0)
+ err_msg("pfiflash: %s\nrc: %d\n", err_buf, rc);
+ return rc;
+}
diff --git a/ubi-utils/src/pfiflash.h b/ubi-utils/src/pfiflash.h
new file mode 100644
index 0000000..a063e7f
--- /dev/null
+++ b/ubi-utils/src/pfiflash.h
@@ -0,0 +1,75 @@
+#ifndef __PFIFLASH_H__
+#define __PFIFLASH_H__
+/*
+ * 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.
+ */
+
+/**
+ *
+ * @file pfi.h
+ *
+ * @author Oliver Lohmann <oliloh@de.ibm.com>
+ *
+ * @brief The pfiflash library offers an interface for using the
+ * pfiflash * utility.
+ */
+
+#include <stdio.h> /* FILE */
+
+#define PFIFLASH_MAX_ERR_BUF_SIZE 1024
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum pdd_handling_t
+{
+ PDD_KEEP = 0,
+ PDD_MERGE,
+ PDD_OVERWRITE,
+ PDD_HANDLING_NUM, /* always the last item */
+} pdd_handling_t; /**< Possible PDD handle algorithms. */
+
+/**
+ * @brief Flashes a PFI file to UBI Device 0.
+ * @param complete [0|1] Do a complete system update.
+ * @param seqnum Index in a redundant group.
+ * @param pdd_handling The PDD handling algorithm.
+ * @param rawdev Device to use for raw flashing
+ * @param err_buf An error buffer.
+ * @param err_buf_size Size of the error buffer.
+ */
+int pfiflash_with_raw(FILE* pfi, int complete, int seqnum,
+ pdd_handling_t pdd_handling, const char* rawdev,
+ char *err_buf, size_t err_buf_size);
+
+/**
+ * @brief Flashes a PFI file to UBI Device 0.
+ * @param complete [0|1] Do a complete system update.
+ * @param seqnum Index in a redundant group.
+ * @param pdd_handling The PDD handling algorithm.
+ * @param err_buf An error buffer.
+ * @param err_buf_size Size of the error buffer.
+ */
+int pfiflash(FILE* pfi, int complete, int seqnum, pdd_handling_t pdd_handling,
+ char *err_buf, size_t err_buf_size);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __PFIFLASH_H__ */
diff --git a/ubi-utils/src/pfiflash_error.h b/ubi-utils/src/pfiflash_error.h
new file mode 100644
index 0000000..c06232a
--- /dev/null
+++ b/ubi-utils/src/pfiflash_error.h
@@ -0,0 +1,71 @@
+#ifndef __PFIFLASH_ERROR_H__
+#define __PFIFLASH_ERROR_H__
+/*
+ * 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.
+ */
+
+/*
+ * Author: Drake Dowsett <dowsett@de.ibm.com>
+ * Contact: Andreas Arnez <arnez@de.ibm.com>
+ */
+
+enum pfiflash_err {
+ PFIFLASH_ERR_EOF = 1,
+ PFIFLASH_ERR_FIO,
+ PFIFLASH_ERR_UBI_OPEN,
+ PFIFLASH_ERR_UBI_CLOSE,
+ PFIFLASH_ERR_UBI_MKVOL,
+ PFIFLASH_ERR_UBI_RMVOL,
+ PFIFLASH_ERR_UBI_VOL_UPDATE,
+ PFIFLASH_ERR_UBI_VOL_FOPEN,
+ PFIFLASH_ERR_UBI_UNKNOWN,
+ PFIFLASH_ERR_UBI_VID_OOB,
+ PFIFLASH_ERR_BOOTENV_CREATE,
+ PFIFLASH_ERR_BOOTENV_READ,
+ PFIFLASH_ERR_BOOTENV_SIZE,
+ PFIFLASH_ERR_BOOTENV_WRITE,
+ PFIFLASH_ERR_PDD_UNKNOWN,
+ PFIFLASH_ERR_MTD_OPEN,
+ PFIFLASH_ERR_MTD_CLOSE,
+ PFIFLASH_ERR_CRC_CHECK,
+ PFIFLASH_ERR_MTD_ERASE
+};
+
+const char *const PFIFLASH_ERRSTR[] = {
+ "",
+ "unexpected EOF",
+ "file I/O error",
+ "couldn't open UBI",
+ "couldn't close UBI",
+ "couldn't make UBI volume %d",
+ "couldn't remove UBI volume %d",
+ "couldn't update UBI volume %d",
+ "couldn't open UBI volume %d",
+ "unknown UBI operation",
+ "PFI data contains out of bounds UBI id %d",
+ "couldn't create bootenv%s",
+ "couldn't read bootenv",
+ "couldn't resize bootenv",
+ "couldn't write bootenv on ubi%d_%d",
+ "unknown PDD handling algorithm",
+ "couldn't open MTD device %s",
+ "couldn't close MTD device %s",
+ "CRC check failed: given=0x%08x, calculated=0x%08x",
+ "couldn't erase raw mtd region"
+};
+
+#endif /* __PFIFLASH_ERROR_H__ */
diff --git a/ubi-utils/src/reader.c b/ubi-utils/src/reader.c
new file mode 100644
index 0000000..0ea8c6d
--- /dev/null
+++ b/ubi-utils/src/reader.c
@@ -0,0 +1,482 @@
+/*
+ * 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.
+ *
+ * Author: Oliver Lohmann
+ *
+ * Read in PFI (partial flash image) data and store it into internal
+ * data structures for further processing. Take also care about
+ * special handling if the data contains PDD (platform description
+ * data/boot-parameters).
+ */
+
+#include <string.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "bootenv.h"
+#include "reader.h"
+
+#define __unused __attribute__((unused))
+
+/* @FIXME hard coded offsets right now - get them from Artem? */
+#define NAND2048_DEFAULT_VID_HDR_OFF 1984
+#define NAND512_DEFAULT_VID_HDR_OFF 448
+#define NOR_DEFAULT_VID_HDR_OFF 64
+
+#define EBUF_PFI(fmt...) \
+ do { int i = snprintf(err_buf, err_buf_size, "%s\n", label); \
+ snprintf(err_buf + i, err_buf_size - i, fmt); \
+ } while (0)
+
+#define EBUF(fmt...) \
+ do { snprintf(err_buf, err_buf_size, fmt); } while (0)
+
+
+int
+read_pdd_data(FILE* fp_pdd, pdd_data_t* pdd_data,
+ char* err_buf, size_t err_buf_size)
+{
+ int rc = 0;
+ bootenv_t pdd = NULL;
+ pdd_data_t res = NULL;
+ const char* value;
+
+ res = (pdd_data_t) malloc(sizeof(struct pdd_data));
+ if (!res) {
+ rc = -ENOMEM;
+ goto err;
+ }
+ rc = bootenv_create(&pdd);
+ if (rc != 0) {
+ goto err;
+ }
+ rc = bootenv_read_txt(fp_pdd, pdd);
+ if (rc != 0) {
+ goto err;
+ }
+ rc = bootenv_get(pdd, "flash_type", &value);
+ if (rc != 0) {
+ goto err;
+ }
+
+ if (strcmp(value, "NAND") == 0) {
+
+ rc = bootenv_get_num(pdd, "flash_page_size",
+ &(res->flash_page_size));
+ if (rc != 0) {
+ EBUF("Cannot read 'flash_page_size' from pdd.");
+ goto err;
+ }
+ res->flash_type = NAND_FLASH;
+
+ switch (res->flash_page_size) {
+ case 512:
+ res->vid_hdr_offset = NAND512_DEFAULT_VID_HDR_OFF;
+ break;
+ case 2048:
+ res->vid_hdr_offset = NAND2048_DEFAULT_VID_HDR_OFF;
+ break;
+ default:
+ EBUF("Unsupported 'flash_page_size' %d.",
+ res->flash_page_size);
+ goto err;
+ }
+ }
+ else if (strcmp(value, "NOR") == 0){
+ res->flash_type = NOR_FLASH;
+ res->vid_hdr_offset = NOR_DEFAULT_VID_HDR_OFF;
+ }
+ else {
+ snprintf(err_buf, err_buf_size,
+ "Unkown flash type: %s", value);
+ goto err;
+ }
+
+ rc = bootenv_get_num(pdd, "flash_eraseblock_size",
+ &(res->eb_size));
+ if (rc != 0) {
+ EBUF("Cannot read 'flash_eraseblock_size' from pdd.");
+ goto err;
+ }
+
+ rc = bootenv_get_num(pdd, "flash_size",
+ &(res->flash_size));
+ if (rc != 0) {
+ EBUF("Cannot read 'flash_size' from pdd.");
+ goto err;
+ }
+
+ goto out;
+ err:
+ if (res) {
+ free(res);
+ res = NULL;
+ }
+ out:
+ bootenv_destroy(&pdd);
+ *pdd_data = res;
+ return rc;
+}
+
+int
+read_pfi_raw(pfi_header pfi_hd, FILE* fp_pfi __unused, pfi_raw_t* pfi_raw,
+ const char* label, char* err_buf, size_t err_buf_size)
+{
+ int rc = 0;
+ char tmp_str[PFI_KEYWORD_LEN];
+ bootenv_list_t raw_start_list = NULL;
+ pfi_raw_t res;
+ size_t size;
+
+ res = (pfi_raw_t) malloc(sizeof(struct pfi_raw));
+ if (!res)
+ return -ENOMEM;
+
+ rc = pfi_header_getnumber(pfi_hd, "size", &(res->data_size));
+ if (rc != 0) {
+ EBUF_PFI("Cannot read 'size' from PFI.");
+ goto err;
+ }
+
+ rc = pfi_header_getnumber(pfi_hd, "crc", &(res->crc));
+ if (rc != 0) {
+ EBUF_PFI("Cannot read 'crc' from PFI.");
+ goto err;
+ }
+
+ rc = pfi_header_getstring(pfi_hd, "raw_starts",
+ tmp_str, PFI_KEYWORD_LEN);
+ if (rc != 0) {
+ EBUF_PFI("Cannot read 'raw_starts' from PFI.");
+ goto err;
+ }
+
+ rc = bootenv_list_create(&raw_start_list);
+ if (rc != 0) {
+ goto err;
+ }
+
+ rc = bootenv_list_import(raw_start_list, tmp_str);
+ if (rc != 0) {
+ EBUF_PFI("Cannot translate PFI value: %s", tmp_str);
+ goto err;
+ }
+
+ rc = bootenv_list_to_num_vector(raw_start_list,
+ &size, &(res->starts));
+ res->starts_size = size;
+
+ if (rc != 0) {
+ EBUF_PFI("Cannot create numeric value array: %s", tmp_str);
+ goto err;
+ }
+
+ goto out;
+
+ err:
+ if (res) {
+ free(res);
+ res = NULL;
+ }
+ out:
+ bootenv_list_destroy(&raw_start_list);
+ *pfi_raw = res;
+ return rc;
+}
+
+int
+read_pfi_ubi(pfi_header pfi_hd, FILE* fp_pfi __unused, pfi_ubi_t* pfi_ubi,
+ const char *label, char* err_buf, size_t err_buf_size)
+{
+ int rc = 0;
+ const char** tmp_names = NULL;
+ char tmp_str[PFI_KEYWORD_LEN];
+ bootenv_list_t ubi_id_list = NULL;
+ bootenv_list_t ubi_name_list = NULL;
+ pfi_ubi_t res;
+ uint32_t i;
+ size_t size;
+
+ res = (pfi_ubi_t) calloc(1, sizeof(struct pfi_ubi));
+ if (!res)
+ return -ENOMEM;
+
+ rc = pfi_header_getnumber(pfi_hd, "size", &(res->data_size));
+ if (rc != 0) {
+ EBUF_PFI("Cannot read 'size' from PFI.");
+ goto err;
+ }
+
+ rc = pfi_header_getnumber(pfi_hd, "crc", &(res->crc));
+ if (rc != 0) {
+ EBUF_PFI("Cannot read 'crc' from PFI.");
+ goto err;
+ }
+
+ rc = pfi_header_getstring(pfi_hd, "ubi_ids", tmp_str, PFI_KEYWORD_LEN);
+ if (rc != 0) {
+ EBUF_PFI("Cannot read 'ubi_ids' from PFI.");
+ goto err;
+ }
+
+ rc = bootenv_list_create(&ubi_id_list);
+ if (rc != 0) {
+ goto err;
+ }
+ rc = bootenv_list_create(&ubi_name_list);
+ if (rc != 0) {
+ goto err;
+ }
+
+ rc = bootenv_list_import(ubi_id_list, tmp_str);
+ if (rc != 0) {
+ EBUF_PFI("Cannot translate PFI value: %s", tmp_str);
+ goto err;
+ }
+
+ rc = bootenv_list_to_num_vector(ubi_id_list, &size,
+ &(res->ids));
+ res->ids_size = size;
+ if (rc != 0) {
+ EBUF_PFI("Cannot create numeric value array: %s", tmp_str);
+ goto err;
+ }
+
+ if (res->ids_size == 0) {
+ rc = -1;
+ EBUF_PFI("Sanity check failed: No ubi_ids specified.");
+ goto err;
+ }
+
+ rc = pfi_header_getstring(pfi_hd, "ubi_type",
+ tmp_str, PFI_KEYWORD_LEN);
+ if (rc != 0) {
+ EBUF_PFI("Cannot read 'ubi_type' from PFI.");
+ goto err;
+ }
+ if (strcmp(tmp_str, "static") == 0)
+ res->type = pfi_ubi_static;
+ else if (strcmp(tmp_str, "dynamic") == 0)
+ res->type = pfi_ubi_dynamic;
+ else {
+ EBUF_PFI("Unknown ubi_type in PFI.");
+ goto err;
+ }
+
+ rc = pfi_header_getnumber(pfi_hd, "ubi_alignment", &(res->alignment));
+ if (rc != 0) {
+ EBUF_PFI("Cannot read 'ubi_alignment' from PFI.");
+ goto err;
+ }
+
+ rc = pfi_header_getnumber(pfi_hd, "ubi_size", &(res->size));
+ if (rc != 0) {
+ EBUF_PFI("Cannot read 'ubi_size' from PFI.");
+ goto err;
+ }
+
+ rc = pfi_header_getstring(pfi_hd, "ubi_names",
+ tmp_str, PFI_KEYWORD_LEN);
+ if (rc != 0) {
+ EBUF_PFI("Cannot read 'ubi_names' from PFI.");
+ goto err;
+ }
+
+ rc = bootenv_list_import(ubi_name_list, tmp_str);
+ if (rc != 0) {
+ EBUF_PFI("Cannot translate PFI value: %s", tmp_str);
+ goto err;
+ }
+ rc = bootenv_list_to_vector(ubi_name_list, &size,
+ &(tmp_names));
+ res->names_size = size;
+ if (rc != 0) {
+ EBUF_PFI("Cannot create string array: %s", tmp_str);
+ goto err;
+ }
+
+ if (res->names_size != res->ids_size) {
+ EBUF_PFI("Sanity check failed: ubi_ids list does not match "
+ "sizeof ubi_names list.");
+ rc = -1;
+ }
+
+ /* copy tmp_names to own structure */
+ res->names = (char**) calloc(1, res->names_size * sizeof (char*));
+ if (res->names == NULL)
+ goto err;
+
+ for (i = 0; i < res->names_size; i++) {
+ res->names[i] = calloc(PFI_UBI_VOL_NAME_LEN + 1, sizeof(char));
+ if (res->names[i] == NULL)
+ goto err;
+ strncpy(res->names[i], tmp_names[i], PFI_UBI_VOL_NAME_LEN + 1);
+ }
+
+ goto out;
+
+ err:
+ if (res) {
+ if (res->names) {
+ for (i = 0; i < res->names_size; i++) {
+ if (res->names[i]) {
+ free(res->names[i]);
+ }
+ }
+ free(res->names);
+ }
+ if (res->ids) {
+ free(res->ids);
+ }
+ free(res);
+ res = NULL;
+ }
+
+ out:
+ bootenv_list_destroy(&ubi_id_list);
+ bootenv_list_destroy(&ubi_name_list);
+ if (tmp_names != NULL)
+ free(tmp_names);
+ *pfi_ubi = res;
+ return rc;
+}
+
+
+int
+free_pdd_data(pdd_data_t* pdd_data)
+{
+ if (*pdd_data) {
+ free(*pdd_data);
+ }
+ *pdd_data = NULL;
+
+ return 0;
+}
+
+int
+free_pfi_raw(pfi_raw_t* pfi_raw)
+{
+ pfi_raw_t tmp = *pfi_raw;
+ if (tmp) {
+ if (tmp->starts)
+ free(tmp->starts);
+ free(tmp);
+ }
+ *pfi_raw = NULL;
+
+ return 0;
+}
+
+int
+free_pfi_ubi(pfi_ubi_t* pfi_ubi)
+{
+ size_t i;
+ pfi_ubi_t tmp = *pfi_ubi;
+ if (tmp) {
+ if (tmp->ids)
+ free(tmp->ids);
+ if (tmp->names) {
+ for (i = 0; i < tmp->names_size; i++) {
+ if (tmp->names[i]) {
+ free(tmp->names[i]);
+ }
+ }
+ free(tmp->names);
+ }
+ free(tmp);
+ }
+ *pfi_ubi = NULL;
+
+ return 0;
+}
+
+
+int
+read_pfi_headers(list_t *pfi_raws, list_t *pfi_ubis, FILE* fp_pfi,
+ char* err_buf, size_t err_buf_size)
+{
+ int rc = 0;
+ char mode[PFI_KEYWORD_LEN];
+ char label[PFI_LABEL_LEN];
+
+ *pfi_raws = mk_empty(); pfi_raw_t raw = NULL;
+ *pfi_ubis = mk_empty(); pfi_ubi_t ubi = NULL;
+ pfi_header pfi_header = NULL;
+
+ /* read all headers from PFI and store them in lists */
+ rc = pfi_header_init(&pfi_header);
+ if (rc != 0) {
+ EBUF("Cannot initialize pfi header.");
+ goto err;
+ }
+ while ((rc == 0) && !feof(fp_pfi)) {
+ rc = pfi_header_read(fp_pfi, pfi_header);
+ if (rc != 0) {
+ if (rc == PFI_DATA_START) {
+ rc = 0;
+ break; /* data section starts,
+ all headers read */
+ }
+ else {
+ goto err;
+ }
+ }
+ rc = pfi_header_getstring(pfi_header, "label", label,
+ PFI_LABEL_LEN);
+ if (rc != 0) {
+ EBUF("Cannot read 'label' from PFI.");
+ goto err;
+ }
+ rc = pfi_header_getstring(pfi_header, "mode", mode,
+ PFI_KEYWORD_LEN);
+ if (rc != 0) {
+ EBUF("Cannot read 'mode' from PFI.");
+ goto err;
+ }
+ if (strcmp(mode, "ubi") == 0) {
+ rc = read_pfi_ubi(pfi_header, fp_pfi, &ubi, label,
+ err_buf, err_buf_size);
+ if (rc != 0) {
+ goto err;
+ }
+ *pfi_ubis = append_elem(ubi, *pfi_ubis);
+ }
+ else if (strcmp(mode, "raw") == 0) {
+ rc = read_pfi_raw(pfi_header, fp_pfi, &raw, label,
+ err_buf, err_buf_size);
+ if (rc != 0) {
+ goto err;
+ }
+ *pfi_raws = append_elem(raw, *pfi_raws);
+ }
+ else {
+ EBUF("Recvieved unknown mode from PFI: %s", mode);
+ goto err;
+ }
+ }
+ goto out;
+
+ err:
+ *pfi_raws = remove_all((free_func_t)&free_pfi_raw, *pfi_raws);
+ *pfi_ubis = remove_all((free_func_t)&free_pfi_ubi, *pfi_ubis);
+ out:
+ pfi_header_destroy(&pfi_header);
+ return rc;
+
+}
diff --git a/ubi-utils/src/reader.h b/ubi-utils/src/reader.h
new file mode 100644
index 0000000..715e464
--- /dev/null
+++ b/ubi-utils/src/reader.h
@@ -0,0 +1,87 @@
+#ifndef __READER_H__
+#define __READER_H__
+/*
+ * 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.
+ *
+ * Author: Oliver Lohmann
+ *
+ * Read Platform Description Data (PDD).
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+
+#include "pfi.h"
+#include "bootenv.h"
+#include "list.h"
+
+typedef enum flash_type_t {
+ NAND_FLASH = 0,
+ NOR_FLASH,
+} flash_type_t;
+
+typedef struct pdd_data *pdd_data_t;
+typedef struct pfi_raw *pfi_raw_t;
+typedef struct pfi_ubi *pfi_ubi_t;
+
+struct pdd_data {
+ uint32_t flash_size;
+ uint32_t flash_page_size;
+ uint32_t eb_size;
+ uint32_t vid_hdr_offset;
+ flash_type_t flash_type;
+};
+
+struct pfi_raw {
+ uint32_t data_size;
+ uint32_t *starts;
+ uint32_t starts_size;
+ uint32_t crc;
+};
+
+struct pfi_ubi {
+ uint32_t data_size;
+ uint32_t alignment;
+ uint32_t *ids;
+ uint32_t ids_size;
+ char **names;
+ uint32_t names_size;
+ uint32_t size;
+ enum { pfi_ubi_dynamic, pfi_ubi_static } type;
+ int curr_seqnum; /* specifies the seqnum taken in an update,
+ default: 0 (used by pfiflash, ubimirror) */
+ uint32_t crc;
+};
+
+int read_pdd_data(FILE* fp_pdd, pdd_data_t *pdd_data,
+ char *err_buf, size_t err_buf_size);
+int read_pfi_raw(pfi_header pfi_hd, FILE* fp_pfi, pfi_raw_t *pfi_raw,
+ const char *label, char *err_buf, size_t err_buf_size);
+int read_pfi_ubi(pfi_header pfi_hd, FILE* fp_pfi, pfi_ubi_t *pfi_ubi,
+ const char *label, char *err_buf, size_t err_buf_size);
+
+/**
+ * @brief Reads all pfi headers into list structures, separated by
+ * RAW and UBI sections.
+ */
+int read_pfi_headers(list_t *pfi_raws, list_t *pfi_ubis, FILE* fp_pfi,
+ char* err_buf, size_t err_buf_size);
+int free_pdd_data(pdd_data_t *pdd_data);
+int free_pfi_raw(pfi_raw_t *raw_pfi);
+int free_pfi_ubi(pfi_ubi_t *pfi_ubi);
+
+#endif /* __READER_H__ */
diff --git a/ubi-utils/src/ubicrc32.c b/ubi-utils/src/ubicrc32.c
new file mode 100644
index 0000000..fb4ef49
--- /dev/null
+++ b/ubi-utils/src/ubicrc32.c
@@ -0,0 +1,143 @@
+/*
+ * 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.
+ *
+ * Author: Oliver Lohmann
+ *
+ * Calculate CRC32 with UBI start value for a given binary image.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <argp.h>
+#include <unistd.h>
+#include <errno.h>
+#include <mtd/ubi-header.h>
+
+#include "config.h"
+#include "crc32.h"
+
+#define BUFSIZE 4096
+
+const char *argp_program_version = PACKAGE_VERSION;
+const char *argp_program_bug_address = PACKAGE_BUGREPORT;
+static char doc[] = "\nVersion: " PACKAGE_VERSION "\n\tBuilt on "
+ BUILD_CPU" "BUILD_OS" at "__DATE__" "__TIME__"\n"
+ "\n"
+ "ubicrc32 - calculates the UBI CRC32 value and prints it to stdout.\n";
+
+static const char copyright [] __attribute__((unused)) =
+ "FIXME: insert license type"; /* FIXME */
+
+
+static struct argp_option options[] = {
+ { name: "copyright", key: 'c', arg: NULL, flags: 0,
+ doc: "Print copyright information.",
+ group: 1 },
+
+ { name: NULL, key: 0, arg: NULL, flags: 0, doc: NULL, group: 0 },
+};
+
+typedef struct myargs {
+ FILE* fp_in;
+
+ char *arg1;
+ char **options; /* [STRING...] */
+} myargs;
+
+static error_t
+parse_opt(int key, char *arg, struct argp_state *state)
+{
+ int err = 0;
+
+ myargs *args = state->input;
+
+ switch (key) {
+ case 'c':
+ fprintf(stderr, "%s\n", copyright);
+ exit(0);
+ break;
+ case ARGP_KEY_ARG:
+ args->fp_in = fopen(arg, "rb");
+ if ((args->fp_in) == NULL) {
+ fprintf(stderr,
+ "Cannot open file %s for input\n", arg);
+ exit(1);
+ }
+ args->arg1 = arg;
+ args->options = &state->argv[state->next];
+ state->next = state->argc;
+ break;
+ case ARGP_KEY_END:
+ if (err) {
+ fprintf(stderr, "\n");
+ argp_usage(state);
+ exit(1);
+ }
+ break;
+ default:
+ return(ARGP_ERR_UNKNOWN);
+ }
+
+ return 0;
+}
+
+static struct argp argp = {
+ options: options,
+ parser: parse_opt,
+ args_doc: "[file]",
+ doc: doc,
+ children: NULL,
+ help_filter: NULL,
+ argp_domain: NULL,
+};
+
+int
+main(int argc, char **argv) {
+ int rc = 0;
+ uint32_t crc32_table[256];
+ uint8_t buf[BUFSIZE];
+ size_t read;
+ uint32_t crc32;
+
+ myargs args = {
+ .fp_in = stdin,
+ .arg1 = NULL,
+ .options = NULL,
+ };
+
+ argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &args);
+
+ init_crc32_table(crc32_table);
+ crc32 = UBI_CRC32_INIT;
+ while (!feof(args.fp_in)) {
+ read = fread(buf, 1, BUFSIZE, args.fp_in);
+ if (ferror(args.fp_in)) {
+ fprintf(stderr, "I/O Error.");
+ exit(EXIT_FAILURE);
+ }
+ crc32 = clc_crc32(crc32_table, crc32, buf, read);
+ }
+
+ if (args.fp_in != stdin) {
+ fclose(args.fp_in);
+ }
+
+ fprintf(stdout, "0x%08x\n", crc32);
+ return rc;
+}
diff --git a/ubi-utils/src/ubigen.c b/ubi-utils/src/ubigen.c
new file mode 100644
index 0000000..d99ba2d
--- /dev/null
+++ b/ubi-utils/src/ubigen.c
@@ -0,0 +1,361 @@
+/*
+ * 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.
+ *
+ * Author: Oliver Lohmann
+ *
+ * Tool to add UBI headers to binary images.
+ *
+ * 1.0 Initial version
+ * 1.1 Different CRC32 start value
+ * 1.2 Removed argp because we want to use uClibc.
+ * 1.3 Minor cleanups
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <mtd/ubi-header.h>
+
+#include "ubigen.h"
+#include "config.h"
+
+#define PROGRAM_VERSION "1.3"
+
+typedef enum action_t {
+ ACT_NORMAL = 0x00000001,
+ ACT_BROKEN_UPDATE = 0x00000002,
+} action_t;
+
+static char doc[] = "\nVersion: " PROGRAM_VERSION "\n\tBuilt on "
+ BUILD_CPU" "BUILD_OS" at "__DATE__" "__TIME__"\n"
+ "\n"
+ "ubigen - a tool for adding UBI information to a binary input file.\n";
+
+static const char *optionsstr =
+" Common settings:\n"
+" -c, --copyright Print copyright information.\n"
+" -d, --debug\n"
+" -v, --verbose Print more progress information.\n"
+"\n"
+" UBI Settings:\n"
+" -A, --alignment=<num> Set the alignment size to <num> (default 1).\n"
+" Values can be specified as bytes, 'ki' or 'Mi'.\n"
+" -B, --blocksize=<num> Set the eraseblock size to <num> (default 128\n"
+" KiB).\n"
+" Values can be specified as bytes, 'ki' or 'Mi'.\n"
+" -E, --erasecount=<num> Set the erase count to <num> (default 0)\n"
+" -I, --id=<num> The UBI volume id.\n"
+" -O, --offset=<num> Offset from start of an erase block to the UBI\n"
+" volume header.\n"
+" -T, --type=<num> The UBI volume type:\n"
+" 1 = dynamic, 2 = static\n"
+" -X, --setver=<num> Set UBI version number to <num> (default 1)\n"
+"\n"
+" Input/Output:\n"
+" -i, --infile=<filename> Read input from file.\n"
+" -o, --outfile=<filename> Write output to file (default is stdout).\n"
+"\n"
+" Special options:\n"
+" -U, --broken-update=<leb> Create an ubi image which simulates a broken\n"
+" update.\n"
+" <leb> specifies the logical eraseblock number to\n"
+" update.\n"
+"\n"
+" -?, --help Give this help list\n"
+" --usage Give a short usage message\n"
+" -V, --version Print program version\n";
+
+static const char *usage =
+"Usage: ubigen [-cdv?V] [-A <num>] [-B <num>] [-E <num>] [-I <num>]\n"
+" [-O <num>] [-T <num>] [-X <num>] [-i <filename>] [-o <filename>]\n"
+" [-U <leb>] [--copyright] [--debug] [--verbose] [--alignment=<num>]\n"
+" [--blocksize=<num>] [--erasecount=<num>] [--id=<num>]\n"
+" [--offset=<num>] [--type=<num>] [--setver=<num>]\n"
+" [--infile=<filename>] [--outfile=<filename>]\n"
+" [--broken-update=<leb>] [--help] [--usage] [--version]\n";
+
+struct option long_options[] = {
+ { .name = "copyright", .has_arg = 0, .flag = NULL, .val = 'c' },
+ { .name = "debug", .has_arg = 0, .flag = NULL, .val = 'd' },
+ { .name = "verbose", .has_arg = 0, .flag = NULL, .val = 'v' },
+ { .name = "alignment", .has_arg = 1, .flag = NULL, .val = 'A' },
+ { .name = "blocksize", .has_arg = 1, .flag = NULL, .val = 'B' },
+ { .name = "erasecount", .has_arg = 1, .flag = NULL, .val = 'E' },
+ { .name = "id", .has_arg = 1, .flag = NULL, .val = 'I' },
+ { .name = "offset", .has_arg = 1, .flag = NULL, .val = 'O' },
+ { .name = "type", .has_arg = 1, .flag = NULL, .val = 'T' },
+ { .name = "setver", .has_arg = 1, .flag = NULL, .val = 'X' },
+ { .name = "infile", .has_arg = 1, .flag = NULL, .val = 'i' },
+ { .name = "outfile", .has_arg = 1, .flag = NULL, .val = 'o' },
+ { .name = "broken-update", .has_arg = 1, .flag = NULL, .val = 'U' },
+ { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' },
+ { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 },
+ { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' },
+ { NULL, 0, NULL, 0}
+};
+
+static const char copyright [] __attribute__((unused)) =
+ "Copyright IBM Corp 2006";
+
+#define CHECK_ENDP(option, endp) do { \
+ if (*endp) { \
+ fprintf(stderr, \
+ "Parse error option \'%s\'. " \
+ "No correct numeric value.\n" \
+ , option); \
+ exit(EXIT_FAILURE); \
+ } \
+} while(0)
+
+typedef struct myargs {
+ /* common settings */
+ action_t action;
+ int verbose;
+
+ int32_t id;
+ uint8_t type;
+ uint32_t eb_size;
+ uint64_t ec;
+ uint8_t version;
+ uint32_t hdr_offset;
+ uint32_t update_block;
+ uint32_t alignment;
+
+ FILE* fp_in;
+ FILE* fp_out;
+
+ /* special stuff needed to get additional arguments */
+ char *arg1;
+ char **options; /* [STRING...] */
+} myargs;
+
+
+static int ustrtoul(const char *cp, char **endp, unsigned int base)
+{
+ unsigned long result = strtoul(cp, endp, base);
+
+ switch (**endp) {
+ case 'G':
+ result *= 1024;
+ case 'M':
+ result *= 1024;
+ case 'k':
+ case 'K':
+ result *= 1024;
+ /* "Ki", "ki", "Mi" or "Gi" are to be used. */
+ if ((*endp)[1] == 'i')
+ (*endp) += 2;
+ }
+ return result;
+}
+
+static int
+parse_opt(int argc, char **argv, myargs *args)
+{
+ int err = 0;
+ char* endp;
+
+ while (1) {
+ int key;
+
+ key = getopt_long(argc, argv, "cdvA:B:E:I:O:T:X:i:o:U:?V",
+ long_options, NULL);
+ if (key == -1)
+ break;
+
+ switch (key) {
+ case 'c':
+ fprintf(stderr, "%s\n", copyright);
+ exit(0);
+ break;
+ case 'o': /* output */
+ args->fp_out = fopen(optarg, "wb");
+ if ((args->fp_out) == NULL) {
+ fprintf(stderr, "Cannot open file %s "
+ "for output\n", optarg);
+ exit(1);
+ }
+ break;
+ case 'i': /* input */
+ args->fp_in = fopen(optarg, "rb");
+ if ((args->fp_in) == NULL) {
+ fprintf(stderr, "Cannot open file %s "
+ "for input\n", optarg);
+ exit(1);
+ }
+ break;
+ case 'v': /* verbose */
+ args->verbose = 1;
+ break;
+
+ case 'B': /* eb_size */
+ args->eb_size =
+ (uint32_t)ustrtoul(optarg, &endp, 0);
+ CHECK_ENDP("B", endp);
+ break;
+ case 'E': /* erasecount */
+ args->ec = (uint64_t)strtoul(optarg, &endp, 0);
+ CHECK_ENDP("E", endp);
+ break;
+ case 'I': /* id */
+ args->id = (uint16_t)strtoul(optarg, &endp, 0);
+ CHECK_ENDP("I", endp);
+ break;
+ case 'T': /* type */
+ args->type =
+ (uint16_t)strtoul(optarg, &endp, 0);
+ CHECK_ENDP("T", endp);
+ break;
+ case 'X': /* versionnr */
+ args->version =
+ (uint8_t)strtoul(optarg, &endp, 0);
+ CHECK_ENDP("X", endp);
+ break;
+ case 'O': /* offset for volume hdr */
+ args->hdr_offset =
+ (uint32_t) strtoul(optarg, &endp, 0);
+ CHECK_ENDP("O", endp);
+ break;
+
+ case 'U': /* broken update */
+ args->action = ACT_BROKEN_UPDATE;
+ args->update_block =
+ (uint32_t) strtoul(optarg, &endp, 0);
+ CHECK_ENDP("U", endp);
+ break;
+
+ case '?': /* help */
+ fprintf(stderr, "Usage: ubigen [OPTION...]\n");
+ fprintf(stderr, "%s", doc);
+ fprintf(stderr, "%s", optionsstr);
+ fprintf(stderr, "\nReport bugs to %s\n",
+ PACKAGE_BUGREPORT);
+ exit(0);
+ break;
+
+ case 'V':
+ fprintf(stderr, "%s\n", PROGRAM_VERSION);
+ exit(0);
+ break;
+
+ default:
+ fprintf(stderr, "%s", usage);
+ exit(-1);
+ }
+ }
+
+ if (optind < argc) {
+ if (!args->fp_in) {
+ args->fp_in = fopen(argv[optind++], "rb");
+ if ((args->fp_in) == NULL) {
+ fprintf(stderr, "Cannot open file %s for "
+ "input\n", argv[optind]);
+ exit(1);
+ }
+ }
+ }
+ if (args->id < 0) {
+ err = 1;
+ fprintf(stderr,
+ "Please specify an UBI Volume ID.\n");
+ }
+ if (args->type == 0) {
+ err = 1;
+ fprintf(stderr,
+ "Please specify an UBI Volume type.\n");
+ }
+ if (err) {
+ fprintf(stderr, "%s", usage);
+ exit(1);
+ }
+
+ return 0;
+}
+
+
+int
+main(int argc, char **argv)
+{
+ int rc = 0;
+ ubi_info_t u;
+ struct stat file_info;
+ off_t input_len = 0; /* only used in static volumes */
+
+ myargs args = {
+ .action = ACT_NORMAL,
+ .verbose = 0,
+
+ .id = -1,
+ .type = 0,
+ .eb_size = 0,
+ .update_block = 0,
+ .ec = 0,
+ .version = 0,
+ .hdr_offset = (DEFAULT_PAGESIZE) - (UBI_VID_HDR_SIZE),
+ .alignment = 1,
+
+ .fp_in = NULL,
+ .fp_out = stdout,
+ /* arguments */
+ .arg1 = NULL,
+ .options = NULL,
+ };
+
+ ubigen_init(); /* Init CRC32 table in ubigen */
+
+ /* parse arguments */
+ parse_opt(argc, argv, &args);
+
+ if (fstat(fileno(args.fp_in), &file_info) != 0) {
+ fprintf(stderr, "Cannot fetch file size "
+ "from input file.\n");
+ }
+ input_len = file_info.st_size;
+
+ rc = ubigen_create(&u, (uint32_t)args.id, args.type,
+ args.eb_size, args.ec, args.alignment,
+ args.version, args.hdr_offset, 0 ,input_len,
+ args.fp_in, args.fp_out);
+
+ if (rc != 0) {
+ fprintf(stderr, "Cannot create UBI info handler rc: %d\n", rc);
+ exit(EXIT_FAILURE);
+ }
+
+ if (!args.fp_in || !args.fp_out) {
+ fprintf(stderr, "Input/Output error.\n");
+ exit(EXIT_FAILURE);
+
+ }
+
+ if (args.action & ACT_NORMAL) {
+ rc = ubigen_write_complete(u);
+ }
+ else if (args.action & ACT_BROKEN_UPDATE) {
+ rc = ubigen_write_broken_update(u, args.update_block);
+ }
+ if (rc != 0) {
+ fprintf(stderr, "Error converting input data.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ rc = ubigen_destroy(&u);
+ return rc;
+}
diff --git a/ubi-utils/src/ubigen.h b/ubi-utils/src/ubigen.h
new file mode 100644
index 0000000..9e9e8ec
--- /dev/null
+++ b/ubi-utils/src/ubigen.h
@@ -0,0 +1,149 @@
+#ifndef __UBIGEN_H__
+#define __UBIGEN_H__
+/*
+ * 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.
+ *
+ * Author: Frank Haverkamp
+ *
+ * An utility to update UBI volumes.
+ */
+
+#include <stdio.h> /* FILE */
+#include <stdint.h>
+#include <mtd/ubi-header.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define DEFAULT_BLOCKSIZE (128 * 1024)
+#define DEFAULT_PAGESIZE (2*1024)
+
+#define EUBIGEN_INVALID_TYPE 1
+#define EUBIGEN_INVALID_HDR_OFFSET 2
+#define EUBIGEN_INVALID_ALIGNMENT 3
+#define EUBIGEN_TOO_SMALL_EB 4
+#define EUBIGEN_MAX_ERROR 5
+
+
+typedef enum action {
+ NO_ERROR = 0x00000000,
+ BROKEN_HDR_CRC = 0x00000001,
+ BROKEN_DATA_CRC = 0x00000002,
+ BROKEN_DATA_SIZE = 0x00000004,
+ BROKEN_OMIT_BLK = 0x00000008,
+ MARK_AS_UPDATE = 0x00000010,
+} ubigen_action_t;
+
+typedef struct ubi_info *ubi_info_t;
+
+/**
+ * @brief Initialize the internal CRC32 table.
+ * @note Necessary because of the used crc32 function in UBI.
+ * A usage of CRC32, from e.g. zlib will fail.
+ */
+void ubigen_init(void);
+
+/**
+ * @brief Create an ubigen handle.
+ * @param ...
+ * @return 0 On sucess.
+ * else Error.
+ * @note This parameterlist is ugly. But we have to use
+ * two big structs and meta information internally,
+ * filling them would be even uglier.
+ */
+int ubigen_create(ubi_info_t *u, uint32_t vol_id, uint8_t vol_type,
+ uint32_t eb_size, uint64_t ec, uint32_t alignment,
+ uint8_t version, uint32_t vid_hdr_offset,
+ uint8_t compat_flag, size_t data_size,
+ FILE* fp_in, FILE* fp_out);
+
+/**
+ * @brief Destroy an ubigen handle.
+ * @param u Handle to free.
+ * @return 0 On success.
+ * else Error.
+ */
+int ubigen_destroy(ubi_info_t *u);
+
+/**
+ * @brief Get number of total logical EBs, necessary for the
+ * complete storage of data in the handle.
+ * @param u The handle.
+ * @return 0 On success.
+ * else Error.
+ */
+int ubigen_get_leb_total(ubi_info_t u, size_t* total);
+
+/**
+ * @brief Get the size in bytes of one logical EB in the handle.
+ * @param u The handle.
+ * @return 0 On success.
+ * else Error.
+ */
+int ubigen_get_leb_size(ubi_info_t u, size_t* size);
+
+
+/**
+ * @brief Write a logical EB (fits exactly into 1 physical EB).
+ * @param u Handle which holds all necessary data.
+ * @param action Additional operations which shall be applied on this
+ * logical eraseblock. Mostly injecting artifical errors.
+ * @return 0 On success.
+ * else Error.
+ */
+int ubigen_write_leb(ubi_info_t u, ubigen_action_t action);
+
+/**
+ * @brief Write a complete array of logical eraseblocks at once.
+ * @param u Handle which holds all necessary data.
+ * @return 0 On success.
+ * else Error.
+ */
+int ubigen_write_complete(ubi_info_t u);
+
+/**
+ * @brief Write a single block which is extracted from the
+ * binary input data.
+ * @param u Handle which holds all necessary data.
+ * @param blk Logical eraseblock which shall hold a inc. copy entry
+ * and a bad data crc.
+ * @return 0 On success.
+ * else Error.
+ */
+int ubigen_write_broken_update(ubi_info_t u, uint32_t blk);
+
+/**
+ * @brief Use the current ubi_info data and some additional data
+ * to set an UBI volume table entry from it.
+ * @param u Handle which holds some of the necessary data.
+ * @param res_bytes Number of reserved bytes which is stored in the volume
+ * table entry.
+ * @param name A string which shall be used as a volume label.
+ * @param lvol_r A pointer to a volume table entry.
+ * @return 0 On success.
+ * else Error.
+ */
+int ubigen_set_lvol_rec(ubi_info_t u, size_t reserved_bytes,
+ const char* name, struct ubi_vol_tbl_record *lvol_rec);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __UBIGEN_H__ */
diff --git a/ubi-utils/src/ubimirror.c b/ubi-utils/src/ubimirror.c
new file mode 100644
index 0000000..eeedb3a
--- /dev/null
+++ b/ubi-utils/src/ubimirror.c
@@ -0,0 +1,214 @@
+/*
+ * 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.
+ *
+ * Author: Oliver Lohmann
+ *
+ * 1.2 Removed argp because we want to use uClibc.
+ * 1.3 Minor cleanups
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <errno.h>
+#include <mtd/ubi-header.h>
+
+#include "config.h"
+#include "error.h"
+#include "example_ubi.h"
+#include "ubimirror.h"
+
+#define PROGRAM_VERSION "1.3"
+
+typedef enum action_t {
+ ACT_NORMAL = 0,
+ ACT_ARGP_ABORT,
+ ACT_ARGP_ERR,
+} action_t;
+
+#define ABORT_ARGP do { \
+ args->action = ACT_ARGP_ABORT; \
+} while (0)
+
+#define ERR_ARGP do { \
+ args->action = ACT_ARGP_ERR; \
+} while (0)
+
+#define VOL_ARGS_MAX 2
+
+static char doc[] = "\nVersion: " PROGRAM_VERSION "\n\tBuilt on "
+ BUILD_CPU" "BUILD_OS" at "__DATE__" "__TIME__"\n"
+ "\n"
+ "ubimirror - mirrors ubi volumes.\n";
+
+static const char *optionsstr =
+" -c, --copyright Print copyright information.\n"
+" -s, --side=<seqnum> Use the side <seqnum> as source.\n"
+" -?, --help Give this help list\n"
+" --usage Give a short usage message\n"
+" -V, --version Print program version\n";
+
+static const char *usage =
+"Usage: ubimirror [-c?V] [-s <seqnum>] [--copyright] [--side=<seqnum>]\n"
+" [--help] [--usage] [--version] <source> <destination>\n";
+
+static const char copyright [] __attribute__((unused)) =
+ "(C) IBM Coorporation 2007";
+
+struct option long_options[] = {
+ { .name = "copyright", .has_arg = 0, .flag = NULL, .val = 'c' },
+ { .name = "side", .has_arg = 1, .flag = NULL, .val = 's' },
+ { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' },
+ { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 },
+ { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' },
+ { NULL, 0, NULL, 0}
+};
+
+typedef struct myargs {
+ action_t action;
+ int side;
+ int vol_no; /* index of current volume */
+ /* @FIXME replace by bootenv_list, makes live easier */
+ /* @FIXME remove the constraint of two entries in the array */
+ const char* vol[VOL_ARGS_MAX]; /* comma separated list of src/dst
+ volumes */
+ char *arg1;
+ char **options; /* [STRING...] */
+} myargs;
+
+static int
+get_update_side(const char* str)
+{
+ uint32_t i = strtoul(str, NULL, 0);
+
+ if ((i != 0) && (i != 1)) {
+ return -1;
+ }
+ return i;
+}
+
+
+static int
+parse_opt(int argc, char **argv, myargs *args)
+{
+ while (1) {
+ int key;
+
+ key = getopt_long(argc, argv, "cs:?V", long_options, NULL);
+ if (key == -1)
+ break;
+
+ switch (key) {
+ case 'c':
+ err_msg("%s", copyright);
+ ABORT_ARGP;
+ break;
+ case 's':
+ args->side = get_update_side(optarg);
+ if (args->side < 0) {
+ err_msg("Unsupported seqnum: %s.\n"
+ "Supported seqnums are '0' "
+ "and '1'\n", optarg);
+ ERR_ARGP;
+ }
+ break;
+ case '?': /* help */
+ err_msg("Usage: ubimirror [OPTION...] "
+ "<source> <destination>\n");
+ err_msg("%s", doc);
+ err_msg("%s", optionsstr);
+ err_msg("\nReport bugs to %s\n",
+ PACKAGE_BUGREPORT);
+ exit(0);
+ break;
+ case 'V':
+ err_msg("%s", PROGRAM_VERSION);
+ exit(0);
+ break;
+ default:
+ err_msg("%s", usage);
+ exit(-1);
+ }
+ }
+
+ while (optind < argc) {
+ /* only two entries allowed */
+ if (args->vol_no >= VOL_ARGS_MAX) {
+ err_msg("%s", usage);
+ ERR_ARGP;
+ }
+ args->vol[(args->vol_no)++] = argv[optind++];
+ }
+
+ return 0;
+}
+
+
+int
+main(int argc, char **argv) {
+ int rc = 0;
+ unsigned int ids[VOL_ARGS_MAX];
+ char err_buf[1024];
+
+ myargs args = {
+ .action = ACT_NORMAL,
+ .side = -1,
+ .vol_no = 0,
+ .vol = {"", ""},
+ .options = NULL,
+ };
+
+ parse_opt(argc, argv, &args);
+ if (args.action == ACT_ARGP_ERR) {
+ rc = 127;
+ goto err;
+ }
+ if (args.action == ACT_ARGP_ABORT) {
+ rc = 126;
+ goto out;
+ }
+ if (args.vol_no < VOL_ARGS_MAX) {
+ fprintf(stderr, "missing volume number for %s\n",
+ args.vol_no == 0 ? "source and target" : "target");
+ rc = 125;
+ goto out;
+ }
+ for( rc = 0; rc < args.vol_no; ++rc){
+ char *endp;
+ ids[rc] = strtoul(args.vol[rc], &endp, 0);
+ if( *endp != '\0' ){
+ fprintf(stderr, "invalid volume number %s\n",
+ args.vol[rc]);
+ rc = 125;
+ goto out;
+ }
+ }
+ rc = ubimirror(EXAMPLE_UBI_DEVICE, args.side, ids, args.vol_no,
+ err_buf, sizeof(err_buf));
+ if( rc ){
+ err_buf[sizeof err_buf - 1] = '\0';
+ fprintf(stderr, err_buf);
+ if( rc < 0 )
+ rc = -rc;
+ }
+ out:
+ err:
+ return rc;
+}
diff --git a/ubi-utils/src/ubimirror.h b/ubi-utils/src/ubimirror.h
new file mode 100644
index 0000000..d7ae2ad
--- /dev/null
+++ b/ubi-utils/src/ubimirror.h
@@ -0,0 +1,66 @@
+#ifndef __UBIMIRROR_H__
+#define __UBIMIRROR_H__
+/*
+ * 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.
+ *
+ * Author: Oliver Lohmann
+ *
+ * An utility to mirror UBI volumes.
+ */
+
+#include <stdint.h>
+
+/**
+ * @def EUBIMIRROR_SRC_EQ_DST
+ * @brief Given source volume is also in the set of destination volumes.
+ */
+#define EUBIMIRROR_SRC_EQ_DST 20
+
+/**
+ * @def EUBIMIRROR_NO_SRC
+ * @brief The given source volume does not exist.
+ */
+#define EUBIMIRROR_NO_SRC 21
+
+/**
+ * @def EUBIMIRROR_NO_DST
+ * @brief One of the given destination volumes does not exist.
+ */
+#define EUBIMIRROR_NO_DST 22
+
+/**
+ * @brief Mirrors UBI devices from a source device (specified by seqnum)
+ * to n target devices.
+ * @param devno Device number used by the UBI operations.
+ * @param seqnum An index into ids (defines the src_id).
+ * @param ids An array of ids.
+ * @param ids_size The number of entries in the ids array.
+ * @param err_buf A buffer to store verbose error messages.
+ * @param err_buf_size The size of the error buffer.
+ *
+ * @note A seqnum of value < 0 defaults to a seqnum of 0.
+ * @note A seqnum exceeding the range of ids_size defaults to 0.
+ * @note An empty ids list results in a empty stmt.
+ * @pre The UBI volume which shall be used as source volume exists.
+ * @pre The UBI volumes which are defined as destination volumes exist.
+ * @post The content of the UBI volume which was defined as source volume
+ * equals the content of the volumes which were defined as destination.
+ */
+int ubimirror(uint32_t devno, int seqnum, uint32_t* ids, ssize_t ids_size,
+ char *err_buf, size_t err_buf_size);
+
+#endif /* __UBIMIRROR_H__ */
diff --git a/ubi-utils/src/ubimkvol.c b/ubi-utils/src/ubimkvol.c
new file mode 100644
index 0000000..1368671
--- /dev/null
+++ b/ubi-utils/src/ubimkvol.c
@@ -0,0 +1,322 @@
+/*
+ * 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.
+ */
+
+/*
+ * An utility to create UBI volumes.
+ *
+ * Author: Artem B. Bityutskiy <dedekind@linutronix.de>
+ * Frank Haverkamp <haver@vnet.ibm.com>
+ *
+ * 1.0 Initial release
+ * 1.1 Does not support erase blocks anymore. This is replaced by
+ * the number of bytes.
+ * 1.2 Reworked the user-interface to use argp.
+ * 1.3 Removed argp because we want to use uClibc.
+ * 1.4 Minor cleanups
+ * 1.5 Use a different libubi
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <config.h>
+#include <libubi.h>
+
+#define PROGRAM_VERSION "1.5"
+
+/*
+ * The variables below are set by command line arguments.
+ */
+struct args {
+ int devn;
+ int vol_id;
+ int vol_type;
+ long long bytes;
+ int alignment;
+ char *name;
+ int nlen;
+ char node[256];
+
+ /* special stuff needed to get additional arguments */
+ char *arg1;
+ char **options; /* [STRING...] */
+};
+
+static struct args myargs = {
+ .vol_type = UBI_DYNAMIC_VOLUME,
+ .devn = -1,
+ .bytes = 0,
+ .alignment = 1,
+ .vol_id = UBI_VOL_NUM_AUTO,
+ .name = NULL,
+ .nlen = 0,
+};
+
+static int param_sanity_check(struct args *args, libubi_t libubi);
+
+static char doc[] = "\nVersion: " PROGRAM_VERSION "\n\t"
+ BUILD_OS" "BUILD_CPU" at "__DATE__" "__TIME__"\n"
+ "\nMake UBI Volume.\n";
+
+static const char *optionsstr =
+" -a, --alignment=<alignment> volume alignment (default is 1)\n"
+" -d, --devn=<devn> UBI device\n"
+" -n, --vol_id=<volume id> UBI volume id, if not specified, the volume ID\n"
+" will be assigned automatically\n"
+" -N, --name=<name> volume name\n"
+" -s, --size=<bytes> volume size volume size in bytes, kilobytes (KiB)\n"
+" or megabytes (MiB)\n"
+" -t, --type=<static|dynamic> volume type (dynamic, static), default is\n"
+" dynamic\n"
+" -?, --help Give this help list\n"
+" --usage Give a short usage message\n"
+" -V, --version Print program version\n";
+
+static const char *usage =
+"Usage: ubimkvol [-?V] [-a <alignment>] [-d <devn>] [-n <volume id>]\n"
+" [-N <name>] [-s <bytes>] [-t <static|dynamic>]\n"
+" [--alignment=<alignment>] [--devn=<devn>] [--vol_id=<volume id>]\n"
+" [--name=<name>] [--size=<bytes>] [--type=<static|dynamic>] [--help]\n"
+" [--usage] [--version]\n";
+
+struct option long_options[] = {
+ { .name = "alignment", .has_arg = 1, .flag = NULL, .val = 'a' },
+ { .name = "devn", .has_arg = 1, .flag = NULL, .val = 'd' },
+ { .name = "vol_id", .has_arg = 1, .flag = NULL, .val = 'n' },
+ { .name = "name", .has_arg = 1, .flag = NULL, .val = 'N' },
+ { .name = "size", .has_arg = 1, .flag = NULL, .val = 's' },
+ { .name = "type", .has_arg = 1, .flag = NULL, .val = 't' },
+ { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' },
+ { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 },
+ { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' },
+ { NULL, 0, NULL, 0}
+};
+
+/*
+ * @brief Parse the arguments passed into the test case.
+ *
+ * @param argc The number of arguments
+ * @param argv The list of arguments
+ * @param args Pointer to argument structure
+ *
+ * @return error
+ *
+ */
+static int
+parse_opt(int argc, char **argv, struct args *args)
+{
+ char *endp;
+
+ while (1) {
+ int key;
+
+ key = getopt_long(argc, argv, "a:d:n:N:s:t:?V", long_options, NULL);
+ if (key == -1)
+ break;
+
+ switch (key) {
+ case 't':
+ if (!strcmp(optarg, "dynamic"))
+ args->vol_type = UBI_DYNAMIC_VOLUME;
+ else if (!strcmp(optarg, "static"))
+ args->vol_type = UBI_STATIC_VOLUME;
+ else {
+ fprintf(stderr,
+ "Bad volume type: \"%s\"\n",
+ optarg);
+ goto out;
+ }
+ break;
+ case 's':
+ args->bytes = strtoull(optarg, &endp, 0);
+ if (endp == optarg || args->bytes < 0) {
+ fprintf(stderr,
+ "Bad volume size: \"%s\"\n",
+ optarg);
+ goto out;
+ }
+ if (endp != '\0') {
+ if (strcmp(endp, "KiB") == 0)
+ args->bytes *= 1024;
+ else if (strcmp(endp, "MiB") == 0)
+ args->bytes *= 1024*1024;
+ }
+ break;
+ case 'a':
+ args->alignment = strtoul(optarg, &endp, 0);
+ if (*endp != '\0' || endp == optarg ||
+ args->alignment <= 0) {
+ fprintf(stderr, "Bad volume alignment: "
+ "\"%s\"\n", optarg);
+ goto out;
+ }
+ break;
+ case 'd': /* --devn=<device number> */
+ args->devn = strtoul(optarg, &endp, 0);
+ if (*endp != '\0' || endp == optarg ||
+ args->devn < 0) {
+ fprintf(stderr,
+ "Bad UBI device number: "
+ "\"%s\"\n", optarg);
+ goto out;
+ }
+ sprintf(args->node, "/dev/ubi%d", args->devn);
+ break;
+ case 'n': /* --volid=<volume id> */
+ args->vol_id = strtoul(optarg, &endp, 0);
+ if (*endp != '\0' ||
+ endp == optarg ||
+ (args->vol_id < 0 &&
+ args->vol_id != UBI_DYNAMIC_VOLUME)) {
+ fprintf(stderr, "Bad volume ID: "
+ "\"%s\"\n", optarg);
+ goto out;
+ }
+ break;
+ case 'N':
+ args->name = optarg;
+ args->nlen = strlen(args->name);
+ break;
+
+ case ':':
+ fprintf(stderr, "Parameter is missing\n");
+ goto out;
+
+ case '?': /* help */
+ fprintf(stderr,
+ "Usage: ubimkvol [OPTION...]\n");
+ fprintf(stderr, "%s", doc);
+ fprintf(stderr, "%s", optionsstr);
+ fprintf(stderr, "\nReport bugs to %s\n",
+ PACKAGE_BUGREPORT);
+ exit(0);
+ break;
+
+ case 'V':
+ fprintf(stderr, "%s\n", PROGRAM_VERSION);
+ exit(0);
+ break;
+
+ default:
+ fprintf(stderr, "%s", usage);
+ exit(-1);
+ }
+ }
+
+ return 0;
+ out:
+ return -1;
+}
+
+static int param_sanity_check(struct args *args, libubi_t libubi)
+{
+ int err, len;
+ struct ubi_info ubi;
+
+ if (args->bytes == 0) {
+ fprintf(stderr, "Volume size was not specified\n");
+ goto out;
+ }
+
+ if (args->name == NULL) {
+ fprintf(stderr, "Volume name was not specified\n");
+ goto out;
+ }
+
+ err = ubi_get_info(libubi, &ubi);
+ if (err)
+ return -1;
+
+ if (args->devn >= (int)ubi.dev_count) {
+ fprintf(stderr, "Device %d does not exist\n", args->devn);
+ goto out;
+ }
+
+ len = strlen(args->name);
+ if (len > UBI_MAX_VOLUME_NAME) {
+ fprintf(stderr, "Too long name (%d symbols), max is %d\n",
+ len, UBI_MAX_VOLUME_NAME);
+ goto out;
+ }
+
+ return 0;
+out:
+ errno = EINVAL;
+ return -1;
+}
+
+int main(int argc, char * const argv[])
+{
+ int err;
+ libubi_t libubi;
+ struct ubi_mkvol_request req;
+
+ err = parse_opt(argc, (char **)argv, &myargs);
+ if (err) {
+ fprintf(stderr, "Wrong options ...\n");
+ return err == 1 ? 0 : -1;
+ }
+
+ if (myargs.devn == -1) {
+ fprintf(stderr, "Device number was not specified\n");
+ fprintf(stderr, "Use -h option for help\n");
+ return -1;
+ }
+
+ libubi = libubi_open();
+ if (libubi == NULL) {
+ perror("Cannot open libubi");
+ return -1;
+ }
+
+ err = param_sanity_check(&myargs, libubi);
+ if (err) {
+ perror("Input parameters check");
+ fprintf(stderr, "Use -h option for help\n");
+ goto out_libubi;
+ }
+
+ req.vol_id = myargs.vol_id;
+ req.alignment = myargs.alignment;
+ req.bytes = myargs.bytes;
+ req.vol_type = myargs.vol_type;
+ req.name = myargs.name;
+
+ err = ubi_mkvol(libubi, myargs.node, &req);
+ if (err < 0) {
+ perror("Cannot create volume");
+ fprintf(stderr, " err=%d\n", err);
+ goto out_libubi;
+ }
+
+ /* printf("Created volume %d, %lld bytes, type %s, name %s\n",
+ vol_id, bytes, vol_type == UBI_DYNAMIC_VOLUME ?
+ "dynamic" : "static", name); */
+
+ myargs.vol_id = err;
+ libubi_close(libubi);
+ return 0;
+
+out_libubi:
+ libubi_close(libubi);
+ return -1;
+}
diff --git a/ubi-utils/src/ubirmvol.c b/ubi-utils/src/ubirmvol.c
new file mode 100644
index 0000000..f32cbe0
--- /dev/null
+++ b/ubi-utils/src/ubirmvol.c
@@ -0,0 +1,230 @@
+/*
+ * 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.
+ */
+
+/*
+ * An utility to remove UBI volumes.
+ *
+ * Author: Artem B. Bityutskiy <dedekind@linutronix.de>
+ * Frank Haverkamp <haver@vnet.ibm.com>
+ *
+ * 1.1 Reworked the userinterface to use argp.
+ * 1.2 Removed argp because we want to use uClibc.
+ * 1.3 Minor cleanups
+ * 1.4 Use a different libubi
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <config.h>
+#include <libubi.h>
+
+#define PROGRAM_VERSION "1.4"
+
+/*
+ * The below variables are set by command line options.
+ */
+struct args {
+ int devn;
+ int vol_id;
+ char node[256];
+
+ /* special stuff needed to get additional arguments */
+ char *arg1;
+ char **options; /* [STRING...] */
+};
+
+static struct args myargs = {
+ .devn = -1,
+ .vol_id = -1,
+
+ .arg1 = NULL,
+ .options = NULL,
+};
+
+static int param_sanity_check(struct args *args, libubi_t libubi);
+
+static char doc[] = "\nVersion: " PROGRAM_VERSION "\n\t"
+ BUILD_OS" "BUILD_CPU" at "__DATE__" "__TIME__"\n"
+ "\nMake UBI Volume.\n";
+
+static const char *optionsstr =
+" -d, --devn=<devn> UBI device\n"
+" -n, --vol_id=<volume id> UBI volume id, if not specified, the volume ID\n"
+" will be assigned automatically\n"
+" -?, --help Give this help list\n"
+" --usage Give a short usage message\n"
+" -V, --version Print program version\n";
+
+static const char *usage =
+"Usage: ubirmvol [-?V] [-d <devn>] [-n <volume id>] [--devn=<devn>]\n"
+" [--vol_id=<volume id>] [--help] [--usage] [--version]\n";
+
+struct option long_options[] = {
+ { .name = "devn", .has_arg = 1, .flag = NULL, .val = 'd' },
+ { .name = "vol_id", .has_arg = 1, .flag = NULL, .val = 'n' },
+ { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' },
+ { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 },
+ { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' },
+ { NULL, 0, NULL, 0}
+};
+
+/*
+ * @brief Parse the arguments passed into the test case.
+ *
+ * @param argc The number of arguments
+ * @param argv The list of arguments
+ * @param args Pointer to argument structure
+ *
+ * @return error
+ *
+ */
+static int
+parse_opt(int argc, char **argv, struct args *args)
+{
+ char *endp;
+
+ while (1) {
+ int key;
+
+ key = getopt_long(argc, argv, "d:n:?V", long_options, NULL);
+ if (key == -1)
+ break;
+
+ switch (key) {
+ case 'd': /* --devn=<device number> */
+ args->devn = strtoul(optarg, &endp, 0);
+ if (*endp != '\0' || endp == optarg ||
+ args->devn < 0) {
+ fprintf(stderr,
+ "Bad UBI device number: "
+ "\"%s\"\n", optarg);
+ goto out;
+ }
+ sprintf(args->node, "/dev/ubi%d", args->devn);
+ break;
+ case 'n': /* --volid=<volume id> */
+ args->vol_id = strtoul(optarg, &endp, 0);
+ if (*endp != '\0' || endp == optarg ||
+ (args->vol_id < 0 &&
+ args->vol_id != UBI_DYNAMIC_VOLUME)) {
+ fprintf(stderr, "Bad volume ID: "
+ "\"%s\"\n", optarg);
+ goto out;
+ }
+ break;
+ case ':':
+ fprintf(stderr, "Parameter is missing\n");
+ goto out;
+ case '?': /* help */
+ fprintf(stderr,
+ "Usage: ubirmvol [OPTION...]\n");
+ fprintf(stderr, "%s", doc);
+ fprintf(stderr, "%s", optionsstr);
+ fprintf(stderr, "\nReport bugs to %s\n",
+ PACKAGE_BUGREPORT);
+ exit(0);
+ break;
+ case 'V':
+ fprintf(stderr, "%s\n", PROGRAM_VERSION);
+ exit(0);
+ break;
+ default:
+ fprintf(stderr, "%s", usage);
+ exit(-1);
+ }
+ }
+
+ return 0;
+ out:
+ return -1;
+}
+
+static int param_sanity_check(struct args *args, libubi_t libubi)
+{
+ int err;
+ struct ubi_info ubi;
+
+ if (args->vol_id == -1) {
+ fprintf(stderr, "Volume ID was not specified\n");
+ goto out;
+ }
+
+ err = ubi_get_info(libubi, &ubi);
+ if (err)
+ return -1;
+
+ if (args->devn >= (int)ubi.dev_count) {
+ fprintf(stderr, "Device %d does not exist\n", args->devn);
+ goto out;
+ }
+
+ return 0;
+
+out:
+ errno = EINVAL;
+ return -1;
+}
+
+int main(int argc, char * const argv[])
+{
+ int err, old_errno;
+ libubi_t libubi;
+
+ err = parse_opt(argc, (char **)argv, &myargs);
+ if (err)
+ return err == 1 ? 0 : -1;
+
+ if (myargs.devn == -1) {
+ fprintf(stderr, "Device number was not specified\n");
+ fprintf(stderr, "Use -h option for help\n");
+ return -1;
+ }
+
+ libubi = libubi_open();
+ if (libubi == NULL) {
+ perror("Cannot open libubi");
+ return -1;
+ }
+
+ err = param_sanity_check(&myargs, libubi);
+ if (err) {
+ perror("Input parameters check");
+ fprintf(stderr, "Use -h option for help\n");
+ goto out_libubi;
+ }
+
+ err = ubi_rmvol(libubi, myargs.node, myargs.vol_id);
+ old_errno = errno;
+ if (err < 0) {
+ perror("Cannot remove volume");
+ fprintf(stderr, " err=%d errno=%d\n", err, old_errno);
+ goto out_libubi;
+ }
+
+ libubi_close(libubi);
+ return 0;
+
+out_libubi:
+ libubi_close(libubi);
+ return -1;
+}
diff --git a/ubi-utils/src/ubiupdatevol.c b/ubi-utils/src/ubiupdatevol.c
new file mode 100644
index 0000000..5401eb1
--- /dev/null
+++ b/ubi-utils/src/ubiupdatevol.c
@@ -0,0 +1,337 @@
+/*
+ * 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.
+ */
+
+/*
+ * An utility to update UBI volumes.
+ *
+ * Author: Frank Haverkamp
+ * Joshua W. Boyer
+ *
+ * 1.0 Reworked the userinterface to use argp.
+ * 1.1 Removed argp parsing because we want to use uClib.
+ * 1.2 Minor cleanups
+ * 1.3 Use a different libubi
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <getopt.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#include <config.h>
+#include <libubi.h>
+
+#define PROGRAM_VERSION "1.3"
+
+#define MAXPATH 1024
+#define BUFSIZE 128 * 1024
+#define MIN(x,y) ((x)<(y)?(x):(y))
+
+struct args {
+ int devn;
+ int vol_id;
+ int truncate;
+ int broken_update;
+ int bufsize;
+
+ /* special stuff needed to get additional arguments */
+ char *arg1;
+ char **options; /* [STRING...] */
+};
+
+static struct args myargs = {
+ .devn = -1,
+ .vol_id = -1,
+ .truncate = 0,
+ .broken_update = 0,
+ .bufsize = BUFSIZE,
+ .arg1 = NULL,
+ .options = NULL,
+};
+
+static int verbose = 0;
+
+static char doc[] = "\nVersion: " PROGRAM_VERSION "\n\t"
+ BUILD_OS" "BUILD_CPU" at "__DATE__" "__TIME__"\n"
+ "\nWrite to UBI Volume.\n";
+
+static const char *optionsstr =
+" -B, --broken-update broken update, this is for testing\n"
+" -d, --devn=<devn> UBI device\n"
+" -n, --vol_id=<volume id> UBI volume id\n"
+" -t, --truncate truncate volume\n"
+" -?, --help Give this help list\n"
+" --usage Give a short usage message\n"
+" -V, --version Print program version\n";
+
+static const char *usage =
+"Usage: ubiupdatevol [-Bt?V] [-d <devn>] [-n <volume id>] [--broken-update]\n"
+" [--devn=<devn>] [--vol_id=<volume id>] [--truncate] [--help]\n"
+" [--usage] [--version] <image file>\n";
+
+struct option long_options[] = {
+ { .name = "broken-update", .has_arg = 0, .flag = NULL, .val = 'B' },
+ { .name = "devn", .has_arg = 1, .flag = NULL, .val = 'd' },
+ { .name = "vol_id", .has_arg = 1, .flag = NULL, .val = 'n' },
+ { .name = "truncate", .has_arg = 0, .flag = NULL, .val = 't' },
+ { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' },
+ { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 },
+ { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' },
+ { NULL, 0, NULL, 0}
+};
+
+/*
+ * @brief Parse the arguments passed into the test case.
+ */
+static int
+parse_opt(int argc, char **argv, struct args *args)
+{
+ while (1) {
+ int key;
+
+ key = getopt_long(argc, argv, "Bd:n:t?V", long_options, NULL);
+ if (key == -1)
+ break;
+
+ switch (key) {
+ case 'v': /* --verbose=<level> */
+ verbose = strtoul(optarg, (char **)NULL, 0);
+ break;
+
+ case 'n': /* --vol_id=<volume id> */
+ args->vol_id = strtol(optarg, (char **)NULL, 0);
+ break;
+
+ case 'd': /* --devn=<device number> */
+ args->devn = strtol(optarg, (char **)NULL, 0);
+ break;
+
+ case 'b': /* --bufsize=<bufsize> */
+ args->bufsize = strtol(optarg, (char **)NULL, 0);
+ if (args->bufsize <= 0)
+ args->bufsize = BUFSIZE;
+ break;
+
+ case 't': /* --truncate */
+ args->truncate = 1;
+ break;
+
+ case 'B': /* --broken-update */
+ args->broken_update = 1;
+ break;
+
+ case '?': /* help */
+ fprintf(stderr, "Usage: "
+ "ubiupdatevol [OPTION...] <image file>\n%s%s"
+ "\nReport bugs to %s\n",
+ doc, optionsstr, PACKAGE_BUGREPORT);
+ exit(EXIT_SUCCESS);
+ break;
+
+ case 'V':
+ fprintf(stderr, "%s\n", PROGRAM_VERSION);
+ exit(0);
+ break;
+
+ default:
+ fprintf(stderr, "%s", usage);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (optind < argc) {
+ /* only one additional argument required */
+ args->arg1 = argv[optind++];
+ }
+ return 0;
+}
+
+/**
+ * @bytes bytes must be always 0, if not 0 this is a testcase for a
+ * broken volume update where data is promissed to be written, but for
+ * some reason nothing is written. The volume is unusable after this.
+ */
+static int
+ubi_truncate_volume(struct args *args, int64_t bytes,libubi_t libubi)
+{
+ int rc, ofd;
+ char path[MAXPATH];
+ int old_errno;
+
+ snprintf(path, MAXPATH-1, "/dev/ubi%d_%d", args->devn, args->vol_id);
+ path[MAXPATH-1] = '\0';
+
+ ofd = open(path, O_RDWR);
+ if (ofd < 0) {
+ fprintf(stderr, "Cannot open volume %s\n", path);
+ exit(EXIT_FAILURE);
+ }
+ rc = ubi_update_start(libubi, ofd, bytes);
+ old_errno = errno;
+ if (rc < 0) {
+ perror("UBI volume update ioctl");
+ fprintf(stderr, " rc=%d errno=%d\n", rc, old_errno);
+ exit(EXIT_FAILURE);
+ }
+ close(ofd);
+ return 0;
+}
+
+static ssize_t ubi_write(int fd, const void *buf, size_t count)
+{
+ int rc;
+ int len = count;
+
+ while (len) {
+ rc = write(fd, buf, len);
+ if (rc == -1) {
+ if (errno == EINTR)
+ continue; /* try again */
+ perror("write error");
+ return rc;
+ }
+
+ len -= rc;
+ buf += rc;
+ }
+ return count;
+}
+
+static int
+ubi_update_volume(struct args *args, libubi_t libubi)
+{
+ int rc, ofd;
+ FILE *ifp = NULL;
+ struct stat st;
+ int size = 0;
+ char *fname = args->arg1;
+ char path[MAXPATH];
+ char *buf;
+ int64_t bytes = 0;
+ int old_errno;
+
+ buf = malloc(args->bufsize);
+ if (!buf) {
+ perror("Out of memory");
+ exit(EXIT_FAILURE);
+ }
+
+ if (fname == NULL) {
+ fprintf(stderr, "Please specify an existing image file.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ rc = stat(fname, &st);
+ if (rc < 0) {
+ fprintf(stderr, "Cannot stat input file %s\n", fname);
+ exit(EXIT_FAILURE);
+ }
+ bytes = size = st.st_size;
+
+ ifp = fopen(fname, "r");
+ if (!ifp)
+ exit(EXIT_FAILURE);
+
+ snprintf(path, MAXPATH-1, "/dev/ubi%d_%d", args->devn, args->vol_id);
+ path[MAXPATH-1] = '\0';
+
+ ofd = open(path, O_RDWR);
+ if (ofd < 0) {
+ fprintf(stderr, "Cannot open UBI volume %s\n", path);
+ exit(EXIT_FAILURE);
+ }
+
+ rc = ubi_update_start(libubi, ofd, bytes);
+ old_errno = errno;
+ if (rc < 0) {
+ perror("UBI volume update ioctl");
+ fprintf(stderr, " rc=%d errno=%d\n", rc, old_errno);
+ exit(EXIT_FAILURE);
+ }
+
+ while (size > 0) {
+ ssize_t tocopy = MIN(args->bufsize, size);
+
+ rc = fread(buf, tocopy, 1, ifp);
+ if (rc != 1) {
+ perror("Could not read everything.");
+ exit(EXIT_FAILURE);
+ }
+
+ rc = ubi_write(ofd, buf, tocopy);
+ old_errno = errno;
+ if (rc != tocopy) {
+ perror("Could not write to device");
+ fprintf(stderr, " rc=%d errno=%d\n", rc, old_errno);
+ exit(EXIT_FAILURE);
+ }
+ size -= tocopy;
+ }
+
+ free(buf);
+ fclose(ifp);
+ rc = close(ofd);
+ if (rc != 0) {
+ perror("UBI volume close failed");
+ exit(EXIT_FAILURE);
+ }
+ return 0;
+}
+
+int
+main(int argc, char *argv[])
+{
+ int rc;
+ libubi_t libubi;
+
+ parse_opt(argc, argv, &myargs);
+
+ libubi = libubi_open();
+ if (libubi == NULL) {
+ perror("Cannot open libubi");
+ return -1;
+ }
+
+ if (myargs.truncate) {
+ rc = ubi_truncate_volume(&myargs, 0LL, libubi);
+ if (rc < 0)
+ goto out_libubi;
+ }
+ else if (myargs.broken_update) {
+ rc = ubi_truncate_volume(&myargs, 1LL, libubi);
+ if (rc < 0)
+ goto out_libubi;
+ } else {
+ rc = ubi_update_volume(&myargs, libubi);
+ if (rc < 0)
+ goto out_libubi;
+ }
+
+ libubi_close(libubi);
+ return 0;
+
+out_libubi:
+ libubi_close(libubi);
+ return -1;
+}
diff --git a/ubi-utils/src/unubi.c b/ubi-utils/src/unubi.c
new file mode 100644
index 0000000..811a6db
--- /dev/null
+++ b/ubi-utils/src/unubi.c
@@ -0,0 +1,837 @@
+/*
+ * 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.
+ */
+
+/*
+ * Authors: Frank Haverkamp, haver@vnet.ibm.com
+ * Drake Dowsett, dowsett@de.ibm.com
+ *
+ * 1.2 Removed argp because we want to use uClibc.
+ * 1.3 Minor cleanups
+ */
+
+/*
+ * unubi reads an image file containing blocks of UBI headers and data
+ * (such as produced from nand2bin) and rebuilds the volumes within. The
+ * default operation (when no flags are given) is to rebuild all valid
+ * volumes found in the image. unubi can also read straight from the
+ * onboard MTD device (ex. /dev/mtdblock/NAND).
+ */
+
+/* TODO: consideration for dynamic vs. static volumes */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <getopt.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <mtd/ubi-header.h>
+
+#include "crc32.h"
+#include "eb_chain.h"
+#include "unubi_analyze.h"
+
+#define EXEC "unubi"
+#define CONTACT "haver@vnet.ibm.com"
+#define VERSION "1.3"
+
+static char doc[] = "\nVersion: " VERSION "\n\t"
+ BUILD_OS" "BUILD_CPU" at "__DATE__" "__TIME__"\n"
+ "\nAnalyze raw flash containing UBI data.\n";
+
+static const char *optionsstr =
+" OPTIONS\n"
+" -r, --rebuild=<volume-id> Extract and rebuild volume\n"
+"\n"
+" -d, --dir=<output-dir> Specify output directory\n"
+"\n"
+" -a, --analyze Analyze image\n"
+"\n"
+" -b, --blocksize=<block-size> Specify size of eraseblocks in image in bytes\n"
+" (default 128KiB)\n"
+"\n"
+" -e, --eb-split Generate individual eraseblock images (all\n"
+" eraseblocks)\n"
+" -v, --vol-split Generate individual eraseblock images (valid\n"
+" eraseblocks only)\n"
+" -V, --vol-split! Raw split by eraseblock (valid eraseblocks only)\n"
+"\n"
+" -?, --help Give this help list\n"
+" --usage Give a short usage message\n"
+" --version Print program version\n";
+
+static const char *usage =
+"Usage: unubi [-aevV?] [-r <volume-id>] [-d <output-dir>] [-b <block-size>]\n"
+" [--rebuild=<volume-id>] [--dir=<output-dir>] [--analyze]\n"
+" [--blocksize=<block-size>] [--eb-split] [--vol-split]\n"
+" [--vol-split!] [--help] [--usage] [--version] image-file\n";
+
+#define ERR_MSG(fmt...) \
+ fprintf(stderr, EXEC ": " fmt)
+
+#define SPLIT_DATA 1
+#define SPLIT_RAW 2
+
+#define DIR_FMT "unubi_%s"
+#define KIB 1024
+#define MIB (KIB * KIB)
+#define MAXPATH KIB
+
+/* filenames */
+#define FN_INVAL "%s/eb%04u%s" /* invalid eraseblock */
+#define FN_NSURE "%s/eb%04u_%03u_%03u_%03x%s" /* unsure eraseblock */
+#define FN_VALID "%s/eb%04u_%03u_%03u_%03x%s" /* valid eraseblock */
+#define FN_VOLSP "%s/vol%03u_%03u_%03u_%04zu" /* split volume */
+#define FN_VOLWH "%s/volume%03u" /* whole volume */
+
+static uint32_t crc32_table[256];
+
+/* struct args:
+ * bsize int, blocksize of image blocks
+ * analyze flag, when non-zero produce analysis
+ * eb_split flag, when non-zero output eb####
+ * note: SPLIT_DATA vs. SPLIT_RAW
+ * vol_split flag, when non-zero output vol###_####
+ * note: SPLIT_DATA vs. SPLIT_RAW
+ * odir_path string, directory to place volumes in
+ * img_path string, file to read as ubi image
+ * vols int array of size UBI_MAX_VOLUMES, where a 1 can be
+ * written for each --rebuild flag in the index specified
+ * then the array can be counted and collapsed using
+ * count_set() and collapse()
+ */
+struct args {
+ uint32_t bsize;
+ int analyze;
+ int eb_split;
+ int vol_split;
+ char *odir_path;
+ char *img_path;
+ uint32_t *vols;
+
+ char **options;
+};
+
+struct option long_options[] = {
+ { .name = "rebuild", .has_arg = 1, .flag = NULL, .val = 'r' },
+ { .name = "dir", .has_arg = 1, .flag = NULL, .val = 'd' },
+ { .name = "analyze", .has_arg = 0, .flag = NULL, .val = 'a' },
+ { .name = "blocksize", .has_arg = 1, .flag = NULL, .val = 'b' },
+ { .name = "eb-split", .has_arg = 0, .flag = NULL, .val = 'e' },
+ { .name = "vol-split", .has_arg = 0, .flag = NULL, .val = 'v' },
+ { .name = "vol-split!", .has_arg = 0, .flag = NULL, .val = 'e' },
+ { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' },
+ { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 },
+ { .name = "version", .has_arg = 0, .flag = NULL, .val = 'J' },
+ { NULL, 0, NULL, 0}
+};
+
+/**
+ * parses out a numerical value from a string of numbers followed by:
+ * k, K, kib, KiB for kibibyte
+ * m, M, mib, MiB for mebibyte
+ **/
+static uint32_t
+str_to_num(char *str)
+{
+ char *s;
+ ulong num;
+
+ s = str;
+ num = strtoul(s, &s, 0);
+
+ if (*s != '\0') {
+ if ((strcmp(s, "KiB") == 0) || (strcmp(s, "K") == 0) ||
+ (strcmp(s, "kib") == 0) || (strcmp(s, "k") == 0))
+ num *= KIB;
+ if ((strcmp(s, "MiB") == 0) || (strcmp(s, "M") == 0) ||
+ (strcmp(s, "mib") == 0) || (strcmp(s, "m") == 0))
+ num *= MIB;
+ else
+ ERR_MSG("couldn't parse '%s', assuming %lu\n",
+ s, num);
+ }
+ return num;
+}
+
+static int
+parse_opt(int argc, char **argv, struct args *args)
+{
+ uint32_t i;
+
+ while (1) {
+ int key;
+
+ key = getopt_long(argc, argv, "r:d:ab:evV?", long_options, NULL);
+ if (key == -1)
+ break;
+
+ switch (key) {
+ case 'a':
+ args->analyze = 1;
+ break;
+ case 'b':
+ args->bsize = str_to_num(optarg);
+ break;
+ case 'd':
+ args->odir_path = optarg;
+ break;
+ case 'e':
+ args->eb_split = SPLIT_RAW;
+ break;
+ case 'r':
+ i = str_to_num(optarg);
+ if (i < UBI_MAX_VOLUMES)
+ args->vols[str_to_num(optarg)] = 1;
+ else {
+ ERR_MSG("volume-id out of bounds\n");
+ return -1;
+ }
+ break;
+ case 'v':
+ if (args->vol_split != SPLIT_RAW)
+ args->vol_split = SPLIT_DATA;
+ break;
+ case 'V':
+ args->vol_split = SPLIT_RAW;
+ break;
+ case '?': /* help */
+ fprintf(stderr,
+ "Usage: unubi [OPTION...] "
+ "image-file\n");
+ fprintf(stderr, "%s", doc);
+ fprintf(stderr, "%s", optionsstr);
+ fprintf(stderr,
+ "\nReport bugs to %s\n", CONTACT);
+ exit(0);
+ break;
+ case 'J':
+ fprintf(stderr, "%s\n", VERSION);
+ exit(0);
+ break;
+ default:
+ fprintf(stderr, "%s", usage);
+ exit(-1);
+ }
+ }
+
+ if (optind < argc)
+ args->img_path = argv[optind++];
+ return 0;
+}
+
+
+/**
+ * counts the number of indicies which are flagged in full_array;
+ * full_array is an array of flags (1/0);
+ **/
+static size_t
+count_set(uint32_t *full_array, size_t full_len)
+{
+ size_t count, i;
+
+ if (full_array == NULL)
+ return 0;
+
+ for (i = 0, count = 0; i < full_len; i++)
+ if (full_array[i] != 0)
+ count++;
+
+ return count;
+}
+
+
+/**
+ * generates coll_array from full_array;
+ * full_array is an array of flags (1/0);
+ * coll_array is an array of the indicies in full_array which are flagged (1);
+ **/
+static size_t
+collapse(uint32_t *full_array, size_t full_len,
+ uint32_t *coll_array, size_t coll_len)
+{
+ size_t i, j;
+
+ if ((full_array == NULL) || (coll_array == NULL))
+ return 0;
+
+ for (i = 0, j = 0; (i < full_len) && (j < coll_len); i++)
+ if (full_array[i] != 0) {
+ coll_array[j] = i;
+ j++;
+ }
+
+ return j;
+}
+
+
+/**
+ * header_crc: calculate the crc of EITHER a eb_hdr or vid_hdr
+ * one of the first to args MUST be NULL, the other is the header
+ * to caculate the crc on
+ * always returns 0
+ **/
+static int
+header_crc(struct ubi_ec_hdr *ebh, struct ubi_vid_hdr *vidh, uint32_t *ret_crc)
+{
+ uint32_t crc = UBI_CRC32_INIT;
+
+ if (ret_crc == NULL)
+ return 0;
+
+ if ((ebh != NULL) && (vidh == NULL))
+ crc = clc_crc32(crc32_table, crc, ebh, UBI_EC_HDR_SIZE_CRC);
+ else if ((ebh == NULL) && (vidh != NULL))
+ crc = clc_crc32(crc32_table, crc, vidh, UBI_VID_HDR_SIZE_CRC);
+ else
+ return 0;
+
+ *ret_crc = crc;
+ return 0;
+}
+
+
+/**
+ * data_crc: save the FILE* position, calculate the crc over a span,
+ * reset the position
+ * returns non-zero when EOF encountered
+ **/
+static int
+data_crc(FILE* fpin, size_t length, uint32_t *ret_crc)
+{
+ int rc;
+ size_t i;
+ char buf[length];
+ uint32_t crc;
+ fpos_t start;
+
+ rc = fgetpos(fpin, &start);
+ if (rc < 0)
+ return -1;
+
+ for (i = 0; i < length; i++) {
+ int c = fgetc(fpin);
+ if (c == EOF) {
+ ERR_MSG("unexpected EOF\n");
+ return -1;
+ }
+ buf[i] = (char)c;
+ }
+
+ rc = fsetpos(fpin, &start);
+ if (rc < 0)
+ return -1;
+
+ crc = clc_crc32(crc32_table, UBI_CRC32_INIT, buf, length);
+ *ret_crc = crc;
+ return 0;
+}
+
+
+/**
+ * reads data of size len from fpin and writes it to path
+ **/
+static int
+extract_data(FILE* fpin, size_t len, const char *path)
+{
+ int rc;
+ size_t i;
+ FILE* fpout;
+
+ rc = 0;
+ fpout = NULL;
+
+ fpout = fopen(path, "wb");
+ if (fpout == NULL) {
+ ERR_MSG("couldn't open file for writing: %s\n", path);
+ rc = -1;
+ goto err;
+ }
+
+ for (i = 0; i < len; i++) {
+ int c = fgetc(fpin);
+ if (c == EOF) {
+ ERR_MSG("unexpected EOF while writing: %s\n", path);
+ rc = -2;
+ goto err;
+ }
+ c = fputc(c, fpout);
+ if (c == EOF) {
+ ERR_MSG("couldn't write: %s\n", path);
+ rc = -3;
+ goto err;
+ }
+ }
+
+ err:
+ if (fpout != NULL)
+ fclose(fpout);
+ return rc;
+}
+
+
+/**
+ * using eb chain, tries to rebuild the data of volume at vol_id, or for all
+ * the known volumes, if vol_id is NULL;
+ **/
+static int
+rebuild_volume(FILE* fpin, uint32_t *vol_id, eb_info_t *head, const char* path)
+{
+ char filename[MAXPATH];
+ int rc;
+ uint32_t vol, num;
+ FILE* fpout;
+ eb_info_t cur;
+
+ rc = 0;
+
+ if ((fpin == NULL) || (head == NULL) || (*head == NULL))
+ return 0;
+
+ /* when vol_id is null, then do all */
+ if (vol_id == NULL) {
+ cur = *head;
+ vol = ubi32_to_cpu(cur->inner.vol_id);
+ }
+ else {
+ vol = *vol_id;
+ eb_chain_position(head, vol, NULL, &cur);
+ if (cur == NULL) {
+ ERR_MSG("no valid volume %d was found\n", vol);
+ return -1;
+ }
+ }
+
+ num = 0;
+ snprintf(filename, MAXPATH, FN_VOLWH, path, vol);
+ fpout = fopen(filename, "wb");
+ if (fpout == NULL) {
+ ERR_MSG("couldn't open file for writing: %s\n", filename);
+ return -1;
+ }
+
+ while (cur != NULL) {
+ size_t i;
+
+ if (ubi32_to_cpu(cur->inner.vol_id) != vol) {
+ /* close out file */
+ fclose(fpout);
+
+ /* only stay around if that was the only volume */
+ if (vol_id != NULL)
+ goto out;
+
+ /* begin with next */
+ vol = ubi32_to_cpu(cur->inner.vol_id);
+ num = 0;
+ snprintf(filename, MAXPATH, FN_VOLWH, path, vol);
+ fpout = fopen(filename, "wb");
+ if (fpout == NULL) {
+ ERR_MSG("couldn't open file for writing: %s\n",
+ filename);
+ return -1;
+ }
+ }
+
+ if (ubi32_to_cpu(cur->inner.lnum) != num) {
+ ERR_MSG("missing valid block %d for volume %d\n",
+ num, vol);
+ }
+
+ rc = fsetpos(fpin, &(cur->eb_top));
+ if (rc < 0)
+ goto out;
+ fseek(fpin, ubi32_to_cpu(cur->outer.data_offset), SEEK_CUR);
+
+ for (i = 0; i < ubi32_to_cpu(cur->inner.data_size); i++) {
+ int c = fgetc(fpin);
+ if (c == EOF) {
+ ERR_MSG("unexpected EOF while writing: %s\n",
+ filename);
+ rc = -2;
+ goto out;
+ }
+ c = fputc(c, fpout);
+ if (c == EOF) {
+ ERR_MSG("couldn't write: %s\n", filename);
+ rc = -3;
+ goto out;
+ }
+ }
+
+ cur = cur->next;
+ num++;
+ }
+
+ out:
+ if (vol_id == NULL)
+ fclose(fpout);
+ return rc;
+}
+
+
+/**
+ * traverses FILE* trying to load complete, valid and accurate header data
+ * into the eb chain;
+ **/
+static int
+unubi_volumes(FILE* fpin, uint32_t *vols, size_t vc, struct args *a)
+{
+ char filename[MAXPATH + 1];
+ char reason[MAXPATH + 1];
+ int rc;
+ size_t i, count;
+ /* relations:
+ * cur ~ head
+ * next ~ first */
+ eb_info_t head, cur, first, next;
+ eb_info_t *next_ptr;
+
+ rc = 0;
+ count = 0;
+ head = NULL;
+ first = NULL;
+ next = NULL;
+ cur = malloc(sizeof(*cur));
+ if (cur == NULL) {
+ ERR_MSG("out of memory\n");
+ rc = -ENOMEM;
+ goto err;
+ }
+ memset(cur, 0, sizeof(*cur));
+
+ fgetpos(fpin, &(cur->eb_top));
+ while (1) {
+ const char *raw_path;
+ uint32_t crc;
+
+ memset(filename, 0, MAXPATH + 1);
+ memset(reason, 0, MAXPATH + 1);
+
+ /* in case of an incomplete ec header */
+ raw_path = FN_INVAL;
+
+ /* read erasecounter header */
+ rc = fread(&cur->outer, 1, sizeof(cur->outer), fpin);
+ if (rc == 0)
+ goto out; /* EOF */
+ if (rc != sizeof(cur->outer)) {
+ ERR_MSG("reading ec-hdr failed rc=%d\n", rc);
+ rc = -1;
+ goto err;
+ }
+
+ /* check erasecounter header magic */
+ if (ubi32_to_cpu(cur->outer.magic) != UBI_EC_HDR_MAGIC) {
+ snprintf(reason, MAXPATH, ".invalid.ec_magic");
+ goto invalid;
+ }
+
+ /* check erasecounter header crc */
+ header_crc(&(cur->outer), NULL, &crc);
+
+ if (ubi32_to_cpu(cur->outer.hdr_crc) != crc) {
+ snprintf(reason, MAXPATH, ".invalid.ec_hdr_crc");
+ goto invalid;
+ }
+
+ /* read volume id header */
+ rc = fsetpos(fpin, &(cur->eb_top));
+ if (rc != 0)
+ goto err;
+ fseek(fpin, ubi32_to_cpu(cur->outer.vid_hdr_offset), SEEK_CUR);
+
+ /* read erasecounter header */
+ rc = fread(&cur->inner, 1, sizeof(cur->inner), fpin);
+ if (rc == 0)
+ goto out; /* EOF */
+ if (rc != sizeof(cur->inner)) {
+ ERR_MSG("reading vid-hdr failed rc=%d\n", rc);
+ rc = -1;
+ goto err;
+ }
+
+ /* empty? */
+ if (ubi32_to_cpu(cur->inner.magic) == 0xffffffff) {
+ snprintf(reason, MAXPATH, ".empty");
+ goto invalid;
+ }
+
+ /* vol_id should be in bounds */
+ if ((ubi32_to_cpu(cur->inner.vol_id) >= UBI_MAX_VOLUMES) &&
+ (ubi32_to_cpu(cur->inner.vol_id) <
+ UBI_INTERNAL_VOL_START)) {
+ snprintf(reason, MAXPATH, ".invalid");
+ goto invalid;
+ } else
+ raw_path = FN_NSURE;
+
+ /* check volume id header magic */
+ if (ubi32_to_cpu(cur->inner.magic) != UBI_VID_HDR_MAGIC) {
+ snprintf(reason, MAXPATH, ".invalid.vid_magic");
+ goto invalid;
+ }
+
+ /* check volume id header crc */
+ header_crc(NULL, &(cur->inner), &crc);
+ if (ubi32_to_cpu(cur->inner.hdr_crc) != crc) {
+ snprintf(reason, MAXPATH, ".invalid.vid_hdr_crc");
+ goto invalid;
+ }
+
+ /* check data crc */
+ rc = data_crc(fpin, ubi32_to_cpu(cur->inner.data_size), &crc);
+ if (rc < 0)
+ goto err;
+ if (ubi32_to_cpu(cur->inner.data_crc) != crc) {
+ snprintf(reason, MAXPATH, ".invalid.data_crc");
+ goto invalid;
+ }
+
+ /* enlist this vol, it's valid */
+ raw_path = FN_VALID;
+ cur->linear = count;
+ rc = eb_chain_insert(&head, cur);
+ if (rc < 0) {
+ if (rc == -ENOMEM) {
+ ERR_MSG("out of memory\n");
+ goto err;
+ }
+ ERR_MSG("unknown and unexpected error, please contact "
+ CONTACT "\n");
+ goto err;
+ }
+
+ if (a->vol_split) {
+ size_t size = 0;
+
+ rc = fsetpos(fpin, &(cur->eb_top));
+ if (rc != 0)
+ goto err;
+
+ if (a->vol_split == SPLIT_DATA) {
+ /* write only data section */
+ size = ubi32_to_cpu(cur->inner.data_size);
+ fseek(fpin,
+ ubi32_to_cpu(cur->outer.data_offset),
+ SEEK_CUR);
+ }
+ else if (a->vol_split == SPLIT_RAW)
+ /* write entire eraseblock */
+ size = a->bsize;
+
+ snprintf(filename, MAXPATH, FN_VOLSP,
+ a->odir_path,
+ ubi32_to_cpu(cur->inner.vol_id),
+ ubi32_to_cpu(cur->inner.lnum),
+ ubi32_to_cpu(cur->inner.leb_ver), count);
+ rc = extract_data(fpin, size, filename);
+ if (rc < 0)
+ goto err;
+ }
+
+ invalid:
+ if (a->eb_split) {
+ /* jump to top of block */
+ rc = fsetpos(fpin, &(cur->eb_top));
+ if (rc != 0)
+ goto err;
+
+ if (strcmp(raw_path, FN_INVAL) == 0)
+ snprintf(filename, MAXPATH, raw_path,
+ a->odir_path, count, reason);
+ else
+ snprintf(filename, MAXPATH, raw_path,
+ a->odir_path,
+ count,
+ ubi32_to_cpu(cur->inner.vol_id),
+ ubi32_to_cpu(cur->inner.lnum),
+ ubi32_to_cpu(cur->inner.leb_ver),
+ reason);
+
+ rc = extract_data(fpin, a->bsize, filename);
+ if (rc < 0)
+ goto err;
+ }
+
+ /* append to simple linked list */
+ if (first == NULL)
+ next_ptr = &first;
+ else
+ next_ptr = &next->next;
+
+ *next_ptr = malloc(sizeof(**next_ptr));
+ if (*next_ptr == NULL) {
+ ERR_MSG("out of memory\n");
+ rc = -ENOMEM;
+ goto err;
+ }
+ memset(*next_ptr, 0, sizeof(**next_ptr));
+
+ next = *next_ptr;
+ memcpy(next, cur, sizeof(*next));
+ next->next = NULL;
+
+ count++;
+ rc = fsetpos(fpin, &(cur->eb_top));
+ if (rc != 0)
+ goto err;
+ fseek(fpin, a->bsize, SEEK_CUR);
+ memset(cur, 0, sizeof(*cur));
+
+ fgetpos(fpin, &(cur->eb_top));
+ }
+
+ out:
+ for (i = 0; i < vc; i++) {
+ rc = rebuild_volume(fpin, &vols[i], &head, a->odir_path);
+ if (rc < 0)
+ goto err;
+ }
+
+ /* if there were no volumes specified, rebuild them all,
+ * UNLESS eb_ or vol_ split or analyze was specified */
+ if ((vc == 0) && (!a->eb_split) && (!a->vol_split) && (!a->analyze)) {
+ rc = rebuild_volume(fpin, NULL, &head, a->odir_path);
+ if (rc < 0)
+ goto err;
+ }
+
+ err:
+ free(cur);
+
+ if (a->analyze)
+ unubi_analyze(&head, first, a->odir_path);
+ eb_chain_destroy(&head);
+ eb_chain_destroy(&first);
+
+ return rc;
+}
+
+
+/**
+ * handles command line arguments, then calls unubi_volumes
+ **/
+int
+main(int argc, char *argv[])
+{
+ int rc, free_a_odir;
+ size_t vols_len;
+ uint32_t *vols;
+ FILE* fpin;
+ struct args a;
+
+ rc = 0;
+ free_a_odir = 0;
+ vols_len = 0;
+ vols = NULL;
+ fpin = NULL;
+ init_crc32_table(crc32_table);
+
+ /* setup struct args a */
+ memset(&a, 0, sizeof(a));
+ a.bsize = 128 * KIB;
+ a.vols = malloc(sizeof(*a.vols) * UBI_MAX_VOLUMES);
+ if (a.vols == NULL) {
+ ERR_MSG("out of memory\n");
+ rc = ENOMEM;
+ goto err;
+ }
+ memset(a.vols, 0, sizeof(*a.vols) * UBI_MAX_VOLUMES);
+
+ /* parse args and check for validity */
+ parse_opt(argc, argv, &a);
+ if (a.img_path == NULL) {
+ ERR_MSG("no image file specified\n");
+ rc = EINVAL;
+ goto err;
+ }
+ else if (a.odir_path == NULL) {
+ char *ptr;
+ int len;
+
+ ptr = strrchr(a.img_path, '/');
+ if (ptr == NULL)
+ ptr = a.img_path;
+ else
+ ptr++;
+
+ len = strlen(DIR_FMT) + strlen(ptr);
+ free_a_odir = 1;
+ a.odir_path = malloc(sizeof(*a.odir_path) * len);
+ if (a.odir_path == NULL) {
+ ERR_MSG("out of memory\n");
+ rc = ENOMEM;
+ goto err;
+ }
+ snprintf(a.odir_path, len, DIR_FMT, ptr);
+ }
+
+ fpin = fopen(a.img_path, "rb");
+ if (fpin == NULL) {
+ ERR_MSG("couldn't open file for reading: "
+ "%s\n", a.img_path);
+ rc = EINVAL;
+ goto err;
+ }
+
+ rc = mkdir(a.odir_path, 0777);
+ if ((rc < 0) && (errno != EEXIST)) {
+ ERR_MSG("couldn't create ouput directory: "
+ "%s\n", a.odir_path);
+ rc = -rc;
+ goto err;
+ }
+
+ /* fill in vols array */
+ vols_len = count_set(a.vols, UBI_MAX_VOLUMES);
+ if (vols_len > 0) {
+ vols = malloc(sizeof(*vols) * vols_len);
+ if (vols == NULL) {
+ ERR_MSG("out of memory\n");
+ rc = ENOMEM;
+ goto err;
+ }
+ collapse(a.vols, UBI_MAX_VOLUMES, vols, vols_len);
+ }
+
+ /* unubi volumes */
+ rc = unubi_volumes(fpin, vols, vols_len, &a);
+ if (rc < 0) {
+ ERR_MSG("error encountered while working on image file: "
+ "%s\n", a.img_path);
+ rc = -rc;
+ goto err;
+ }
+
+ err:
+ free(a.vols);
+ if (free_a_odir != 0)
+ free(a.odir_path);
+ if (fpin != NULL)
+ fclose(fpin);
+ if (vols_len > 0)
+ free(vols);
+ return rc;
+}
diff --git a/ubi-utils/src/unubi_analyze.c b/ubi-utils/src/unubi_analyze.c
new file mode 100644
index 0000000..6009fc0
--- /dev/null
+++ b/ubi-utils/src/unubi_analyze.c
@@ -0,0 +1,435 @@
+/*
+ * 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.
+ */
+
+/*
+ * Authors: Drake Dowsett, dowsett@de.ibm.com
+ * Contact: Andreas Arnez, arnez@de.ibm.com
+ *
+ * unubi uses the following functions to generate analysis output based on
+ * the header information in a raw-UBI image
+ */
+
+/*
+ * TODO: use OOB data to check for eraseblock validity in NAND images
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "eb_chain.h"
+#include "crc32.h"
+
+#define MAXPATH 1024
+#define EC_X_INT 50
+
+#define FN_STATS "analysis_stats.txt"
+#define FN_EH_DATA "analysis_ec_hdr.data"
+#define FN_EH_PLOT "analysis_ec_hdr.plot"
+#define FN_VH_DATA "analysis_vid_hdr.data"
+#define FN_VH_PLOT "analysis_vid_hdr.plot"
+
+
+/**
+ * intcmp - function needed by qsort to order integers
+ **/
+int intcmp(const void *a, const void *b)
+{
+ int A = *(int *)a;
+ int B = *(int *)b;
+ return A - B;
+}
+
+int longcmp(const void *a, const void *b)
+{
+ long long A = *(long long *)a;
+ long long B = *(long long *)b;
+ return A - B;
+}
+
+
+/**
+ * unubi_analyze_group_index - finds the normalized index in an array
+ * item: look for this item in the array
+ * array: array to search through
+ * size: length of the array
+ * array should be sorted for this algorithm to perform properly;
+ * if the item is not found returns -1, otherwise return value is the
+ * index in the array (note this contricts the array size to 2^32-1);
+ **/
+int
+norm_index(uint32_t item, uint32_t *array, size_t length)
+{
+ size_t i, index;
+
+ for (index = 0, i = 0; i < length; i++) {
+ if ((i != 0) && (array[i] != array[i - 1]))
+ index++;
+
+ if (item == array[i])
+ return index;
+ }
+
+ return -1;
+}
+
+
+/**
+ * unubi_analyze_ec_hdr - generate data table and plot script
+ * first: head of simple linked list
+ * path: folder to write into
+ * generates a data file containing the eraseblock index in the image
+ * and the erase counter found in its ec header;
+ * if the crc check fails, the line is commented out in the data file;
+ * also generates a simple gnuplot sript for quickly viewing one
+ * display of the data file;
+ **/
+int
+unubi_analyze_ec_hdr(eb_info_t first, const char *path)
+{
+ char filename[MAXPATH + 1];
+ size_t count, eraseblocks;
+ uint32_t crc, crc32_table[256];
+ uint64_t *erase_counts;
+ FILE* fpdata;
+ FILE* fpplot;
+ eb_info_t cur;
+
+ /* crc check still needed for `first' linked list */
+ init_crc32_table(crc32_table);
+
+ /* prepare output files */
+ memset(filename, 0, MAXPATH + 1);
+ snprintf(filename, MAXPATH, "%s/%s", path, FN_EH_DATA);
+ fpdata = fopen(filename, "w");
+ if (fpdata == NULL)
+ return -1;
+
+ memset(filename, 0, MAXPATH + 1);
+ snprintf(filename, MAXPATH, "%s/%s", path, FN_EH_PLOT);
+ fpplot = fopen(filename, "w");
+ if (fpplot == NULL)
+ return -1;
+
+ chmod(filename, 0755);
+
+ /* first run: count elements */
+ count = 0;
+ cur = first;
+ while (cur != NULL) {
+ cur = cur->next;
+ count++;
+ }
+ eraseblocks = count;
+
+ erase_counts = malloc(eraseblocks * sizeof(*erase_counts));
+ memset(erase_counts, 0, eraseblocks * sizeof(*erase_counts));
+
+ /* second run: populate array to sort */
+ count = 0;
+ cur = first;
+ while(cur != NULL) {
+ erase_counts[count] = ubi64_to_cpu(cur->outer.ec);
+ cur = cur->next;
+ count++;
+ }
+ qsort(erase_counts, eraseblocks, sizeof(*erase_counts),
+ (void *)longcmp);
+
+ /* third run: generate data file */
+ count = 0;
+ cur = first;
+ fprintf(fpdata, "# eraseblock_no actual_erase_count "
+ "sorted_erase_count\n");
+ while (cur != NULL) {
+ crc = clc_crc32(crc32_table, UBI_CRC32_INIT, &cur->outer,
+ UBI_EC_HDR_SIZE_CRC);
+
+ if ((ubi32_to_cpu(cur->outer.magic) != UBI_EC_HDR_MAGIC) ||
+ (crc != ubi32_to_cpu(cur->outer.hdr_crc)))
+ fprintf(fpdata, "# ");
+
+ fprintf(fpdata, "%zu %llu %llu", count,
+ (unsigned long long) ubi64_to_cpu(cur->outer.ec),
+ (unsigned long long) erase_counts[count]);
+
+ if (ubi32_to_cpu(cur->outer.magic) != UBI_EC_HDR_MAGIC)
+ fprintf(fpdata, " ## bad magic: %08x",
+ ubi32_to_cpu(cur->outer.magic));
+
+ if (crc != ubi32_to_cpu(cur->outer.hdr_crc))
+ fprintf(fpdata, " ## CRC mismatch: given=%08x, "
+ "calc=%08x", ubi32_to_cpu(cur->outer.hdr_crc),
+ crc);
+
+ fprintf(fpdata, "\n");
+
+ cur = cur->next;
+ count++;
+ }
+ fclose(fpdata);
+
+ fprintf(fpplot, "#!/usr/bin/gnuplot -persist\n");
+ fprintf(fpplot, "set xlabel \"eraseblock\"\n");
+
+ /* fourth run: generate plot file xtics */
+ count = 0;
+ cur = first;
+ fprintf(fpplot, "set xtics (");
+ while (cur != NULL) {
+ if ((count % EC_X_INT) == 0) {
+ if (count > 0)
+ fprintf(fpplot, ", ");
+ fprintf(fpplot, "%zd", count);
+ }
+
+ cur = cur->next;
+ count++;
+ }
+ fprintf(fpplot, ")\n");
+
+ fprintf(fpplot, "set ylabel \"erase count\"\n");
+ fprintf(fpplot, "set xrange [-1:%zu]\n", eraseblocks + 1);
+ fprintf(fpplot, "# set yrange [-1:%llu]\n",
+ (unsigned long long) erase_counts[eraseblocks - 1] + 1);
+ fprintf(fpplot, "plot \"%s\" u 1:2 t \"unsorted: %s\" with boxes\n",
+ FN_EH_DATA, FN_EH_DATA);
+ fprintf(fpplot, "# replot \"%s\" u 1:3 t \"sorted: %s\" with lines\n",
+ FN_EH_DATA, FN_EH_DATA);
+ fprintf(fpplot, "pause -1 \"press ENTER\"\n");
+
+ fclose(fpplot);
+
+ return 0;
+}
+
+
+/**
+ * unubi_analyze_vid_hdr - generate data table and plot script
+ * head: head of complex linked list (eb_chain)
+ * path: folder to write into
+ * generates a data file containing the volume id, logical number, leb version,
+ * and data size from the vid header;
+ * all eraseblocks listed in the eb_chain are valid (checked in unubi);
+ * also generates a simple gnuplot sript for quickly viewing one
+ * display of the data file;
+ **/
+int
+unubi_analyze_vid_hdr(eb_info_t *head, const char *path)
+{
+ char filename[MAXPATH + 1];
+ int y1, y2;
+ size_t count, step, breadth;
+ uint32_t *leb_versions, *data_sizes;
+ FILE* fpdata;
+ FILE* fpplot;
+ eb_info_t cur;
+
+ /* prepare output files */
+ memset(filename, 0, MAXPATH + 1);
+ snprintf(filename, MAXPATH, "%s/%s", path, FN_VH_DATA);
+ fpdata = fopen(filename, "w");
+ if (fpdata == NULL)
+ return -1;
+
+ memset(filename, 0, MAXPATH + 1);
+ snprintf(filename, MAXPATH, "%s/%s", path, FN_VH_PLOT);
+ fpplot = fopen(filename, "w");
+ if (fpplot == NULL)
+ return -1;
+
+ chmod(filename, 0755);
+
+ /* first run: count elements */
+ count = 0;
+ cur = *head;
+ while (cur != NULL) {
+ cur = cur->next;
+ count++;
+ }
+ breadth = count;
+
+ leb_versions = malloc(breadth * sizeof(*leb_versions));
+ memset(leb_versions, 0, breadth * sizeof(*leb_versions));
+
+ data_sizes = malloc(breadth * sizeof(*data_sizes));
+ memset(data_sizes, 0, breadth * sizeof(*data_sizes));
+
+ /* second run: populate arrays to sort */
+ count = 0;
+ cur = *head;
+ while (cur != NULL) {
+ leb_versions[count] = ubi32_to_cpu(cur->inner.leb_ver);
+ data_sizes[count] = ubi32_to_cpu(cur->inner.data_size);
+ cur = cur->next;
+ count++;
+ }
+ qsort(leb_versions, breadth, sizeof(*leb_versions), (void *)intcmp);
+ qsort(data_sizes, breadth, sizeof(*data_sizes), (void *)intcmp);
+
+ /* third run: generate data file */
+ count = 0;
+ cur = *head;
+ fprintf(fpdata, "# x_axis vol_id lnum y1_axis leb_ver "
+ "y2_axis data_size\n");
+ while (cur != NULL) {
+ y1 = norm_index(ubi32_to_cpu(cur->inner.leb_ver), leb_versions,
+ breadth);
+ y2 = norm_index(ubi32_to_cpu(cur->inner.data_size), data_sizes,
+ breadth);
+
+ if ((y1 == -1) || (y2 == -1))
+ return -1;
+
+ fprintf(fpdata, "%zu %u %u %u %u %u %u\n",
+ count,
+ ubi32_to_cpu(cur->inner.vol_id),
+ ubi32_to_cpu(cur->inner.lnum),
+ y1,
+ ubi32_to_cpu(cur->inner.leb_ver),
+ y2,
+ ubi32_to_cpu(cur->inner.data_size));
+ cur = cur->next;
+ count++;
+ }
+ fclose(fpdata);
+
+ fprintf(fpplot, "#!/usr/bin/gnuplot -persist\n");
+ fprintf(fpplot, "set xlabel \"volume\"\n");
+
+ /* fourth run: generate plot file xtics */
+ count = 0;
+ step = 0;
+ cur = *head;
+ fprintf(fpplot, "set xtics (");
+ while (cur != NULL) {
+ if (count > 0)
+ fprintf(fpplot, ", ");
+ if (step != ubi32_to_cpu(cur->inner.vol_id)) {
+ step = ubi32_to_cpu(cur->inner.vol_id);
+ fprintf(fpplot, "\"%zd\" %zd 0", step, count);
+ }
+ else
+ fprintf(fpplot, "\"%d\" %zd 1",
+ ubi32_to_cpu(cur->inner.lnum), count);
+ cur = cur->next;
+ count++;
+ }
+ fprintf(fpplot, ")\n");
+ fprintf(fpplot, "set nox2tics\n");
+
+ /* fifth run: generate plot file ytics */
+ count = 0;
+ cur = *head;
+ fprintf(fpplot, "set ylabel \"leb version\"\n");
+ fprintf(fpplot, "set ytics (");
+ while (cur != NULL) {
+ y1 = norm_index(ubi32_to_cpu(cur->inner.leb_ver), leb_versions,
+ breadth);
+
+ if (y1 == -1)
+ return -1;
+
+ if (count > 0)
+ fprintf(fpplot, ", ");
+
+ fprintf(fpplot, "\"%u\" %u", ubi32_to_cpu(cur->inner.leb_ver),
+ y1);
+
+ cur = cur->next;
+ count++;
+ }
+ fprintf(fpplot, ")\n");
+
+ /* sixth run: generate plot file y2tics */
+ count = 0;
+ cur = *head;
+ fprintf(fpplot, "set y2label \"data size\"\n");
+ fprintf(fpplot, "set y2tics (");
+ while (cur != NULL) {
+ y2 = norm_index(ubi32_to_cpu(cur->inner.data_size),
+ data_sizes, breadth);
+
+ if (y2 == -1)
+ return -1;
+
+ if (count > 0)
+ fprintf(fpplot, ", ");
+
+ fprintf(fpplot, "\"%u\" %u", ubi32_to_cpu(cur->inner.data_size),
+ y2);
+
+ cur = cur->next;
+ count++;
+ }
+ fprintf(fpplot, ")\n");
+
+ y1 = norm_index(leb_versions[breadth - 1], leb_versions, breadth);
+ y2 = norm_index(data_sizes[breadth - 1], data_sizes, breadth);
+ fprintf(fpplot, "set xrange [-1:%zu]\n", count + 1);
+ fprintf(fpplot, "set yrange [-1:%u]\n", y1 + 1);
+ fprintf(fpplot, "set y2range [-1:%u]\n", y2 + 1);
+ fprintf(fpplot, "plot \"%s\" u 1:4 t \"leb version: %s\" "
+ "axes x1y1 with lp\n", FN_VH_DATA, FN_VH_DATA);
+ fprintf(fpplot, "replot \"%s\" u 1:6 t \"data size: %s\" "
+ "axes x1y2 with lp\n", FN_VH_DATA, FN_VH_DATA);
+ fprintf(fpplot, "pause -1 \"press ENTER\"\n");
+
+ fclose(fpplot);
+
+ free(data_sizes);
+ free(leb_versions);
+
+ return 0;
+}
+
+
+/**
+ * unubi_analyze - run all analyses
+ * head: eb_chain head
+ * first: simple linked list of eraseblock headers (use .next)
+ * path: directory (without trailing slash) to output to
+ * returns 0 upon successful completion, or -1 otherwise
+ **/
+int
+unubi_analyze(eb_info_t *head, eb_info_t first, const char *path)
+{
+ int rc;
+
+ if (path == NULL)
+ return -1;
+
+ if (first == NULL)
+ return -1;
+
+ if ((head == NULL) || (*head == NULL))
+ return -1;
+
+ rc = unubi_analyze_ec_hdr(first, path);
+ if (rc < 0)
+ return -1;
+
+ rc = unubi_analyze_vid_hdr(head, path);
+ if (rc < 0)
+ return -1;
+
+ return 0;
+}
diff --git a/ubi-utils/src/unubi_analyze.h b/ubi-utils/src/unubi_analyze.h
new file mode 100644
index 0000000..ac01a44
--- /dev/null
+++ b/ubi-utils/src/unubi_analyze.h
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+/*
+ * Authors: Drake Dowsett, dowsett@de.ibm.com
+ */
+
+#include "eb_chain.h"
+
+int
+unubi_analyze(eb_info_t *head, eb_info_t first, const char *path);
diff --git a/ubi-utils/testcases.txt b/ubi-utils/testcases.txt
new file mode 100644
index 0000000..dcc1c35
--- /dev/null
+++ b/ubi-utils/testcases.txt
@@ -0,0 +1,9 @@
+1. Start some large update, but write there byte-by-byte
+
+2. start update for N bytes, write N-x bytes, then write y bytes, y>x.
+ You have to see that at the last write only x bytes were written,
+ but y-x bytes were not. we may vary x a bit. good number would be
+ 1, 128, 128Ki-128...
+
+3. Try to start update for x bytes, write x bytes, then try to write more.
+ Check that it is impossible to write more.
diff --git a/ubi-utils/tests/Makefile b/ubi-utils/tests/Makefile
new file mode 100644
index 0000000..1b17f81
--- /dev/null
+++ b/ubi-utils/tests/Makefile
@@ -0,0 +1,40 @@
+
+INCLUDE1=../inc
+INCLUDE2=../../include
+LIB=.
+
+CC := $(CROSS)gcc
+
+ALL_FILES=libubi.a io_update
+ALL_FILES+=io_paral io_read io_basic mkvol_basic mkvol_bad mkvol_paral rsvol
+ALL_FILES+=integ
+
+CFLAGS += -Wall -I$(INCLUDE1) -I$(INCLUDE2) -L$(LIB) -ggdb
+
+all: $(ALL_FILES)
+
+libubi.a: ../src/libubi.c ../inc/libubi.h ../src/libubi_int.h
+ $(CC) $(CFLAGS) -c ../src/libubi.c -o libubi.o
+ ar cr libubi.a libubi.o
+
+io_paral: io_paral.c common.c
+ $(CC) $(CFLAGS) $^ -lubi -lpthread -o $@
+io_update: io_update.c common.c
+ $(CC) $(CFLAGS) $^ -lubi -o $@
+io_read: io_read.c common.c
+ $(CC) $(CFLAGS) $^ -lubi -o $@
+io_basic: io_basic.c common.c
+ $(CC) $(CFLAGS) $^ -lubi -o $@
+mkvol_basic: mkvol_basic.c common.c
+ $(CC) $(CFLAGS) $^ -lubi -o $@
+mkvol_bad: mkvol_bad.c common.c
+ $(CC) $(CFLAGS) $^ -lubi -o $@
+mkvol_paral: mkvol_paral.c common.c
+ $(CC) $(CFLAGS) $^ -lubi -lpthread -o $@
+rsvol: rsvol.c common.c
+ $(CC) $(CFLAGS) $^ -lubi -o $@
+integ: integ.c
+ $(CC) $(CFLAGS) $^ -lubi -o $@
+
+clean:
+ rm -rf $(ALL_FILES) $(addsuffix .o, $(ALL_FILES))
diff --git a/ubi-utils/tests/common.c b/ubi-utils/tests/common.c
new file mode 100644
index 0000000..cb63e77
--- /dev/null
+++ b/ubi-utils/tests/common.c
@@ -0,0 +1,336 @@
+/*
+ * 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.
+ *
+ * Author: Artem B. Bityutskiy
+ *
+ * The stuff which is common for many tests.
+ */
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "libubi.h"
+#include "common.h"
+
+/**
+ * __initial_check - check that common prerequisites which are required to run
+ * tests.
+ *
+ * @test test name
+ * @argc count of command-line arguments
+ * @argv command-line arguments
+ *
+ * This function returns %0 if all is fine and test may be run and %-1 if not.
+ */
+int __initial_check(const char *test, int argc, char * const argv[])
+{
+ libubi_t libubi;
+ struct ubi_dev_info dev_info;
+
+ /*
+ * All tests require UBI character device name as the first parameter,
+ * check this.
+ */
+ if (argc < 2) {
+ __err_msg(test, __FUNCTION__, __LINE__,
+ "UBI character device node is not specified");
+ return -1;
+ }
+
+ libubi = libubi_open();
+ if (libubi == NULL) {
+ __failed(test, __FUNCTION__, __LINE__, "libubi_open");
+ return -1;
+ }
+
+ if (ubi_get_dev_info(libubi, argv[1], &dev_info)) {
+ __failed(test, __FUNCTION__, __LINE__, "ubi_get_dev_info");
+ goto close;
+ }
+
+ if (dev_info.avail_ebs < MIN_AVAIL_EBS) {
+ __err_msg(test, __FUNCTION__, __LINE__,
+ "insufficient available eraseblocks %d on UBI "
+ "device, required %d",
+ dev_info.avail_ebs, MIN_AVAIL_EBS);
+ goto close;
+ }
+
+ if (dev_info.vol_count != 0) {
+ __err_msg(test, __FUNCTION__, __LINE__,
+ "device %s is not empty", argv[1]);
+ goto close;
+ }
+
+ libubi_close(libubi);
+ return 0;
+
+close:
+ libubi_close(libubi);
+ return -1;
+}
+
+/**
+ * __err_msg - print a message to stderr.
+ *
+ * @test test name
+ * @func function name
+ * @line line number
+ * @fmt format string
+ */
+void __err_msg(const char *test, const char *func, int line,
+ const char *fmt, ...)
+{
+ va_list args;
+
+ fprintf(stderr, "[%s] %s():%d: ", test, func, line);
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ fprintf(stderr, "\n");
+ va_end(args);
+}
+
+/**
+ * __failed - print function fail message.
+ *
+ * @test test name
+ * @func calling function name
+ * @line line number
+ * @failed failed function name
+ */
+void __failed(const char *test, const char *func, int line,
+ const char *failed)
+{
+ fprintf(stderr, "[%s] %s():%d: function %s() failed with error %d (%s)\n",
+ test, func, line, failed, errno, strerror(errno));
+}
+
+/**
+ * __check_volume - check volume information.
+ *
+ * @libubi libubi descriptor
+ * @dev_info UBI device description
+ * @test test name
+ * @func function name
+ * @line line number
+ * @vol_id ID of existing volume to check
+ * @req volume creation request to compare with
+ *
+ * This function checks if a volume created using @req request has exactly the
+ * requested characteristics. Returns 0 in case of success and %-1 in case of
+ * error.
+ */
+int __check_volume(libubi_t libubi, struct ubi_dev_info *dev_info,
+ const char *test, const char *func, int line, int vol_id,
+ const struct ubi_mkvol_request *req)
+{
+ int ret;
+ struct ubi_vol_info vol_info;
+ int eb_size;
+ long long rsvd_bytes;
+
+ ret = ubi_get_vol_info1(libubi, dev_info->dev_num, vol_id, &vol_info);
+ if (ret) {
+ __failed(test, func, line, "ubi_get_vol_info");
+ return -1;
+ }
+
+ if (req->alignment != vol_info.alignment) {
+ __err_msg(test, func, line,
+ "bad alignment: requested %d, got %d",
+ req->alignment, vol_info.alignment);
+ return -1;
+ }
+ if (req->vol_type != vol_info.type) {
+ __err_msg(test, func, line, "bad type: requested %d, got %d",
+ req->vol_type, vol_info.type);
+ return -1;
+ }
+ if (strlen(req->name) != strlen(&vol_info.name[0]) ||
+ strcmp(req->name, &vol_info.name[0]) != 0) {
+ __err_msg(test, func, line,
+ "bad name: requested \"%s\", got \"%s\"",
+ req->name, &vol_info.name[0]);
+ return -1;
+ }
+ if (vol_info.corrupted) {
+ __err_msg(test, func, line, "corrupted new volume");
+ return -1;
+ }
+
+ eb_size = dev_info->eb_size - (dev_info->eb_size % req->alignment);
+ if (eb_size != vol_info.eb_size) {
+ __err_msg(test, func, line,
+ "bad usable LEB size %d, should be %d",
+ vol_info.eb_size, eb_size);
+ return -1;
+ }
+
+ rsvd_bytes = req->bytes;
+ if (rsvd_bytes % eb_size)
+ rsvd_bytes += eb_size - (rsvd_bytes % eb_size);
+
+ if (rsvd_bytes != vol_info.rsvd_bytes) {
+ __err_msg(test, func, line,
+ "bad reserved bytes %lld, should be %lld",
+ vol_info.rsvd_bytes, rsvd_bytes);
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * __check_vol_patt - check that volume contains certain data
+ *
+ * @libubi libubi descriptor
+ * @dev_info UBI device description
+ * @test test name
+ * @func function name
+ * @line line number
+ * @node volume character device node
+ * @byte data pattern to check
+ *
+ * This function returns %0 if the volume contains only @byte bytes, and %-1 if
+ * not.
+ */
+int __check_vol_patt(libubi_t libubi, struct ubi_dev_info *dev_info,
+ const char *test, const char *func, int line,
+ const char *node, uint8_t byte)
+{
+ int ret, fd;
+ long long bytes = 0;
+ struct ubi_vol_info vol_info;
+ unsigned char buf[512];
+
+ fd = open(node, O_RDONLY);
+ if (fd == -1) {
+ __failed(test, func, line, "open");
+ __err_msg(test, func, line, "cannot open \"%s\"\n", node);
+ return -1;
+ }
+
+ ret = ubi_get_vol_info(libubi, node, &vol_info);
+ if (ret) {
+ __failed(test, func, line, "ubi_get_vol_info");
+ goto close;
+ }
+
+ while (bytes < vol_info.data_bytes) {
+ int i;
+
+ memset(&buf[0], ~byte, 512);
+ ret = read(fd, &buf[0], 512);
+ if (ret == -1) {
+ __failed(test, func, line, "read");
+ __err_msg(test, func, line, "bytes = %lld, ret = %d",
+ bytes, ret);
+ goto close;
+ }
+
+ if (ret == 0 && bytes + ret < vol_info.data_bytes) {
+ __err_msg(test, func, line,
+ "EOF, but read only %lld bytes of %lld",
+ bytes + ret, vol_info.data_bytes);
+ goto close;
+ }
+
+ for (i = 0; i < ret; i++)
+ if (buf[i] != byte) {
+ __err_msg(test, func, line,
+ "byte at %lld is not %#x but %#x",
+ bytes + i, byte, (int)buf[i]);
+ goto close;
+ }
+
+ bytes += ret;
+ }
+
+ close(fd);
+ return 0;
+
+close:
+ close(fd);
+ return -1;
+}
+
+/**
+ * __update_vol_patt - update volume using a certain byte pattern
+ *
+ * @libubi libubi descriptor
+ * @dev_info UBI device description
+ * @test test name
+ * @func function name
+ * @line line number
+ * @node volume character device node
+ * @byte data pattern to check
+ *
+ * This function returns %0 in case of success, and %-1 if in case of failure.
+ */
+int __update_vol_patt(libubi_t libubi, const char *test, const char *func,
+ int line, const char *node, long long bytes, uint8_t byte)
+{
+ int ret, fd;
+ long long written = 0;
+ unsigned char buf[512];
+
+ fd = open(node, O_RDWR);
+ if (fd == -1) {
+ __failed(test, func, line, "open");
+ __err_msg(test, func, line, "cannot open \"%s\"\n", node);
+ return -1;
+ }
+
+ if (ubi_update_start(libubi, fd, bytes)) {
+ __failed(test, func, line, "ubi_update_start");
+ __err_msg(test, func, line, "bytes = %lld", bytes);
+ goto close;
+ }
+
+ memset(&buf[0], byte, 512);
+
+ while (written != bytes) {
+ ret = write(fd, &buf[0], 512);
+ if (ret == -1) {
+ __failed(test, func, line, "write");
+ __err_msg(test, func, line, "written = %lld, ret = %d",
+ written, ret);
+ goto close;
+ }
+ written += ret;
+
+ if (written > bytes) {
+ __err_msg(test, func, line, "update length %lld bytes, "
+ "but %lld bytes are already written",
+ bytes, written);
+ goto close;
+ }
+ }
+
+ close(fd);
+ return 0;
+
+close:
+ close(fd);
+ return -1;
+}
diff --git a/ubi-utils/tests/common.h b/ubi-utils/tests/common.h
new file mode 100644
index 0000000..3e8ada8
--- /dev/null
+++ b/ubi-utils/tests/common.h
@@ -0,0 +1,103 @@
+/*
+ * 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.
+ *
+ * Author: Artem B. Bityutskiy
+ *
+ * The stuff which is common for many tests.
+ */
+
+#ifndef __COMMON_H__
+#define __COMMON_H__
+
+#include <string.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define UBI_VOLUME_PATTERN "/dev/ubi%d_%d"
+#define MIN_AVAIL_EBS 5
+#define PAGE_SIZE 4096
+
+#define min(a, b) ((a) < (b) ? (a) : (b))
+
+#define err_msg(fmt, ...) \
+ __err_msg(TESTNAME, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__)
+
+#define failed(name) \
+ __failed(TESTNAME, __FUNCTION__, __LINE__, name)
+
+#define initial_check(argc, argv) \
+ __initial_check(TESTNAME, argc, argv)
+
+#define check_volume(vol_id, req) \
+ __check_volume(libubi, &dev_info, TESTNAME, __FUNCTION__, \
+ __LINE__, vol_id, req)
+
+#define check_vol_patt(node, byte) \
+ __check_vol_patt(libubi, &dev_info, TESTNAME, __FUNCTION__, __LINE__, \
+ node, byte)
+
+#define update_vol_patt(node, bytes, byte) \
+ __update_vol_patt(libubi, TESTNAME, __FUNCTION__, __LINE__, \
+ node, bytes, byte)
+
+#define check_failed(ret, error, func, fmt, ...) ({ \
+ int __ret; \
+ \
+ if (!ret) { \
+ err_msg("%s() returned success but should have failed", func); \
+ err_msg(fmt, ##__VA_ARGS__); \
+ __ret = -1; \
+ } \
+ if (errno != (error)) { \
+ err_msg("%s failed with error %d (%s), expected %d (%s)", \
+ func, errno, strerror(errno), error, strerror(error)); \
+ err_msg(fmt, ##__VA_ARGS__); \
+ __ret = -1; \
+ } \
+ __ret = 0; \
+})
+
+/* Alignments to test, @s is eraseblock size */
+#define ALIGNMENTS(s) \
+ {3, 5, 27, 666, 512, 1024, 2048, (s)/2-3, (s)/2-2, (s)/2-1, (s)/2+1, \
+ (s)/2+2, (s)/2+3, (s)/3-3, (s)/3-2, (s)/3-1, (s)/3+1, (s)/3+2, \
+ (s)/3+3, (s)/4-3, (s)/4-2, (s)/4-1, (s)/4+1, (s)/4+2, (s)/4+3, \
+ (s)/5-3, (s)/5-2, (s)/5-1, (s)/5+1, (s)/5+2, (s)/5+3, (s)-17, (s)-9, \
+ (s)-8, (s)-6, (s)-4, (s)-1, (s)};
+
+extern void __err_msg(const char *test, const char *func, int line,
+ const char *fmt, ...);
+void __failed(const char *test, const char *func, int line,
+ const char *failed);
+int __initial_check(const char *test, int argc, char * const argv[]);
+int __check_volume(libubi_t libubi, struct ubi_dev_info *dev_info,
+ const char *test, const char *func, int line, int vol_id,
+ const struct ubi_mkvol_request *req);
+int __check_vol_patt(libubi_t libubi, struct ubi_dev_info *dev_info,
+ const char *test, const char *func, int line,
+ const char *node, uint8_t byte);
+int __update_vol_patt(libubi_t libubi, const char *test, const char *func,
+ int line, const char *node, long long bytes,
+ uint8_t byte);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !__COMMON_H__ */
diff --git a/ubi-utils/tests/integ.c b/ubi-utils/tests/integ.c
new file mode 100644
index 0000000..4da7121
--- /dev/null
+++ b/ubi-utils/tests/integ.c
@@ -0,0 +1,783 @@
+#define _LARGEFILE64_SOURCE
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+
+#include "libubi.h"
+
+struct erase_block_info;
+struct volume_info;
+struct ubi_device_info;
+
+struct write_info
+{
+ struct write_info *next;
+ struct erase_block_info *erase_block;
+ int offset_within_block; /* Offset within erase block */
+ off64_t offset; /* Offset within volume */
+ int size;
+ int random_seed;
+};
+
+struct erase_block_info
+{
+ struct volume_info *volume;
+ int block_number;
+ off64_t offset; /* Offset within volume */
+ off64_t top_of_data;
+ int touched; /* Have we done anything at all with this erase block */
+ int erased; /* This erased block is currently erased */
+ struct write_info *writes;
+};
+
+struct volume_fd
+{
+ struct volume_fd *next;
+ struct volume_info *volume;
+ int fd;
+};
+
+struct volume_info
+{
+ struct volume_info *next;
+ struct ubi_device_info *ubi_device;
+ struct volume_fd *fds;
+ struct erase_block_info *erase_blocks;
+ const char *device_file_name;
+ struct ubi_vol_info info;
+};
+
+struct ubi_device_info
+{
+ struct volume_info *volumes;
+ const char *device_file_name;
+ struct ubi_dev_info info;
+};
+
+struct open_volume_fd
+{
+ struct open_volume_fd *next;
+ struct volume_fd *vol_fd;
+};
+
+#define MAX_UBI_DEVICES 64
+
+static libubi_t libubi;
+
+static struct ubi_info info;
+static struct ubi_device_info ubi_array[MAX_UBI_DEVICES];
+
+static uint64_t total_written = 0;
+static uint64_t total_space = 0;
+
+static struct open_volume_fd *open_volumes;
+static size_t open_volume_count = 0;
+
+static const char *ubi_module_load_string;
+
+static unsigned char *write_buffer = NULL;
+static unsigned char *read_buffer = NULL;
+
+static long long max_ebs_per_vol = 0; /* max number of ebs per vol (zero => no max) */
+
+static unsigned long next_seed = 1;
+
+static unsigned get_next_seed()
+{
+ next_seed = next_seed * 1103515245 + 12345;
+ return ((unsigned) (next_seed / 65536) % 32768);
+}
+
+static void error_exit(const char *msg)
+{
+ int eno = errno;
+ fprintf(stderr,"UBI Integrity Test Error: %s\n",msg);
+ if (eno) {
+ fprintf(stderr, "errno = %d\n", eno);
+ fprintf(stderr, "strerror = %s\n", strerror(eno));
+ }
+ exit(1);
+}
+
+static void *allocate(size_t n)
+{
+ void *p = malloc(n);
+ if (!p)
+ error_exit("Memory allocation failure");
+ memset(p, 0, n);
+ return p;
+}
+
+static unsigned get_random_number(unsigned n)
+{
+ uint64_t r, b;
+
+ if (n < 1)
+ return 0;
+ r = rand();
+ r *= n;
+ b = RAND_MAX;
+ b += 1;
+ r /= b;
+ return r;
+}
+
+static struct volume_fd *open_volume(struct volume_info *vol)
+{
+ struct volume_fd *s;
+ struct open_volume_fd *ofd;
+ int fd;
+
+ if (vol->fds) {
+ /* If already open dup it */
+ fd = dup(vol->fds->fd);
+ if (fd == -1)
+ error_exit("Failed to dup volume device file des");
+ } else {
+ fd = open(vol->device_file_name, O_RDWR | O_LARGEFILE);
+ if (fd == -1)
+ error_exit("Failed to open volume device file");
+ }
+ s = allocate(sizeof(*s));
+ s->fd = fd;
+ s->volume = vol;
+ s->next = vol->fds;
+ vol->fds = s;
+ /* Add to open volumes list */
+ ofd = allocate(sizeof(*ofd));
+ ofd->vol_fd = s;
+ ofd->next = open_volumes;
+ open_volumes = ofd;
+ open_volume_count += 1;
+ return 0;
+}
+
+static void close_volume(struct volume_fd *vol_fd)
+{
+ struct volume_fd *vfd, *vfd_last;
+ struct open_volume_fd *ofd, *ofd_last;
+ int fd = vol_fd->fd;
+
+ /* Remove from open volumes list */
+ ofd_last = NULL;
+ ofd = open_volumes;
+ while (ofd) {
+ if (ofd->vol_fd == vol_fd) {
+ if (ofd_last)
+ ofd_last->next = ofd->next;
+ else
+ open_volumes = ofd->next;
+ free(ofd);
+ open_volume_count -= 1;
+ break;
+ }
+ ofd_last = ofd;
+ ofd = ofd->next;
+ }
+ /* Remove from volume fd list */
+ vfd_last = NULL;
+ vfd = vol_fd->volume->fds;
+ while (vfd) {
+ if (vfd == vol_fd) {
+ if (vfd_last)
+ vfd_last->next = vfd->next;
+ else
+ vol_fd->volume->fds = vfd->next;
+ free(vfd);
+ break;
+ }
+ vfd_last = vfd;
+ vfd = vfd->next;
+ }
+ /* Close volume device file */
+ if (close(fd) == -1)
+ error_exit("Failed to close volume file descriptor");
+}
+
+static void set_random_data(unsigned seed, unsigned char *buf, int size)
+{
+ int i;
+ unsigned r;
+
+ r = rand();
+ srand(seed);
+ for (i = 0; i < size; ++i)
+ buf[i] = rand();
+ srand(r);
+}
+
+#if 0
+static void print_write_info(struct write_info *w)
+{
+ printf("Offset: %lld Size:%d Seed:%u\n", w->offset, w->size, w->random_seed);
+ fflush(stdout);
+}
+#endif
+
+static void check_erase_block(struct erase_block_info *erase_block, int fd)
+{
+ struct write_info *w;
+ off64_t gap_end;
+ int eb_size = erase_block->volume->info.eb_size;
+ ssize_t bytes_read;
+
+ w = erase_block->writes;
+ gap_end = erase_block->offset + eb_size;
+ while (w) {
+ if (w->offset + w->size < gap_end) {
+ /* There is a gap. Check all 0xff */
+ off64_t gap_start = w->offset + w->size;
+ size_t size = gap_end - gap_start;
+ if (lseek64(fd, gap_start, SEEK_SET) != gap_start)
+ error_exit("lseek64 failed");
+ memset(read_buffer, 0 , size);
+ errno = 0;
+ bytes_read = read(fd, read_buffer, size);
+ if (bytes_read != size)
+ error_exit("read failed in gap");
+ while (size)
+ if (read_buffer[--size] != 0xff) {
+ fprintf(stderr, "block no. = %d\n" , erase_block->block_number);
+ fprintf(stderr, "offset = %lld\n" , (long long) gap_start);
+ fprintf(stderr, "size = %ld\n" , (long) bytes_read);
+ error_exit("verify 0xff failed");
+ }
+ }
+ if (lseek64(fd, w->offset, SEEK_SET) != w->offset)
+ error_exit("lseek64 failed");
+ memset(read_buffer, 0 , w->size);
+ errno = 0;
+ bytes_read = read(fd, read_buffer, w->size);
+ if (bytes_read != w->size) {
+ fprintf(stderr, "offset = %lld\n" , (long long) w->offset);
+ fprintf(stderr, "size = %ld\n" , (long) w->size);
+ fprintf(stderr, "bytes_read = %ld\n" , (long) bytes_read);
+ error_exit("read failed");
+ }
+ set_random_data(w->random_seed, write_buffer, w->size);
+ if (memcmp(read_buffer, write_buffer, w->size))
+ error_exit("verify failed");
+ gap_end = w->offset;
+ w = w->next;
+ }
+ if (gap_end > erase_block->offset) {
+ /* Check all 0xff */
+ off64_t gap_start = erase_block->offset;
+ size_t size = gap_end - gap_start;
+ if (lseek64(fd, gap_start, SEEK_SET) != gap_start)
+ error_exit("lseek64 failed");
+ memset(read_buffer, 0 , size);
+ errno = 0;
+ bytes_read = read(fd, read_buffer, size);
+ if (bytes_read != size)
+ error_exit("read failed in gap");
+ while (size)
+ if (read_buffer[--size] != 0xff) {
+ fprintf(stderr, "block no. = %d\n" , erase_block->block_number);
+ fprintf(stderr, "offset = %lld\n" , (long long) gap_start);
+ fprintf(stderr, "size = %ld\n" , (long) bytes_read);
+ error_exit("verify 0xff failed!");
+ }
+ }
+}
+
+static int write_to_erase_block(struct erase_block_info *erase_block, int fd)
+{
+ int page_size = erase_block->volume->ubi_device->info.min_io_size;
+ int eb_size = erase_block->volume->info.eb_size;
+ int next_offset = 0;
+ int space, size;
+ off64_t offset;
+ unsigned seed;
+ struct write_info *w;
+
+ if (erase_block->writes)
+ next_offset = erase_block->writes->offset_within_block + erase_block->writes->size;
+ space = eb_size - next_offset;
+ if (space <= 0)
+ return 0; /* No space */
+ if (!get_random_number(10)) {
+ /* 1 time in 10 leave a gap */
+ next_offset += get_random_number(space);
+ next_offset = (next_offset / page_size) * page_size;
+ space = eb_size - next_offset;
+ }
+ if (get_random_number(2))
+ size = 1 * page_size;
+ else if (get_random_number(2))
+ size = 2 * page_size;
+ else if (get_random_number(2))
+ size = 3 * page_size;
+ else if (get_random_number(2))
+ size = 4 * page_size;
+ else {
+ if (get_random_number(4))
+ size = get_random_number(space);
+ else
+ size = space;
+ size = (size / page_size) * page_size;
+ }
+ if (size == 0 || size > space)
+ size = page_size;
+ if (next_offset + size > eb_size)
+ error_exit("internal error");
+ offset = erase_block->offset + next_offset;
+ if (offset < erase_block->top_of_data)
+ error_exit("internal error!");
+ if (lseek64(fd, offset, SEEK_SET) != offset)
+ error_exit("lseek64 failed");
+ /* Do write */
+ seed = get_next_seed();
+ if (!seed)
+ seed = 1;
+ set_random_data(seed, write_buffer, size);
+ if (write(fd, write_buffer, size) != size)
+ error_exit("write failed");
+ erase_block->top_of_data = offset + size;
+ /* Make write info and add to eb */
+ w = allocate(sizeof(*w));
+ w->offset_within_block = next_offset;
+ w->offset = offset;
+ w->size = size;
+ w->random_seed = seed;
+ w->next = erase_block->writes;
+ erase_block->writes = w;
+ erase_block->touched = 1;
+ erase_block->erased = 0;
+ total_written += size;
+ return 1;
+}
+
+static void erase_erase_block(struct erase_block_info *erase_block, int fd)
+{
+ struct write_info *w;
+ uint32_t eb_no;
+ int res;
+
+ eb_no = erase_block->block_number;
+ res = ioctl(fd, UBI_IOCEBER, &eb_no);
+ if (res)
+ error_exit("Failed to erase an erase block");
+ /* Remove writes from this eb */
+ while (erase_block->writes) {
+ w = erase_block->writes;
+ erase_block->writes = erase_block->writes->next;
+ free(w);
+ }
+ erase_block->erased = 1;
+ erase_block->touched = 1;
+ erase_block->top_of_data = erase_block->offset;
+}
+
+static void operate_on_erase_block(struct erase_block_info *erase_block, int fd)
+{
+ /*
+ Possible operations:
+ read from it and verify
+ write to it
+ erase it
+ */
+ int work_done = 1;
+ static int no_work_done_count = 0;
+
+ if (!get_random_number(10) && no_work_done_count <= 5) {
+ check_erase_block(erase_block, fd);
+ work_done = 0;
+ } else if (get_random_number(100)) {
+ if (!write_to_erase_block(erase_block, fd)) {
+ /* The erase block was full */
+ if (get_random_number(2) || no_work_done_count > 5)
+ erase_erase_block(erase_block, fd);
+ else
+ work_done = 0;
+ }
+ } else
+ erase_erase_block(erase_block, fd);
+ if (work_done)
+ no_work_done_count = 0;
+ else
+ no_work_done_count += 1;
+}
+
+static void operate_on_open_volume(struct volume_fd *vol_fd)
+{
+ /*
+ Possible operations:
+ operate on an erase block
+ close volume
+ */
+ if (get_random_number(100) == 0)
+ close_volume(vol_fd);
+ else {
+ /* Pick an erase block at random */
+ int eb_no = get_random_number(vol_fd->volume->info.rsvd_ebs);
+ operate_on_erase_block(&vol_fd->volume->erase_blocks[eb_no], vol_fd->fd);
+ }
+}
+
+static void operate_on_volume(struct volume_info *vol)
+{
+ /*
+ Possible operations:
+ open it
+ resize it (must close fd's first) <- TODO
+ delete it (must close fd's first) <- TODO
+ */
+ open_volume(vol);
+}
+
+static int ubi_major(const char *device_file_name)
+{
+ struct stat buf;
+ static int maj = 0;
+
+ if (maj)
+ return maj;
+ if (stat(device_file_name, &buf) == -1)
+ error_exit("Failed to stat ubi device file");
+ maj = major(buf.st_rdev);
+ return maj;
+}
+
+static void operate_on_ubi_device(struct ubi_device_info *ubi_device)
+{
+ /*
+ TODO:
+ Possible operations:
+ create a new volume
+ operate on existing volume
+ */
+ /*
+ Simplified operation (i.e. only have 1 volume):
+ If there are no volumes create 1 volumne
+ Then operate on the volume
+ */
+ if (ubi_device->info.vol_count == 0) {
+ /* Create the one-and-only volume we will use */
+ char dev_name[1024];
+ int i, n, maj, fd;
+ struct volume_info *s;
+ struct ubi_mkvol_request req;
+
+ req.vol_id = UBI_VOL_NUM_AUTO;
+ req.alignment = 1; /* TODO: What is this? */
+ req.bytes = ubi_device->info.eb_size * max_ebs_per_vol;
+ if (req.bytes == 0 || req.bytes > ubi_device->info.avail_bytes)
+ req.bytes = ubi_device->info.avail_bytes;
+ req.vol_type = UBI_DYNAMIC_VOLUME;
+ req.name = "integ-test-vol";
+ if (ubi_mkvol(libubi, ubi_device->device_file_name, &req))
+ error_exit("ubi_mkvol failed");
+ s = allocate(sizeof(*s));
+ s->ubi_device = ubi_device;
+ if (ubi_get_vol_info1(libubi, ubi_device->info.dev_num, req.vol_id, &s->info))
+ error_exit("ubi_get_vol_info failed");
+ n = s->info.rsvd_ebs;
+ s->erase_blocks = allocate(sizeof(struct erase_block_info) * n);
+ for (i = 0; i < n; ++i) {
+ s->erase_blocks[i].volume = s;
+ s->erase_blocks[i].block_number = i;
+ s->erase_blocks[i].offset = i * (off64_t) s->info.eb_size;
+ s->erase_blocks[i].top_of_data = s->erase_blocks[i].offset;
+ }
+ /* FIXME: Correctly get device file name */
+ sprintf(dev_name, "%s_%d", ubi_device->device_file_name, req.vol_id);
+ s->device_file_name = strdup(dev_name);
+ ubi_device->volumes = s;
+ ubi_device->info.vol_count += 1;
+ sleep(1);
+ fd = open(s->device_file_name, O_RDONLY);
+ if (fd == -1) {
+ /* FIXME: Correctly make node */
+ maj = ubi_major(ubi_device->device_file_name);
+ sprintf(dev_name, "mknod %s c %d %d", s->device_file_name, maj, req.vol_id + 1);
+ system(dev_name);
+ } else if (close(fd) == -1)
+ error_exit("Failed to close volume device file");
+ }
+ operate_on_volume(ubi_device->volumes);
+}
+
+static void do_an_operation(void)
+{
+ int too_few = (open_volume_count < info.dev_count * 3);
+ int too_many = (open_volume_count > info.dev_count * 5);
+
+ if (too_many || (!too_few && get_random_number(1000) > 0)) {
+ /* Operate on an open volume */
+ size_t pos;
+ struct open_volume_fd *ofd;
+ pos = get_random_number(open_volume_count);
+ for (ofd = open_volumes; pos && ofd && ofd->next; --pos)
+ ofd = ofd->next;
+ operate_on_open_volume(ofd->vol_fd);
+ } else if (info.dev_count > 0) {
+ /* Operate on a ubi device */
+ size_t ubi_pos = 0;
+ if (info.dev_count > 1)
+ ubi_pos = get_random_number(info.dev_count - 1);
+ operate_on_ubi_device(&ubi_array[ubi_pos]);
+ } else
+ error_exit("Internal error");
+}
+
+static void get_ubi_devices_info(void)
+{
+ int i, ubi_pos = 0;
+ char dev_name[1024];
+ size_t buf_size = 1024 * 128;
+
+ if (ubi_get_info(libubi, &info))
+ error_exit("ubi_get_info failed");
+ if (info.dev_count > MAX_UBI_DEVICES)
+ error_exit("Too many ubi devices");
+ for (i = info.lowest_dev_num; i <= info.highest_dev_num; ++i) {
+ struct ubi_device_info *s;
+ s = &ubi_array[ubi_pos++];
+ if (ubi_get_dev_info1(libubi, i, &s->info))
+ error_exit("ubi_get_dev_info1 failed");
+ if (s->info.vol_count)
+ error_exit("There are existing volumes");
+ /* FIXME: Correctly get device file name */
+ sprintf(dev_name, "/dev/ubi%d", i);
+ s->device_file_name = strdup(dev_name);
+ if (buf_size < s->info.eb_size)
+ buf_size = s->info.eb_size;
+ if (max_ebs_per_vol && s->info.eb_size * max_ebs_per_vol < s->info.avail_bytes)
+ total_space += s->info.eb_size * max_ebs_per_vol;
+ else
+ total_space += s->info.avail_bytes;
+ }
+ write_buffer = allocate(buf_size);
+ read_buffer = allocate(buf_size);
+}
+
+static void load_ubi(void)
+{
+ system("rmmod ubi");
+ if (system(ubi_module_load_string) != 0)
+ error_exit("Failed to load UBI module");
+ sleep(1);
+}
+
+static void do_some_operations(void)
+{
+ unsigned i = 0;
+ total_written = 0;
+ printf("Total space: %llu\n", (unsigned long long) total_space);
+ while (total_written < total_space * 3) {
+ do_an_operation();
+ if (i++ % 10000 == 0)
+ printf("Total written: %llu\n", (unsigned long long) total_written);
+ }
+ printf("Total written: %llu\n", (unsigned long long) total_written);
+}
+
+static void reload_ubi(void)
+{
+ /* Remove module */
+ if (system("rmmod ubi") != 0)
+ error_exit("Failed to remove UBI module");
+ /* Install module */
+ if (system(ubi_module_load_string) != 0)
+ error_exit("Failed to load UBI module");
+ sleep(1);
+}
+
+static void check_volume(struct volume_info *vol)
+{
+ struct erase_block_info *eb = vol->erase_blocks;
+ int pos;
+ int fd;
+
+ fd = open(vol->device_file_name, O_RDWR | O_LARGEFILE);
+ if (fd == -1)
+ error_exit("Failed to open volume device file");
+ for (pos = 0; pos < vol->info.rsvd_ebs; ++pos)
+ check_erase_block(eb++, fd);
+ if (close(fd) == -1)
+ error_exit("Failed to close volume device file");
+}
+
+static void check_ubi_device(struct ubi_device_info *ubi_device)
+{
+ struct volume_info *vol;
+
+ vol = ubi_device->volumes;
+ while (vol) {
+ check_volume(vol);
+ vol = vol->next;
+ }
+}
+
+static void check_ubi(void)
+{
+ int i;
+
+ for (i = 0; i < info.dev_count; ++i)
+ check_ubi_device(&ubi_array[i]);
+}
+
+static int is_all_digits(const char *s)
+{
+ const char *digits = "0123456789";
+ if (!s || !*s)
+ return 0;
+ for (;*s;++s)
+ if (!strchr(digits,*s))
+ return 0;
+ return 1;
+}
+
+static int get_short_arg(int *pos,const char *name,long long *result,int argc,char *argv[])
+{
+ const char *p = NULL;
+ int i = *pos;
+ size_t n = strlen(name);
+
+ if (strlen(argv[i]) > n)
+ p = argv[i] + n;
+ else if (++i < argc)
+ p = argv[i];
+ if (!is_all_digits(p))
+ return 1;
+ *result = atoll(p);
+ *pos = i;
+ return 0;
+}
+
+static int get_long_arg(int *pos,const char *name,long long *result,int argc,char *argv[])
+{
+ const char *p = NULL;
+ int i = *pos;
+ size_t n = strlen(name);
+
+ if (strlen(argv[i]) > n)
+ p = argv[i] + n;
+ else if (++i < argc)
+ p = argv[i];
+ if (p && *p == '=') {
+ p += 1;
+ if (!*p && ++i < argc)
+ p = argv[i];
+ }
+ if (!is_all_digits(p))
+ return 1;
+ *result = atoll(p);
+ *pos = i;
+ return 0;
+}
+
+static int remove_all_volumes(void)
+{
+ int i;
+
+ for (i = 0; i < info.dev_count; ++i) {
+ struct ubi_device_info *ubi_device = &ubi_array[i];
+ struct volume_info *vol;
+ vol = ubi_device->volumes;
+ while (vol) {
+ int res = ubi_rmvol(libubi,
+ ubi_device->device_file_name,
+ vol->info.vol_id);
+ if (res)
+ return res;
+ vol = vol->next;
+ }
+ }
+ return 0;
+}
+
+int main(int argc,char *argv[])
+{
+ int i;
+ long long r, repeat = 1;
+ int initial_seed = 1, args_ok = 1;
+
+ printf("UBI Integrity Test\n");
+
+ /* Get arguments */
+ ubi_module_load_string = 0;
+ for (i = 1; i < argc; ++i) {
+ if (strncmp(argv[i], "-h", 2) == 0)
+ args_ok = 0;
+ else if (strncmp(argv[i], "--help", 6) == 0)
+ args_ok = 0;
+ else if (strncmp(argv[i], "-n", 2) == 0) {
+ if (get_short_arg(&i, "-n", &repeat, argc, argv))
+ args_ok = 0;
+ } else if (strncmp(argv[i], "--repeat", 8) == 0) {
+ if (get_long_arg(&i, "--repeat", &repeat, argc, argv))
+ args_ok = 0;
+ } else if (strncmp(argv[i], "-m", 2) == 0) {
+ if (get_short_arg(&i,"-m", &max_ebs_per_vol, argc, argv))
+ args_ok = 0;
+ } else if (strncmp(argv[i], "--maxebs", 8) == 0) {
+ if (get_long_arg(&i, "--maxebs", &max_ebs_per_vol, argc, argv))
+ args_ok = 0;
+ } else if (!ubi_module_load_string)
+ ubi_module_load_string = argv[i];
+ else
+ args_ok = 0;
+ }
+ if (!args_ok || !ubi_module_load_string) {
+ fprintf(stderr, "Usage is: ubi_integ [<options>] <UBI Module load command>\n");
+ fprintf(stderr, " Options: \n");
+ fprintf(stderr, " -h, --help Help\n");
+ fprintf(stderr, " -n arg, --repeat=arg Repeat test arg times\n");
+ fprintf(stderr, " -m arg, --maxebs=arg Max no. of erase blocks\n");
+ return 1;
+ }
+
+ initial_seed = getpid();
+ printf("Initial seed = %u\n", (unsigned) initial_seed);
+ next_seed = initial_seed;
+ srand(initial_seed);
+ load_ubi();
+
+ libubi = libubi_open();
+ if (!libubi)
+ error_exit("Failed to open libubi");
+
+ get_ubi_devices_info();
+
+ r = 0;
+ while (repeat == 0 || r++ < repeat) {
+ printf("Cycle %lld\n", r);
+ do_some_operations();
+
+ /* Close all volumes */
+ while (open_volumes)
+ close_volume(open_volumes->vol_fd);
+
+ check_ubi();
+
+ libubi_close(libubi);
+
+ reload_ubi();
+
+ libubi = libubi_open();
+ if (!libubi)
+ error_exit("Failed to open libubi");
+
+ check_ubi();
+ }
+
+ if (remove_all_volumes())
+ error_exit("Failed to remove all volumes");
+
+ libubi_close(libubi);
+
+ printf("UBI Integrity Test completed ok\n");
+ return 0;
+}
diff --git a/ubi-utils/tests/io_basic.c b/ubi-utils/tests/io_basic.c
new file mode 100644
index 0000000..2e8809a
--- /dev/null
+++ b/ubi-utils/tests/io_basic.c
@@ -0,0 +1,182 @@
+/*
+ * 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.
+ *
+ * Author: Artem B. Bityutskiy
+ *
+ * Test basic UBI volume I/O capabilities.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "libubi.h"
+#define TESTNAME "io_basic"
+#include "common.h"
+
+static libubi_t libubi;
+static struct ubi_dev_info dev_info;
+const char *node;
+
+static int test_basic(int type);
+static int test_aligned(int type);
+
+int main(int argc, char * const argv[])
+{
+ if (initial_check(argc, argv))
+ return 1;
+
+ node = argv[1];
+
+ libubi = libubi_open();
+ if (libubi == NULL) {
+ failed("libubi_open");
+ return 1;
+ }
+
+ if (ubi_get_dev_info(libubi, node, &dev_info)) {
+ failed("ubi_get_dev_info");
+ goto close;
+ }
+
+ if (test_basic(UBI_DYNAMIC_VOLUME))
+ goto close;
+ if (test_basic(UBI_STATIC_VOLUME))
+ goto close;
+ if (test_aligned(UBI_DYNAMIC_VOLUME))
+ goto close;
+ if (test_aligned(UBI_STATIC_VOLUME))
+ goto close;
+
+ libubi_close(libubi);
+ return 0;
+
+close:
+ libubi_close(libubi);
+ return 1;
+}
+
+/**
+ * test_basic - check basic volume read and update capabilities.
+ *
+ * @type volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME)
+ *
+ * Thus function returns %0 in case of success and %-1 in case of failure.
+ */
+static int test_basic(int type)
+{
+ struct ubi_mkvol_request req;
+ const char *name = TESTNAME ":test_basic()";
+ char vol_node[strlen(UBI_VOLUME_PATTERN) + 100];
+
+ req.vol_id = UBI_VOL_NUM_AUTO;
+ req.alignment = 1;
+ req.bytes = dev_info.avail_bytes;
+ req.vol_type = type;
+ req.name = name;
+
+ if (ubi_mkvol(libubi, node, &req)) {
+ failed("ubi_mkvol");
+ return -1;
+ }
+
+ sprintf(&vol_node[0], UBI_VOLUME_PATTERN, dev_info.dev_num, req.vol_id);
+
+ /* Make sure newly created volume contains only 0xFF bytes */
+ if (check_vol_patt(&vol_node[0], 0xFF))
+ goto remove;
+
+ /* Write 0xA5 bytes to the volume */
+ if (update_vol_patt(&vol_node[0], dev_info.avail_bytes, 0xA5))
+ goto remove;
+ if (check_vol_patt(&vol_node[0], 0xA5))
+ goto remove;
+
+ if (ubi_rmvol(libubi, node, req.vol_id)) {
+ failed("ubi_rmvol");
+ return -1;
+ }
+
+ return 0;
+
+remove:
+ ubi_rmvol(libubi, node, req.vol_id);
+ return -1;
+}
+
+/**
+ * test_aligned - test volume alignment feature.
+ *
+ * @type volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME)
+ *
+ * Thus function returns %0 in case of success and %-1 in case of failure.
+ */
+static int test_aligned(int type)
+{
+ int i, ebsz;
+ struct ubi_mkvol_request req;
+ const char *name = TESTNAME ":test_aligned()";
+ char vol_node[strlen(UBI_VOLUME_PATTERN) + 100];
+ int alignments[] = ALIGNMENTS(dev_info.eb_size);
+
+ req.vol_type = type;
+ req.name = name;
+
+ for (i = 0; i < sizeof(alignments)/sizeof(int); i++) {
+ req.vol_id = UBI_VOL_NUM_AUTO;
+
+ req.alignment = alignments[i];
+ req.alignment -= req.alignment % dev_info.min_io_size;
+ if (req.alignment == 0)
+ req.alignment = dev_info.min_io_size;
+
+ ebsz = dev_info.eb_size - dev_info.eb_size % req.alignment;
+ req.bytes = MIN_AVAIL_EBS * ebsz;
+
+ if (ubi_mkvol(libubi, node, &req)) {
+ failed("ubi_mkvol");
+ return -1;
+ }
+
+ sprintf(&vol_node[0], UBI_VOLUME_PATTERN, dev_info.dev_num, req.vol_id);
+
+ /* Make sure newly created volume contains only 0xFF bytes */
+ if (check_vol_patt(&vol_node[0], 0xFF))
+ goto remove;
+
+ /* Write 0xA5 bytes to the volume */
+ if (update_vol_patt(&vol_node[0], req.bytes, 0xA5))
+ goto remove;
+ if (check_vol_patt(&vol_node[0], 0xA5))
+ goto remove;
+
+ if (ubi_rmvol(libubi, node, req.vol_id)) {
+ failed("ubi_rmvol");
+ return -1;
+ }
+ }
+
+ return 0;
+
+remove:
+ ubi_rmvol(libubi, node, req.vol_id);
+ return -1;
+}
diff --git a/ubi-utils/tests/io_paral.c b/ubi-utils/tests/io_paral.c
new file mode 100644
index 0000000..4857bf8
--- /dev/null
+++ b/ubi-utils/tests/io_paral.c
@@ -0,0 +1,251 @@
+/*
+ * 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.
+ *
+ * Author: Artem B. Bityutskiy
+ *
+ * This test does a lot of I/O to volumes in parallel.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "libubi.h"
+#define TESTNAME "io_paral"
+#include "common.h"
+
+#define THREADS_NUM 3
+#define ITERATIONS 100
+
+static libubi_t libubi;
+static struct ubi_dev_info dev_info;
+const char *node;
+static int iterations = ITERATIONS;
+int total_bytes;
+
+static void * the_thread(void *ptr);
+
+static long long memory_limit(void)
+{
+ long long result = 0;
+ FILE *f;
+
+ f = fopen("/proc/meminfo", "r");
+ if (!f)
+ return 0;
+ fscanf(f, "%*s %lld", &result);
+ fclose(f);
+ return result * 1024 / 4;
+}
+
+int main(int argc, char * const argv[])
+{
+ int i, ret;
+ pthread_t threads[THREADS_NUM];
+ struct ubi_mkvol_request req;
+ long long mem_limit;
+
+ if (initial_check(argc, argv))
+ return 1;
+
+ node = argv[1];
+
+ libubi = libubi_open();
+ if (libubi == NULL) {
+ failed("libubi_open");
+ return 1;
+ }
+
+ if (ubi_get_dev_info(libubi, node, &dev_info)) {
+ failed("ubi_get_dev_info");
+ goto close;
+ }
+
+ req.alignment = 1;
+ mem_limit = memory_limit();
+ if (mem_limit && mem_limit < dev_info.avail_bytes)
+ total_bytes = req.bytes =
+ (mem_limit / dev_info.eb_size / THREADS_NUM)
+ * dev_info.eb_size;
+ else
+ total_bytes = req.bytes =
+ ((dev_info.avail_ebs - 3) / THREADS_NUM)
+ * dev_info.eb_size;
+ for (i = 0; i < THREADS_NUM; i++) {
+ char name[100];
+
+ req.vol_id = i;
+ sprintf(&name[0], TESTNAME":%d", i);
+ req.name = &name[0];
+ req.vol_type = (i & 1) ? UBI_STATIC_VOLUME : UBI_DYNAMIC_VOLUME;
+
+ if (ubi_mkvol(libubi, node, &req)) {
+ failed("ubi_mkvol");
+ goto remove;
+ }
+ }
+
+ /* Create one volume with static data to make WL work more */
+ req.vol_id = THREADS_NUM;
+ req.name = TESTNAME ":static";
+ req.vol_type = UBI_DYNAMIC_VOLUME;
+ req.bytes = 3*dev_info.eb_size;
+ if (ubi_mkvol(libubi, node, &req)) {
+ failed("ubi_mkvol");
+ goto remove;
+ }
+
+ for (i = 0; i < THREADS_NUM; i++) {
+ ret = pthread_create(&threads[i], NULL, &the_thread, (void*)i);
+ if (ret) {
+ failed("pthread_create");
+ goto remove;
+ }
+ }
+
+ for (i = 0; i < THREADS_NUM; i++)
+ pthread_join(threads[i], NULL);
+
+ for (i = 0; i <= THREADS_NUM; i++) {
+ if (ubi_rmvol(libubi, node, i)) {
+ failed("ubi_rmvol");
+ goto remove;
+ }
+ }
+
+ libubi_close(libubi);
+ return 0;
+
+remove:
+ for (i = 0; i <= THREADS_NUM; i++)
+ ubi_rmvol(libubi, node, i);
+
+close:
+ libubi_close(libubi);
+ return 1;
+}
+
+/**
+ * the_thread - the testing thread.
+ *
+ * @ptr thread number
+ */
+static void * the_thread(void *ptr)
+{
+ int fd, iter = iterations, vol_id = (int)ptr;
+ unsigned char *wbuf, *rbuf;
+ char vol_node[strlen(UBI_VOLUME_PATTERN) + 100];
+
+ wbuf = malloc(total_bytes);
+ rbuf = malloc(total_bytes);
+ if (!wbuf || !rbuf) {
+ failed("malloc");
+ goto free;
+ }
+
+ sprintf(&vol_node[0], UBI_VOLUME_PATTERN, dev_info.dev_num, vol_id);
+
+ while (iter--) {
+ int i, ret, written = 0, rd = 0;
+ int bytes = (random() % (total_bytes - 1)) + 1;
+
+ fd = open(vol_node, O_RDWR);
+ if (fd == -1) {
+ failed("open");
+ err_msg("cannot open \"%s\"\n", node);
+ goto free;
+ }
+
+ for (i = 0; i < bytes; i++)
+ wbuf[i] = random() % 255;
+ memset(rbuf, '\0', bytes);
+
+ do {
+ ret = ubi_update_start(libubi, fd, bytes);
+ if (ret && errno != EBUSY) {
+ failed("ubi_update_start");
+ err_msg("vol_id %d", vol_id);
+ goto close;
+ }
+ } while (ret);
+
+ while (written < bytes) {
+ int to_write = random() % (bytes - written);
+
+ if (to_write == 0)
+ to_write = 1;
+
+ ret = write(fd, wbuf, to_write);
+ if (ret != to_write) {
+ failed("write");
+ err_msg("failed to write %d bytes at offset %d "
+ "of volume %d", to_write, written,
+ vol_id);
+ err_msg("update: %d bytes", bytes);
+ goto close;
+ }
+
+ written += to_write;
+ }
+
+ close(fd);
+
+ fd = open(vol_node, O_RDONLY);
+ if (fd == -1) {
+ failed("open");
+ err_msg("cannot open \"%s\"\n", node);
+ goto free;
+ }
+
+ /* read data back and check */
+ while (rd < bytes) {
+ int to_read = random() % (bytes - rd);
+
+ if (to_read == 0)
+ to_read = 1;
+
+ ret = read(fd, rbuf, to_read);
+ if (ret != to_read) {
+ failed("read");
+ err_msg("failed to read %d bytes at offset %d "
+ "of volume %d", to_read, rd, vol_id);
+ goto close;
+ }
+
+ rd += to_read;
+ }
+
+ close(fd);
+
+ }
+
+ free(wbuf);
+ free(rbuf);
+ return NULL;
+
+close:
+ close(fd);
+free:
+ free(wbuf);
+ free(rbuf);
+ return NULL;
+}
diff --git a/ubi-utils/tests/io_read.c b/ubi-utils/tests/io_read.c
new file mode 100644
index 0000000..c5d1da7
--- /dev/null
+++ b/ubi-utils/tests/io_read.c
@@ -0,0 +1,398 @@
+/*
+ * 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.
+ *
+ * Author: Artem B. Bityutskiy
+ *
+ * Test UBI volume read.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "libubi.h"
+#define TESTNAME "io_basic"
+#include "common.h"
+
+static libubi_t libubi;
+static struct ubi_dev_info dev_info;
+const char *node;
+
+static int test_static(void);
+static int test_read(int type);
+
+int main(int argc, char * const argv[])
+{
+ if (initial_check(argc, argv))
+ return 1;
+
+ node = argv[1];
+
+ libubi = libubi_open();
+ if (libubi == NULL) {
+ failed("libubi_open");
+ return 1;
+ }
+
+ if (ubi_get_dev_info(libubi, node, &dev_info)) {
+ failed("ubi_get_dev_info");
+ goto close;
+ }
+
+ if (test_static())
+ goto close;
+ if (test_read(UBI_DYNAMIC_VOLUME))
+ goto close;
+ if (test_read(UBI_STATIC_VOLUME))
+ goto close;
+
+ libubi_close(libubi);
+ return 0;
+
+close:
+ libubi_close(libubi);
+ return 1;
+}
+
+/**
+ * test_static - test static volume-specific features.
+ *
+ * Thus function returns %0 in case of success and %-1 in case of failure.
+ */
+static int test_static(void)
+{
+ struct ubi_mkvol_request req;
+ const char *name = TESTNAME ":io_basic()";
+ char vol_node[strlen(UBI_VOLUME_PATTERN) + 100];
+ struct ubi_vol_info vol_info;
+ int fd, ret;
+ char buf[20];
+
+ req.vol_id = UBI_VOL_NUM_AUTO;
+ req.alignment = 1;
+ req.bytes = dev_info.avail_bytes;
+ req.vol_type = UBI_STATIC_VOLUME;
+ req.name = name;
+
+ if (ubi_mkvol(libubi, node, &req)) {
+ failed("ubi_mkvol");
+ return -1;
+ }
+
+ sprintf(&vol_node[0], UBI_VOLUME_PATTERN, dev_info.dev_num, req.vol_id);
+
+ fd = open(vol_node, O_RDWR);
+ if (fd == -1) {
+ failed("open");
+ err_msg("cannot open \"%s\"\n", node);
+ goto remove;
+ }
+
+ if (ubi_get_vol_info(libubi, vol_node, &vol_info)) {
+ failed("ubi_get_vol_info");
+ goto close;
+ }
+
+ /* Make sure new static volume contains no data */
+ if (vol_info.data_bytes != 0) {
+ err_msg("data_bytes = %lld, not zero", vol_info.data_bytes);
+ goto close;
+ }
+
+ /* Ensure read returns EOF */
+ ret = read(fd, &buf[0], 1);
+ if (ret < 0) {
+ failed("read");
+ goto close;
+ }
+ if (ret != 0) {
+ err_msg("read data from free static volume");
+ goto close;
+ }
+
+ if (ubi_update_start(libubi, fd, 10)) {
+ failed("ubi_update_start");
+ goto close;
+ }
+
+ ret = write(fd, &buf[0], 10);
+ if (ret < 0) {
+ failed("write");
+ goto close;
+ }
+ if (ret != 10) {
+ err_msg("written %d bytes", ret);
+ goto close;
+ }
+
+ if (lseek(fd, 0, SEEK_SET) != 0) {
+ failed("seek");
+ goto close;
+ }
+ ret = read(fd, &buf[0], 20);
+ if (ret < 0) {
+ failed("read");
+ goto close;
+ }
+ if (ret != 10) {
+ err_msg("read %d bytes", ret);
+ goto close;
+ }
+
+ close(fd);
+ if (ubi_rmvol(libubi, node, req.vol_id)) {
+ failed("ubi_rmvol");
+ return -1;
+ }
+
+ return 0;
+
+close:
+ close(fd);
+remove:
+ ubi_rmvol(libubi, node, req.vol_id);
+ return -1;
+}
+
+static int test_read1(struct ubi_vol_info *vol_info);
+
+/**
+ * test_read - test UBI volume reading from different offsets.
+ *
+ * @type volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME)
+ *
+ * Thus function returns %0 in case of success and %-1 in case of failure.
+ */
+static int test_read(int type)
+{
+ const char *name = TESTNAME ":test_read()";
+ int alignments[] = ALIGNMENTS(dev_info.eb_size);
+ char vol_node[strlen(UBI_VOLUME_PATTERN) + 100];
+ struct ubi_mkvol_request req;
+ int i;
+
+ for (i = 0; i < sizeof(alignments)/sizeof(int); i++) {
+ int eb_size;
+ struct ubi_vol_info vol_info;
+
+ req.vol_id = UBI_VOL_NUM_AUTO;
+ req.vol_type = type;
+ req.name = name;
+
+ req.alignment = alignments[i];
+ req.alignment -= req.alignment % dev_info.min_io_size;
+ if (req.alignment == 0)
+ req.alignment = dev_info.min_io_size;
+
+ eb_size = dev_info.eb_size - dev_info.eb_size % req.alignment;
+ req.bytes = MIN_AVAIL_EBS * eb_size;
+
+ if (ubi_mkvol(libubi, node, &req)) {
+ failed("ubi_mkvol");
+ return -1;
+ }
+
+ sprintf(&vol_node[0], UBI_VOLUME_PATTERN, dev_info.dev_num,
+ req.vol_id);
+
+ if (ubi_get_vol_info(libubi, vol_node, &vol_info)) {
+ failed("ubi_get_vol_info");
+ goto remove;
+ }
+
+ if (test_read1(&vol_info)) {
+ err_msg("alignment = %d", req.alignment);
+ goto remove;
+ }
+
+ if (ubi_rmvol(libubi, node, req.vol_id)) {
+ failed("ubi_rmvol");
+ return -1;
+ }
+ }
+
+ return 0;
+
+remove:
+ ubi_rmvol(libubi, node, req.vol_id);
+ return -1;
+}
+
+static int test_read2(const struct ubi_vol_info *vol_info, int len);
+
+static int fd;
+
+/* Data lengthes to test, @io - minimal I/O unit size, @s - eraseblock size */
+#define LENGTHES(io, s) \
+ {1, (io), (io)+1, 2*(io), 3*(io)-1, 3*(io), \
+ PAGE_SIZE-1, PAGE_SIZE-(io), 2*PAGE_SIZE, 2*PAGE_SIZE-(io), \
+ (s)/2-1, (s)/2, (s)/2+1, (s)-1, (s), (s)+1, 2*(s)-(io), 2*(s), \
+ 2*(s)+(io), 3*(s), 3*(s)+(io)};
+
+/*
+ * A helper function for test_read().
+ */
+static int test_read1(struct ubi_vol_info *vol_info)
+{
+ int i, written = 0;
+ char vol_node[strlen(UBI_VOLUME_PATTERN) + 100];
+ int lengthes[] = LENGTHES(dev_info.min_io_size, vol_info->eb_size);
+
+ sprintf(&vol_node[0], UBI_VOLUME_PATTERN, dev_info.dev_num,
+ vol_info->vol_id);
+
+ fd = open(vol_node, O_RDWR);
+ if (fd == -1) {
+ failed("open");
+ err_msg("cannot open \"%s\"\n", node);
+ return -1;
+ }
+
+ /* Write some pattern to the volume */
+ if (ubi_update_start(libubi, fd, vol_info->rsvd_bytes)) {
+ failed("ubi_update_start");
+ err_msg("bytes = %lld", vol_info->rsvd_bytes);
+ goto close;
+ }
+
+ while (written < vol_info->rsvd_bytes) {
+ int i, ret;
+ unsigned char buf[512];
+
+ for (i = 0; i < 512; i++)
+ buf[i] = (unsigned char)(written + i);
+
+ ret = write(fd, &buf[0], 512);
+ if (ret == -1) {
+ failed("write");
+ err_msg("written = %d, ret = %d", written, ret);
+ goto close;
+ }
+ written += ret;
+ }
+
+ close(fd);
+
+ if (ubi_get_vol_info(libubi, vol_node, vol_info)) {
+ failed("ubi_get_vol_info");
+ return -1;
+ }
+
+ fd = open(vol_node, O_RDONLY);
+ if (fd == -1) {
+ failed("open");
+ err_msg("cannot open \"%s\"\n", node);
+ return -1;
+ }
+
+ for (i = 0; i < sizeof(lengthes)/sizeof(int); i++) {
+ if (test_read2(vol_info, lengthes[i])) {
+ err_msg("length = %d", lengthes[i]);
+ goto close;
+ }
+ }
+
+ close(fd);
+ return 0;
+
+close:
+ close(fd);
+ return -1;
+}
+
+static int test_read3(const struct ubi_vol_info *vol_info, int len, off_t off);
+
+/*
+ * Offsets to test, @io - minimal I/O unit size, @s - eraseblock size, @sz -
+ * volume size.
+ */
+#define OFFSETS(io, s, sz) \
+ {0, (io)-1, (io), (io)+1, 2*(io)-1, 2*(io), 3*(io)-1, 3*(io), \
+ PAGE_SIZE-1, PAGE_SIZE-(io), 2*PAGE_SIZE, 2*PAGE_SIZE-(io), \
+ (s)/2-1, (s)/2, (s)/2+1, (s)-1, (s), (s)+1, 2*(s)-(io), 2*(s), \
+ 2*(s)+(io), 3*(s), (sz)-(s)-1, (sz)-(io)-1, (sz)-PAGE_SIZE-1};
+
+/*
+ * A helper function for test_read1().
+ */
+static int test_read2(const struct ubi_vol_info *vol_info, int len)
+{
+ int i;
+ off_t offsets[] = OFFSETS(dev_info.min_io_size, vol_info->eb_size,
+ vol_info->data_bytes);
+
+ for (i = 0; i < sizeof(offsets)/sizeof(off_t); i++) {
+ if (test_read3(vol_info, len, offsets[i])) {
+ err_msg("offset = %d", offsets[i]);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * A helper function for test_read2().
+ */
+static int test_read3(const struct ubi_vol_info *vol_info, int len, off_t off)
+{
+ int i, len1;
+ unsigned char ck_buf[len], buf[len];
+ off_t new_off;
+
+ if (off + len > vol_info->data_bytes)
+ len1 = vol_info->data_bytes - off;
+ else
+ len1 = len;
+
+ if (lseek(fd, off, SEEK_SET) != off) {
+ failed("seek");
+ err_msg("len = %d", len);
+ return -1;
+ }
+ if (read(fd, &buf[0], len) != len1) {
+ failed("read");
+ err_msg("len = %d", len);
+ return -1;
+ }
+
+ new_off = lseek(fd, 0, SEEK_CUR);
+ if (new_off != off + len1) {
+ if (new_off == -1)
+ failed("lseek");
+ else
+ err_msg("read %d bytes from %lld, but resulting "
+ "offset is %lld", len1, (long long) off, (long long) new_off);
+ return -1;
+ }
+
+ for (i = 0; i < len1; i++)
+ ck_buf[i] = (unsigned char)(off + i);
+
+ if (memcmp(&buf[0], &ck_buf[0], len1)) {
+ err_msg("incorrect data read from offset %lld",
+ (long long)off);
+ err_msg("len = %d", len);
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/ubi-utils/tests/io_update.c b/ubi-utils/tests/io_update.c
new file mode 100644
index 0000000..2e3422a
--- /dev/null
+++ b/ubi-utils/tests/io_update.c
@@ -0,0 +1,370 @@
+/*
+ * 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.
+ *
+ * Author: Artem B. Bityutskiy
+ *
+ * Test UBI volume update.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "libubi.h"
+#define TESTNAME "io_update"
+#include "common.h"
+
+static libubi_t libubi;
+static struct ubi_dev_info dev_info;
+const char *node;
+
+static int test_update(int type);
+static int test_update_ff(void);
+
+int main(int argc, char * const argv[])
+{
+ if (initial_check(argc, argv))
+ return 1;
+
+ node = argv[1];
+
+ libubi = libubi_open();
+ if (libubi == NULL) {
+ failed("libubi_open");
+ return 1;
+ }
+
+ if (ubi_get_dev_info(libubi, node, &dev_info)) {
+ failed("ubi_get_dev_info");
+ goto close;
+ }
+
+ if (test_update(UBI_DYNAMIC_VOLUME))
+ goto close;
+ if (test_update(UBI_STATIC_VOLUME))
+ goto close;
+ if (test_update_ff())
+ goto close;
+
+ libubi_close(libubi);
+ return 0;
+
+close:
+ libubi_close(libubi);
+ return 1;
+}
+
+static int test_update1(struct ubi_vol_info *vol_info);
+
+/**
+ * test_update - check volume update capabilities.
+ *
+ * @type volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME)
+ *
+ * This function returns %0 in case of success and %-1 in case of failure.
+ */
+static int test_update(int type)
+{
+ struct ubi_mkvol_request req;
+ const char *name = TESTNAME ":io_update()";
+ int alignments[] = ALIGNMENTS(dev_info.eb_size);
+ struct ubi_vol_info vol_info;
+ char vol_node[strlen(UBI_VOLUME_PATTERN) + 100];
+ int i;
+
+ for (i = 0; i < sizeof(alignments)/sizeof(int); i++) {
+ int eb_size;
+
+ req.vol_id = UBI_VOL_NUM_AUTO;
+ req.vol_type = type;
+ req.name = name;
+
+ req.alignment = alignments[i];
+ req.alignment -= req.alignment % dev_info.min_io_size;
+ if (req.alignment == 0)
+ req.alignment = dev_info.min_io_size;
+
+ eb_size = dev_info.eb_size - dev_info.eb_size % req.alignment;
+ req.bytes = MIN_AVAIL_EBS * eb_size;
+
+ if (ubi_mkvol(libubi, node, &req)) {
+ failed("ubi_mkvol");
+ return -1;
+ }
+
+ sprintf(&vol_node[0], UBI_VOLUME_PATTERN, dev_info.dev_num,
+ req.vol_id);
+ if (ubi_get_vol_info(libubi, vol_node, &vol_info)) {
+ failed("ubi_get_vol_info");
+ goto remove;
+ }
+
+ if (test_update1(&vol_info)) {
+ err_msg("alignment = %d", req.alignment);
+ goto remove;
+ }
+
+ if (ubi_rmvol(libubi, node, req.vol_id)) {
+ failed("ubi_rmvol");
+ return -1;
+ }
+ }
+
+ return 0;
+
+remove:
+ ubi_rmvol(libubi, node, req.vol_id);
+ return -1;
+}
+
+#define SEQUENCES(io, s) { \
+ {3*(s)-(io)-1, 1}, \
+ {512}, \
+ {666}, \
+ {2048}, \
+ {(io), (io), PAGE_SIZE}, \
+ {(io)+1, (io)+1, PAGE_SIZE}, \
+ {PAGE_SIZE}, \
+ {PAGE_SIZE-1}, \
+ {PAGE_SIZE+(io)}, \
+ {(s)}, \
+ {(s)-1}, \
+ {(s)+1}, \
+ {(io), (s)+1}, \
+ {(s)+(io), PAGE_SIZE}, \
+ {2*(s), PAGE_SIZE}, \
+ {PAGE_SIZE, 2*(s), 1}, \
+ {PAGE_SIZE, 2*(s)}, \
+ {2*(s)-1, 2*(s)-1}, \
+ {3*(s), PAGE_SIZE + 1}, \
+ {1, PAGE_SIZE}, \
+ {(io), (s)} \
+}
+#define SEQ_SZ 21
+
+/*
+ * test_update1 - helper function for test_update().
+ */
+static int test_update1(struct ubi_vol_info *vol_info)
+{
+ int sequences[SEQ_SZ][3] = SEQUENCES(dev_info.min_io_size,
+ vol_info->eb_size);
+ char vol_node[strlen(UBI_VOLUME_PATTERN) + 100];
+ unsigned char buf[vol_info->rsvd_bytes];
+ int fd, i, j;
+
+ sprintf(&vol_node[0], UBI_VOLUME_PATTERN, dev_info.dev_num,
+ vol_info->vol_id);
+
+ for (i = 0; i < vol_info->rsvd_bytes; i++)
+ buf[i] = (unsigned char)i;
+
+ fd = open(vol_node, O_RDWR);
+ if (fd == -1) {
+ failed("open");
+ err_msg("cannot open \"%s\"\n", node);
+ return -1;
+ }
+
+ for (i = 0; i < SEQ_SZ; i++) {
+ int ret, stop = 0, len;
+ off_t off = 0;
+ unsigned char buf1[vol_info->rsvd_bytes];
+
+ if (ubi_update_start(libubi, fd, vol_info->rsvd_bytes)) {
+ failed("ubi_update_start");
+ goto close;
+ }
+
+ for (j = 0; off < vol_info->rsvd_bytes; j++) {
+ if (!stop) {
+ if (sequences[i][j] != 0)
+ len = sequences[i][j];
+ else
+ stop = 1;
+ }
+
+ ret = write(fd, &buf[off], len);
+ if (ret < 0) {
+ failed("write");
+ err_msg("failed to write %d bytes at offset "
+ "%lld", len, (long long) off);
+ goto close;
+ }
+ if (off + len > vol_info->rsvd_bytes)
+ len = vol_info->rsvd_bytes - off;
+ if (ret != len) {
+ err_msg("failed to write %d bytes at offset "
+ "%lld, wrote %d", len, (long long) off, ret);
+ goto close;
+ }
+ off += len;
+ }
+
+ /* Check data */
+ if ((ret = lseek(fd, SEEK_SET, 0)) != 0) {
+ if (ret < 0)
+ failed("lseek");
+ err_msg("cannot seek to 0");
+ goto close;
+ }
+ memset(&buf1[0], 0x01, vol_info->rsvd_bytes);
+ ret = read(fd, &buf1[0], vol_info->rsvd_bytes + 1);
+ if (ret < 0) {
+ failed("read");
+ err_msg("failed to read %d bytes",
+ vol_info->rsvd_bytes + 1);
+ goto close;
+ }
+ if (ret != vol_info->rsvd_bytes) {
+ err_msg("failed to read %d bytes, read %d",
+ vol_info->rsvd_bytes, ret);
+ goto close;
+ }
+ if (memcmp(&buf[0], &buf1[0], vol_info->rsvd_bytes)) {
+ err_msg("data corruption");
+ goto close;
+ }
+ }
+
+ close(fd);
+ return 0;
+
+close:
+ close(fd);
+ return -1;
+}
+
+/**
+ * test_update_ff - check volume with 0xFF data
+ *
+ * This function returns %0 in case of success and %-1 in case of failure.
+ */
+static int test_update_ff(void)
+{
+ struct ubi_mkvol_request req;
+ const char *name = TESTNAME ":io_update()";
+ struct ubi_vol_info vol_info;
+ char vol_node[strlen(UBI_VOLUME_PATTERN) + 100];
+ int i, fd, ret, types[2];
+ int upd_len = MIN_AVAIL_EBS * dev_info.eb_size;
+ char buf[upd_len], buf1[upd_len];
+
+ for(i = 0; i < MIN_AVAIL_EBS; i++) {
+ if (i % 1)
+ memset(&buf[0], 0xAB, upd_len);
+ else
+ memset(&buf[0], 0xFF, upd_len);
+ }
+
+ types[0] = UBI_DYNAMIC_VOLUME;
+ types[1] = UBI_STATIC_VOLUME;
+
+ for (i = 0; i < 2; i++) {
+ req.vol_id = UBI_VOL_NUM_AUTO;
+ req.vol_type = types[i];
+ req.name = name;
+
+ req.alignment = 1;
+ req.bytes = upd_len;
+
+ if (ubi_mkvol(libubi, node, &req)) {
+ failed("ubi_mkvol");
+ return -1;
+ }
+
+ sprintf(&vol_node[0], UBI_VOLUME_PATTERN, dev_info.dev_num,
+ req.vol_id);
+ if (ubi_get_vol_info(libubi, vol_node, &vol_info)) {
+ failed("ubi_get_vol_info");
+ goto remove;
+ }
+
+ fd = open(vol_node, O_RDWR);
+ if (fd == -1) {
+ failed("open");
+ err_msg("cannot open \"%s\"\n", node);
+ goto remove;
+ }
+
+ if (ubi_update_start(libubi, fd, upd_len)) {
+ failed("ubi_update_start");
+ goto close;
+ }
+
+
+ ret = write(fd, &buf[0], upd_len);
+ if (ret < 0 || ret != upd_len) {
+ failed("write");
+ err_msg("failed to write %d bytes", upd_len);
+ goto close;
+ }
+
+ /* Check data */
+ if ((ret = lseek(fd, SEEK_SET, 0)) != 0) {
+ if (ret < 0)
+ failed("lseek");
+ err_msg("cannot seek to 0");
+ goto close;
+ }
+
+ close(fd);
+
+ fd = open(vol_node, O_RDWR);
+ if (fd == -1) {
+ failed("open");
+ err_msg("cannot open \"%s\"\n", node);
+ goto remove;
+ }
+
+ memset(&buf1[0], 0x00, upd_len);
+ ret = read(fd, &buf1[0], upd_len);
+ if (ret < 0) {
+ failed("read");
+ err_msg("failed to read %d bytes", upd_len);
+ goto close;
+ }
+ if (ret != upd_len) {
+ err_msg("failed to read %d bytes, read %d",
+ upd_len, ret);
+ goto close;
+ }
+ if (memcmp(&buf[0], &buf1[0], upd_len)) {
+ err_msg("data corruption");
+ goto close;
+ }
+
+ close(fd);
+
+ if (ubi_rmvol(libubi, node, req.vol_id)) {
+ failed("ubi_rmvol");
+ return -1;
+ }
+ }
+
+ return 0;
+
+close:
+ close(fd);
+remove:
+ ubi_rmvol(libubi, node, req.vol_id);
+ return -1;
+}
diff --git a/ubi-utils/tests/mkvol_bad.c b/ubi-utils/tests/mkvol_bad.c
new file mode 100644
index 0000000..58ac4e1
--- /dev/null
+++ b/ubi-utils/tests/mkvol_bad.c
@@ -0,0 +1,311 @@
+/*
+ * 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.
+ *
+ * Author: Artem B. Bityutskiy
+ *
+ * Test UBI volume creation and deletion ioctl()s with bad input and in case of
+ * incorrect usage.
+ */
+
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+#include "libubi.h"
+#define TESTNAME "mkvol_bad"
+#include "common.h"
+
+static libubi_t libubi;
+static struct ubi_dev_info dev_info;
+const char *node;
+
+static int test_mkvol(void);
+static int test_rmvol(void);
+
+int main(int argc, char * const argv[])
+{
+ if (initial_check(argc, argv))
+ return 1;
+
+ node = argv[1];
+
+ libubi = libubi_open();
+ if (libubi == NULL) {
+ failed("libubi_open");
+ return 1;
+ }
+
+ if (ubi_get_dev_info(libubi, node, &dev_info)) {
+ failed("ubi_get_dev_info");
+ goto close;
+ }
+
+ if (test_mkvol())
+ goto close;
+
+ if (test_rmvol())
+ goto close;
+
+ libubi_close(libubi);
+ return 0;
+
+close:
+ libubi_close(libubi);
+ return 1;
+}
+
+/**
+ * test_mkvol - test that UBI mkvol ioctl rejects bad input parameters.
+ *
+ * This function returns %0 if the test passed and %-1 if not.
+ */
+static int test_mkvol(void)
+{
+ int ret, i;
+ struct ubi_mkvol_request req;
+ const char *name = TESTNAME ":test_mkvol()";
+
+ req.alignment = 1;
+ req.bytes = dev_info.avail_bytes;
+ req.vol_type = UBI_DYNAMIC_VOLUME;
+ req.name = name;
+
+ /* Bad volume ID */
+ req.vol_id = -2;
+ ret = ubi_mkvol(libubi, node, &req);
+ if (check_failed(ret, EINVAL, "ubi_mkvol", "vol_id = %d", req.vol_id))
+ return -1;
+
+ req.vol_id = dev_info.max_vol_count;
+ ret = ubi_mkvol(libubi, node, &req);
+ if (check_failed(ret, EINVAL, "ubi_mkvol", "vol_id = %d", req.vol_id))
+ return -1;
+
+ /* Bad alignment */
+ req.vol_id = 0;
+ req.alignment = 0;
+ ret = ubi_mkvol(libubi, node, &req);
+ if (check_failed(ret, EINVAL, "ubi_mkvol", "alignment = %d",
+ req.alignment))
+ return -1;
+
+ req.alignment = -1;
+ ret = ubi_mkvol(libubi, node, &req);
+ if (check_failed(ret, EINVAL, "ubi_mkvol", "alignment = %d",
+ req.alignment))
+ return -1;
+
+ req.alignment = dev_info.eb_size + 1;
+ ret = ubi_mkvol(libubi, node, &req);
+ if (check_failed(ret, EINVAL, "ubi_mkvol", "alignment = %d",
+ req.alignment))
+ return -1;
+
+ if (dev_info.min_io_size > 1) {
+ req.alignment = dev_info.min_io_size + 1;
+ ret = ubi_mkvol(libubi, node, &req);
+ if (check_failed(ret, EINVAL, "ubi_mkvol", "alignment = %d",
+ req.alignment))
+ return -1;
+ }
+
+ /* Bad bytes */
+ req.alignment = 1;
+ req.bytes = -1;
+ ret = ubi_mkvol(libubi, node, &req);
+ if (check_failed(ret, EINVAL, "ubi_mkvol", "bytes = %lld", req.bytes))
+ return -1;
+
+ req.bytes = 0;
+ ret = ubi_mkvol(libubi, node, &req);
+ if (check_failed(ret, EINVAL, "ubi_mkvol", "bytes = %lld", req.bytes))
+ return -1;
+
+ req.bytes = dev_info.avail_bytes + 1;
+ ret = ubi_mkvol(libubi, node, &req);
+ if (check_failed(ret, ENOSPC, "ubi_mkvol", "bytes = %lld", req.bytes))
+ return -1;
+
+ req.alignment = dev_info.eb_size - dev_info.min_io_size;
+ req.bytes = (dev_info.eb_size - dev_info.eb_size % req.alignment) *
+ dev_info.avail_ebs + 1;
+ ret = ubi_mkvol(libubi, node, &req);
+ if (check_failed(ret, ENOSPC, "ubi_mkvol", "bytes = %lld", req.bytes))
+ return -1;
+
+ /* Bad vol_type */
+ req.alignment = 1;
+ req.bytes = dev_info.avail_bytes;
+ req.vol_type = UBI_DYNAMIC_VOLUME + UBI_STATIC_VOLUME;
+ ret = ubi_mkvol(libubi, node, &req);
+ if (check_failed(ret, EINVAL, "ubi_mkvol", "vol_type = %d",
+ req.vol_type))
+ return -1;
+
+ req.vol_type = UBI_DYNAMIC_VOLUME;
+
+ /* Too long name */
+ {
+ char name[UBI_VOL_NAME_MAX + 5];
+
+ memset(&name[0], 'x', UBI_VOL_NAME_MAX + 1);
+ name[UBI_VOL_NAME_MAX + 1] = '\0';
+
+ req.name = &name[0];
+ ret = ubi_mkvol(libubi, node, &req);
+ if (check_failed(ret, EINVAL, "ubi_mkvol", "name_len = %d",
+ UBI_VOL_NAME_MAX + 1))
+ return -1;
+ }
+
+ /* Try to create 2 volumes with the same ID and name */
+ req.name = name;
+ req.vol_id = 0;
+ if (ubi_mkvol(libubi, node, &req)) {
+ failed("ubi_mkvol");
+ return -1;
+ }
+
+ ret = ubi_mkvol(libubi, node, &req);
+ if (check_failed(ret, EEXIST, "ubi_mkvol",
+ "volume with ID 0 created twice"))
+ return -1;
+
+ req.vol_id = 1;
+ ret = ubi_mkvol(libubi, node, &req);
+ if (check_failed(ret, EEXIST, "ubi_mkvol",
+ "volume with name \"%s\" created twice", name))
+ return -1;
+
+ if (ubi_rmvol(libubi, node, 0)) {
+ failed("ubi_rmvol");
+ return -1;
+ }
+
+ /* Try to use too much space */
+ req.vol_id = 0;
+ req.bytes = dev_info.avail_bytes;
+ if (ubi_mkvol(libubi, node, &req)) {
+ failed("ubi_mkvol");
+ return -1;
+ }
+
+ req.bytes = 1;
+ req.vol_id = 1;
+ ret = ubi_mkvol(libubi, node, &req);
+ if (check_failed(ret, EEXIST, "ubi_mkvol",
+ "created volume of maximum size %lld, but still "
+ "can create more volumes", dev_info.avail_bytes))
+ return -1;
+
+ if (ubi_rmvol(libubi, node, 0)) {
+ failed("ubi_rmvol");
+ return -1;
+ }
+
+ /* Try to create too many volumes */
+ for (i = 0; i < dev_info.max_vol_count; i++) {
+ char nm[strlen(name) + 50];
+
+ req.vol_id = UBI_VOL_NUM_AUTO;
+ req.alignment = 1;
+ req.bytes = 1;
+ req.vol_type = UBI_STATIC_VOLUME;
+
+ sprintf(&nm[0], "%s:%d", name, i);
+ req.name = &nm[0];
+
+ if (ubi_mkvol(libubi, node, &req)) {
+ failed("ubi_mkvol");
+ err_msg("vol_id %d", i);
+ goto remove;
+ }
+ }
+
+ req.vol_id = UBI_VOL_NUM_AUTO;
+ req.name = TESTNAME ":impossible";
+ ret = ubi_mkvol(libubi, node, &req);
+ if (check_failed(ret, ENOSPC, "ubi_mkvol", "volume %d created",
+ req.vol_id))
+ goto remove;
+
+ req.vol_id = dev_info.max_vol_count;
+ ret = ubi_mkvol(libubi, node, &req);
+ if (check_failed(ret, EINVAL, "ubi_mkvol", "volume %d created",
+ req.vol_id))
+ goto remove;
+
+ for (i = 0; i < dev_info.max_vol_count + 1; i++)
+ ubi_rmvol(libubi, node, i);
+
+ return 0;
+
+remove:
+ for (i = 0; i < dev_info.max_vol_count + 1; i++)
+ ubi_rmvol(libubi, node, i);
+ return -1;
+}
+
+/**
+ * test_rmvol - test that UBI rmvol ioctl rejects bad input parameters.
+ *
+ * This function returns %0 if the test passed and %-1 if not.
+ */
+static int test_rmvol(void)
+{
+ int ret;
+ struct ubi_mkvol_request req;
+ const char *name = TESTNAME ":test_rmvol()";
+
+ /* Bad vol_id */
+ ret = ubi_rmvol(libubi, node, -1);
+ if (check_failed(ret, EINVAL, "ubi_rmvol", "vol_id = -1"))
+ return -1;
+
+ ret = ubi_rmvol(libubi, node, dev_info.max_vol_count);
+ if (check_failed(ret, EINVAL, "ubi_rmvol", "vol_id = %d",
+ dev_info.max_vol_count))
+ return -1;
+
+ /* Try to remove non-existing volume */
+ ret = ubi_rmvol(libubi, node, 0);
+ if (check_failed(ret, ENODEV, "ubi_rmvol",
+ "removed non-existing volume 0"))
+ return -1;
+
+ /* Try to remove volume twice */
+ req.vol_id = UBI_VOL_NUM_AUTO;
+ req.alignment = 1;
+ req.bytes = dev_info.avail_bytes;
+ req.vol_type = UBI_DYNAMIC_VOLUME;
+ req.name = name;
+ if (ubi_mkvol(libubi, node, &req)) {
+ failed("ubi_mkvol");
+ return -1;
+ }
+
+ if (ubi_rmvol(libubi, node, req.vol_id)) {
+ failed("ubi_rmvol");
+ return -1;
+ }
+
+ ret = ubi_rmvol(libubi, node, req.vol_id);
+ if (check_failed(ret, ENODEV, "ubi_rmvol", "volume %d removed twice",
+ req.vol_id))
+ return -1;
+
+ return 0;
+}
diff --git a/ubi-utils/tests/mkvol_basic.c b/ubi-utils/tests/mkvol_basic.c
new file mode 100644
index 0000000..2c6a512
--- /dev/null
+++ b/ubi-utils/tests/mkvol_basic.c
@@ -0,0 +1,249 @@
+/*
+ * 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.
+ *
+ * Author: Artem B. Bityutskiy
+ *
+ * Test test checks basic volume creation and deletion capabilities.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include "libubi.h"
+#define TESTNAME "mkvol_basic"
+#include "common.h"
+
+static libubi_t libubi;
+static struct ubi_dev_info dev_info;
+const char *node;
+
+static int mkvol_basic(void);
+static int mkvol_alignment(void);
+static int mkvol_multiple(void);
+
+int main(int argc, char * const argv[])
+{
+ if (initial_check(argc, argv))
+ return 1;
+
+ node = argv[1];
+
+ libubi = libubi_open();
+ if (libubi == NULL) {
+ failed("libubi_open");
+ return 1;
+ }
+
+ if (ubi_get_dev_info(libubi, node, &dev_info)) {
+ failed("ubi_get_dev_info");
+ goto close;
+ }
+
+ if (mkvol_basic())
+ goto close;
+
+ if (mkvol_alignment())
+ goto close;
+
+ if (mkvol_multiple())
+ goto close;
+
+ libubi_close(libubi);
+ return 0;
+
+close:
+ libubi_close(libubi);
+ return 1;
+}
+
+/**
+ * mkvol_alignment - create volumes with different alignments.
+ *
+ * Thus function returns %0 in case of success and %-1 in case of failure.
+ */
+static int mkvol_alignment(void)
+{
+ struct ubi_mkvol_request req;
+ int i, vol_id, ebsz;
+ const char *name = TESTNAME ":mkvol_alignment()";
+ int alignments[] = ALIGNMENTS(dev_info.eb_size);
+
+ for (i = 0; i < sizeof(alignments)/sizeof(int); i++) {
+ req.vol_id = UBI_VOL_NUM_AUTO;
+
+ /* Alignment should actually be multiple of min. I/O size */
+ req.alignment = alignments[i];
+ req.alignment -= req.alignment % dev_info.min_io_size;
+ if (req.alignment == 0)
+ req.alignment = dev_info.min_io_size;
+
+ /* Bear in mind alignment reduces EB size */
+ ebsz = dev_info.eb_size - dev_info.eb_size % req.alignment;
+ req.bytes = dev_info.avail_ebs * ebsz;
+
+ req.vol_type = UBI_DYNAMIC_VOLUME;
+ req.name = name;
+
+ if (ubi_mkvol(libubi, node, &req)) {
+ failed("ubi_mkvol");
+ err_msg("alignment %d", req.alignment);
+ return -1;
+ }
+
+ vol_id = req.vol_id;
+ if (check_volume(vol_id, &req))
+ goto remove;
+
+ if (ubi_rmvol(libubi, node, vol_id)) {
+ failed("ubi_rmvol");
+ return -1;
+ }
+ }
+
+ return 0;
+
+remove:
+ ubi_rmvol(libubi, node, vol_id);
+ return -1;
+}
+
+/**
+ * mkvol_basic - simple test that checks basic volume creation capability.
+ *
+ * Thus function returns %0 in case of success and %-1 in case of failure.
+ */
+static int mkvol_basic(void)
+{
+ struct ubi_mkvol_request req;
+ struct ubi_vol_info vol_info;
+ int vol_id, ret;
+ const char *name = TESTNAME ":mkvol_basic()";
+
+ /* Create dynamic volume of maximum size */
+ req.vol_id = UBI_VOL_NUM_AUTO;
+ req.alignment = 1;
+ req.bytes = dev_info.avail_bytes;
+ req.vol_type = UBI_DYNAMIC_VOLUME;
+ req.name = name;
+
+ if (ubi_mkvol(libubi, node, &req)) {
+ failed("ubi_mkvol");
+ return -1;
+ }
+
+ vol_id = req.vol_id;
+ if (check_volume(vol_id, &req))
+ goto remove;
+
+ if (ubi_rmvol(libubi, node, vol_id)) {
+ failed("ubi_rmvol");
+ return -1;
+ }
+
+ /* Create static volume of maximum size */
+ req.vol_id = UBI_VOL_NUM_AUTO;
+ req.alignment = 1;
+ req.bytes = dev_info.avail_bytes;
+ req.vol_type = UBI_STATIC_VOLUME;
+ req.name = name;
+
+ if (ubi_mkvol(libubi, node, &req)) {
+ failed("ubi_mkvol");
+ return -1;
+ }
+
+ vol_id = req.vol_id;
+ if (check_volume(vol_id, &req))
+ goto remove;
+
+ if (ubi_rmvol(libubi, node, vol_id)) {
+ failed("ubi_rmvol");
+ return -1;
+ }
+
+ /* Make sure volume does not exist */
+ ret = ubi_get_vol_info1(libubi, dev_info.dev_num, vol_id, &vol_info);
+ if (ret == 0) {
+ err_msg("removed volume %d exists", vol_id);
+ goto remove;
+ }
+
+ return 0;
+
+remove:
+ ubi_rmvol(libubi, node, vol_id);
+ return -1;
+}
+
+/**
+ * mkvol_multiple - test multiple volumes creation
+ *
+ * Thus function returns %0 if the test passed and %-1 if not.
+ */
+static int mkvol_multiple(void)
+{
+ struct ubi_mkvol_request req;
+ int i, ret;
+ const char *name = TESTNAME ":mkvol_multiple()";
+
+ /* Create maximum number of volumes */
+ for (i = 0; i < dev_info.max_vol_count; i++) {
+ char nm[strlen(name) + 50];
+
+ req.vol_id = UBI_VOL_NUM_AUTO;
+ req.alignment = 1;
+ req.bytes = 1;
+ req.vol_type = UBI_STATIC_VOLUME;
+
+ sprintf(&nm[0], "%s:%d", name, i);
+ req.name = &nm[0];
+
+ if (ubi_mkvol(libubi, node, &req)) {
+ failed("ubi_mkvol");
+ err_msg("vol_id %d", i);
+ goto remove;
+ }
+
+ if (check_volume(req.vol_id, &req)) {
+ err_msg("vol_id %d", i);
+ goto remove;
+ }
+ }
+
+ for (i = 0; i < dev_info.max_vol_count; i++) {
+ struct ubi_vol_info vol_info;
+
+ if (ubi_rmvol(libubi, node, i)) {
+ failed("ubi_rmvol");
+ return -1;
+ }
+
+ /* Make sure volume does not exist */
+ ret = ubi_get_vol_info1(libubi, dev_info.dev_num, i, &vol_info);
+ if (ret == 0) {
+ err_msg("removed volume %d exists", i);
+ goto remove;
+ }
+ }
+
+ return 0;
+
+remove:
+ for (i = 0; i < dev_info.max_vol_count + 1; i++)
+ ubi_rmvol(libubi, node, i);
+ return -1;
+}
diff --git a/ubi-utils/tests/mkvol_paral.c b/ubi-utils/tests/mkvol_paral.c
new file mode 100644
index 0000000..faf085c
--- /dev/null
+++ b/ubi-utils/tests/mkvol_paral.c
@@ -0,0 +1,112 @@
+/*
+ * 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.
+ *
+ * Author: Artem B. Bityutskiy
+ *
+ * This test creates and deletes volumes in parallel.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <pthread.h>
+#include "libubi.h"
+#define TESTNAME "mkvol_paral"
+#include "common.h"
+
+#define THREADS_NUM 4
+#define ITERATIONS 500
+
+static libubi_t libubi;
+static struct ubi_dev_info dev_info;
+const char *node;
+static int iterations = ITERATIONS;
+
+static void * the_thread(void *ptr);
+
+int main(int argc, char * const argv[])
+{
+ int i, ret;
+ pthread_t threads[THREADS_NUM];
+
+ if (initial_check(argc, argv))
+ return 1;
+
+ node = argv[1];
+
+ libubi = libubi_open();
+ if (libubi == NULL) {
+ failed("libubi_open");
+ return 1;
+ }
+
+ if (ubi_get_dev_info(libubi, node, &dev_info)) {
+ failed("ubi_get_dev_info");
+ goto close;
+ }
+
+ for (i = 0; i < THREADS_NUM; i++) {
+ ret = pthread_create(&threads[i], NULL, &the_thread, (void*)i);
+ if (ret) {
+ failed("pthread_create");
+ goto close;
+ }
+ }
+
+ for (i = 0; i < THREADS_NUM; i++)
+ pthread_join(threads[i], NULL);
+
+ libubi_close(libubi);
+ return 0;
+
+close:
+ libubi_close(libubi);
+ return 1;
+}
+
+/**
+ * the_thread - the testing thread.
+ *
+ * @ptr thread number
+ */
+static void * the_thread(void *ptr)
+{
+ int n = (int)ptr, iter = iterations;
+ struct ubi_mkvol_request req;
+ const char *name = TESTNAME ":the_thread()";
+ char nm[strlen(name) + 50];
+
+ req.alignment = 1;
+ req.bytes = dev_info.avail_bytes/ITERATIONS;
+ req.vol_type = UBI_DYNAMIC_VOLUME;
+ sprintf(&nm[0], "%s:%d", name, n);
+ req.name = &nm[0];
+
+ while (iter--) {
+ req.vol_id = UBI_VOL_NUM_AUTO;
+ if (ubi_mkvol(libubi, node, &req)) {
+ failed("ubi_mkvol");
+ return NULL;
+ }
+ if (ubi_rmvol(libubi, node, req.vol_id)) {
+ failed("ubi_rmvol");
+ return NULL;
+ }
+ }
+
+ return NULL;
+}
diff --git a/ubi-utils/tests/rsvol.c b/ubi-utils/tests/rsvol.c
new file mode 100644
index 0000000..51fa069
--- /dev/null
+++ b/ubi-utils/tests/rsvol.c
@@ -0,0 +1,310 @@
+/*
+ * 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.
+ *
+ * Author: Artem B. Bityutskiy
+ *
+ * Tes UBI volume re-size.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "libubi.h"
+#define TESTNAME "rsvol"
+#include "common.h"
+
+static libubi_t libubi;
+static struct ubi_dev_info dev_info;
+const char *node;
+
+static int test_basic(int type);
+static int test_rsvol(int type);
+
+int main(int argc, char * const argv[])
+{
+ if (initial_check(argc, argv))
+ return 1;
+
+ node = argv[1];
+
+ libubi = libubi_open();
+ if (libubi == NULL) {
+ failed("libubi_open");
+ return 1;
+ }
+
+ if (ubi_get_dev_info(libubi, node, &dev_info)) {
+ failed("ubi_get_dev_info");
+ goto close;
+ }
+
+ if (test_basic(UBI_DYNAMIC_VOLUME))
+ goto close;
+ if (test_basic(UBI_STATIC_VOLUME))
+ goto close;
+ if (test_rsvol(UBI_DYNAMIC_VOLUME))
+ goto close;
+ if (test_rsvol(UBI_STATIC_VOLUME))
+ goto close;
+
+ libubi_close(libubi);
+ return 0;
+
+close:
+ libubi_close(libubi);
+ return 1;
+}
+
+/**
+ * test_basic - check volume re-size capability.
+ *
+ * @type volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME)
+ *
+ * Thus function returns %0 in case of success and %-1 in case of failure.
+ */
+static int test_basic(int type)
+{
+ struct ubi_mkvol_request req;
+ const char *name = TESTNAME ":test_basic()";
+
+ req.vol_id = UBI_VOL_NUM_AUTO;
+ req.alignment = 1;
+ req.bytes = MIN_AVAIL_EBS * dev_info.eb_size;
+ req.vol_type = type;
+ req.name = name;
+
+ if (ubi_mkvol(libubi, node, &req)) {
+ failed("ubi_mkvol");
+ return -1;
+ }
+
+ req.bytes = dev_info.eb_size;
+ if (ubi_rsvol(libubi, node, req.vol_id, req.bytes)) {
+ failed("ubi_rsvol");
+ goto remove;
+ }
+
+ if (check_volume(req.vol_id, &req))
+ goto remove;
+
+ req.bytes = (MIN_AVAIL_EBS + 1) * dev_info.eb_size;
+ if (ubi_rsvol(libubi, node, req.vol_id, req.bytes)) {
+ failed("ubi_rsvol");
+ goto remove;
+ }
+
+ if (check_volume(req.vol_id, &req))
+ goto remove;
+
+ req.bytes -= 1;
+ if (ubi_rsvol(libubi, node, req.vol_id, req.bytes)) {
+ failed("ubi_rsvol");
+ goto remove;
+ }
+
+ if (check_volume(req.vol_id, &req))
+ goto remove;
+
+ if (ubi_rmvol(libubi, node, req.vol_id)) {
+ failed("ubi_rmvol");
+ return -1;
+ }
+
+ return 0;
+
+remove:
+ ubi_rmvol(libubi, node, req.vol_id);
+ return -1;
+}
+
+static int test_rsvol1(struct ubi_vol_info *vol_info);
+
+/**
+ * test_rsvol - test UBI volume re-size.
+ *
+ * @type volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME)
+ *
+ * Thus function returns %0 in case of success and %-1 in case of failure.
+ */
+static int test_rsvol(int type)
+{
+ const char *name = TESTNAME "test_rsvol:()";
+ int alignments[] = ALIGNMENTS(dev_info.eb_size);
+ char vol_node[strlen(UBI_VOLUME_PATTERN) + 100];
+ struct ubi_mkvol_request req;
+ int i;
+
+ for (i = 0; i < sizeof(alignments)/sizeof(int); i++) {
+ int eb_size;
+ struct ubi_vol_info vol_info;
+
+ req.vol_id = UBI_VOL_NUM_AUTO;
+ req.vol_type = type;
+ req.name = name;
+
+ req.alignment = alignments[i];
+ req.alignment -= req.alignment % dev_info.min_io_size;
+ if (req.alignment == 0)
+ req.alignment = dev_info.min_io_size;
+
+ eb_size = dev_info.eb_size - dev_info.eb_size % req.alignment;
+ req.bytes = MIN_AVAIL_EBS * eb_size;
+
+ if (ubi_mkvol(libubi, node, &req)) {
+ failed("ubi_mkvol");
+ return -1;
+ }
+
+ sprintf(&vol_node[0], UBI_VOLUME_PATTERN, dev_info.dev_num,
+ req.vol_id);
+
+ if (ubi_get_vol_info(libubi, vol_node, &vol_info)) {
+ failed("ubi_get_vol_info");
+ goto remove;
+ }
+
+ if (test_rsvol1(&vol_info)) {
+ err_msg("alignment = %d", req.alignment);
+ goto remove;
+ }
+
+ if (ubi_rmvol(libubi, node, req.vol_id)) {
+ failed("ubi_rmvol");
+ return -1;
+ }
+ }
+
+ return 0;
+
+remove:
+ ubi_rmvol(libubi, node, req.vol_id);
+ return -1;
+}
+
+/*
+ * Helper function for test_rsvol().
+ */
+static int test_rsvol1(struct ubi_vol_info *vol_info)
+{
+ long long bytes;
+ struct ubi_vol_info vol_info1;
+ char vol_node[strlen(UBI_VOLUME_PATTERN) + 100];
+ unsigned char buf[vol_info->rsvd_bytes];
+ int fd, i, ret;
+
+ /* Make the volume smaller and check basic volume I/O */
+ bytes = vol_info->rsvd_bytes - vol_info->eb_size;
+ if (ubi_rsvol(libubi, node, vol_info->vol_id, bytes - 1)) {
+ failed("ubi_rsvol");
+ return -1;
+ }
+
+ if (ubi_get_vol_info1(libubi, vol_info->dev_num, vol_info->vol_id,
+ &vol_info1)) {
+ failed("ubi_get_vol_info");
+ return -1;
+ }
+
+ if (vol_info1.rsvd_bytes != bytes) {
+ err_msg("rsvd_bytes %lld, must be %lld",
+ vol_info1.rsvd_bytes, bytes);
+ return -1;
+ }
+
+ if (vol_info1.rsvd_ebs != vol_info->rsvd_ebs - 1) {
+ err_msg("rsvd_ebs %d, must be %d",
+ vol_info1.rsvd_ebs, vol_info->rsvd_ebs - 1);
+ return -1;
+ }
+
+ /* Write data to the volume */
+ sprintf(&vol_node[0], UBI_VOLUME_PATTERN, dev_info.dev_num,
+ vol_info->vol_id);
+
+ fd = open(vol_node, O_RDWR);
+ if (fd == -1) {
+ failed("open");
+ err_msg("cannot open \"%s\"\n", node);
+ return -1;
+ }
+
+ bytes = vol_info->rsvd_bytes - vol_info->eb_size - 1;
+ if (ubi_update_start(libubi, fd, bytes)) {
+ failed("ubi_update_start");
+ goto close;
+ }
+
+ for (i = 0; i < bytes; i++)
+ buf[i] = (unsigned char)i;
+
+ ret = write(fd, &buf[0], bytes);
+ if (ret != bytes) {
+ failed("write");
+ goto close;
+ }
+
+ close(fd);
+
+ if (ubi_rsvol(libubi, node, vol_info->vol_id, bytes)) {
+ failed("ubi_rsvol");
+ return -1;
+ }
+
+ if (ubi_rsvol(libubi, node, vol_info->vol_id,
+ vol_info->eb_size * dev_info.avail_ebs)) {
+ failed("ubi_rsvol");
+ return -1;
+ }
+
+ fd = open(vol_node, O_RDWR);
+ if (fd == -1) {
+ failed("open");
+ err_msg("cannot open \"%s\"\n", node);
+ return -1;
+ }
+
+ /* Read data back */
+ if (lseek(fd, 0, SEEK_SET) != 0) {
+ failed("seek");
+ goto close;
+ }
+ memset(&buf[0], 0, bytes);
+ ret = read(fd, &buf[0], bytes);
+ if (ret != bytes) {
+ failed("read");
+ goto close;
+ }
+
+ for (i = 0; i < bytes; i++) {
+ if (buf[i] != (unsigned char)i) {
+ err_msg("bad data");
+ goto close;
+ }
+ }
+
+ close(fd);
+ return 0;
+
+close:
+ close(fd);
+ return -1;
+}
diff --git a/ubi-utils/tests/runtests.pl b/ubi-utils/tests/runtests.pl
new file mode 100755
index 0000000..8005716
--- /dev/null
+++ b/ubi-utils/tests/runtests.pl
@@ -0,0 +1,30 @@
+#!/usr/bin/perl -w
+
+sub usage;
+
+my @tests = ("mkvol_basic", "mkvol_bad", "mkvol_paral", "rsvol",
+ "io_basic", "io_read", "io_update", "io_paral");
+
+if (not defined @ARGV) {
+ usage();
+ exit;
+}
+
+foreach (@ARGV) {
+ -c or die "Error: $_ is not character device\n";
+}
+
+my $dev;
+foreach $dev (@ARGV) {
+ foreach (@tests) {
+ print "Running: $_ $dev";
+ system "./$_ $dev" and die;
+ print "\tSUCCESS\n";
+ }
+}
+
+sub usage
+{
+ print "Usage:\n";
+ print "$0 <UBI device 1> <UBI device 2> ...\n";
+}
diff --git a/ubi-utils/tests/runtests.sh b/ubi-utils/tests/runtests.sh
new file mode 100755
index 0000000..f993bc0
--- /dev/null
+++ b/ubi-utils/tests/runtests.sh
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+ubidev="$1"
+ubiloadcmd="$2"
+tests="mkvol_basic mkvol_bad mkvol_paral rsvol io_basic io_read io_update io_paral"
+
+if test -z "$ubidev" || test -z "$ubiloadcmd";
+then
+ echo "Usage:"
+ echo "$0 <UBI device> <ubi module load command>"
+ exit 1
+fi
+
+ubiname=`echo $ubidev | cut -d/ -f3`
+
+major=`cat /sys/class/ubi/$ubiname/dev | cut -d: -f1`
+
+for minor in `seq 0 4`; do
+ if test ! -e ${ubidev}_${minor} ;
+ then
+ mknod ${ubidev}_${minor} c $major $(($minor + 1))
+ fi
+done
+
+if ! test -c "$ubidev";
+then
+ echo "Error: $ubidev is not character device"
+ exit 1
+fi
+
+for t in `echo $tests`;
+do
+ echo "Running $t $ubidev"
+ "./$t" "$ubidev" || exit 1
+done
+
+./integ "$ubiloadcmd" || exit 1
+
+echo SUCCESS
+
+exit 0