From f175083413f0f94de88def865eeb65e465ded389 Mon Sep 17 00:00:00 2001 From: Frank Haverkamp Date: Wed, 14 Jun 2006 11:53:59 +0200 Subject: 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. Signed-off-by: Frank Haverkamp --- ubi-utils/Makefile | 79 +++ ubi-utils/Makefile.am | 96 +++ ubi-utils/README | 236 ++++++++ ubi-utils/UBI.TXT | 108 ++++ ubi-utils/bootstrap | 15 + ubi-utils/configure.ac | 52 ++ ubi-utils/inc/Makefile.am | 5 + ubi-utils/inc/bootenv.h | 415 +++++++++++++ ubi-utils/inc/config-h.in | 74 +++ ubi-utils/inc/crc32.h | 36 ++ ubi-utils/inc/error.h | 84 +++ ubi-utils/inc/example_ubi.h | 28 + ubi-utils/inc/libubi.h | 310 ++++++++++ ubi-utils/inc/list.h | 56 ++ ubi-utils/inc/nandecc.h | 28 + ubi-utils/inc/peb.h | 41 ++ ubi-utils/inc/pfi.h | 244 ++++++++ ubi-utils/inc/pfiflash.h | 62 ++ ubi-utils/inc/reader.h | 84 +++ ubi-utils/inc/ubigen.h | 149 +++++ ubi-utils/inc/ubimirror.h | 66 ++ ubi-utils/lib/Makefile.am | 58 ++ ubi-utils/scripts/Makefile | 57 ++ ubi-utils/scripts/README | 11 + ubi-utils/scripts/TODO | 5 + ubi-utils/scripts/jffs2_test.sh | 91 +++ ubi-utils/scripts/test.cfg | 23 + ubi-utils/scripts/ubi_test.sh | 318 ++++++++++ ubi-utils/scripts/ubi_tools_test.sh | 244 ++++++++ ubi-utils/src/bin2nand/bin2nand.c | 343 +++++++++++ ubi-utils/src/bin2nand/nandecc.c | 159 +++++ ubi-utils/src/bin2nand/nandecc.h | 11 + ubi-utils/src/libbootenv/bootenv.c | 959 ++++++++++++++++++++++++++++++ ubi-utils/src/libbootenv/hashmap.c | 412 +++++++++++++ ubi-utils/src/libbootenv/hashmap.h | 49 ++ ubi-utils/src/libcrc32/crc32.c | 83 +++ ubi-utils/src/liberror/error.c | 177 ++++++ ubi-utils/src/liblist/list.c | 149 +++++ ubi-utils/src/libpeb/peb.c | 116 ++++ ubi-utils/src/libpfi/pfi.c | 461 ++++++++++++++ ubi-utils/src/libpfiflash/pfiflash.c | 617 +++++++++++++++++++ ubi-utils/src/libreader/reader.c | 442 ++++++++++++++ ubi-utils/src/libubi/libubi.c | 773 ++++++++++++++++++++++++ ubi-utils/src/libubi/libubi_int.h | 119 ++++ ubi-utils/src/libubi/libubi_sysfs.c | 231 +++++++ ubi-utils/src/libubi/libubi_sysfs.h | 109 ++++ ubi-utils/src/libubigen/ubigen.c | 486 +++++++++++++++ ubi-utils/src/libubimirror/ubimirror.c | 217 +++++++ ubi-utils/src/mkbootenv/mkbootenv.c | 173 ++++++ ubi-utils/src/mkpfi/f128_nand_sample.cfg | 38 ++ ubi-utils/src/mkpfi/f64_nor_sample.cfg | 39 ++ ubi-utils/src/mkpfi/mkpfi | 723 ++++++++++++++++++++++ ubi-utils/src/nand2bin/nand2bin.c | 327 ++++++++++ ubi-utils/src/nand2bin/nandcorr.c | 85 +++ ubi-utils/src/pddcustomize/pddcustomize.c | 496 +++++++++++++++ ubi-utils/src/pfi2bin/pfi2bin.c | 678 +++++++++++++++++++++ ubi-utils/src/pfiflash/pfiflash.c | 243 ++++++++ ubi-utils/src/ubicrc32/ubicrc32.c | 143 +++++ ubi-utils/src/ubicrc32/ubicrc32.pl | 74 +++ ubi-utils/src/ubigen/ubigen_main.c | 369 ++++++++++++ ubi-utils/src/ubiinfo/ubiflash.h | 185 ++++++ ubi-utils/src/ubiinfo/ubiinfo.c | 406 +++++++++++++ ubi-utils/src/ubiinfo/ubiipl.h | 87 +++ ubi-utils/src/ubimirror/ubimirror.c | 206 +++++++ ubi-utils/src/ubimkvol/ubimkvol.c | 252 ++++++++ ubi-utils/src/ubirmvol/ubirmvol.c | 172 ++++++ ubi-utils/src/ubiwritevol/ubiwritevol.c | 352 +++++++++++ ubi-utils/src/unubi/unubi.c | 391 ++++++++++++ ubi-utils/testcases.txt | 9 + 69 files changed, 14436 insertions(+) create mode 100644 ubi-utils/Makefile create mode 100644 ubi-utils/Makefile.am create mode 100644 ubi-utils/README create mode 100644 ubi-utils/UBI.TXT create mode 100755 ubi-utils/bootstrap create mode 100644 ubi-utils/configure.ac create mode 100644 ubi-utils/inc/Makefile.am create mode 100644 ubi-utils/inc/bootenv.h create mode 100644 ubi-utils/inc/config-h.in create mode 100644 ubi-utils/inc/crc32.h create mode 100644 ubi-utils/inc/error.h create mode 100644 ubi-utils/inc/example_ubi.h create mode 100644 ubi-utils/inc/libubi.h create mode 100644 ubi-utils/inc/list.h create mode 100644 ubi-utils/inc/nandecc.h create mode 100644 ubi-utils/inc/peb.h create mode 100644 ubi-utils/inc/pfi.h create mode 100644 ubi-utils/inc/pfiflash.h create mode 100644 ubi-utils/inc/reader.h create mode 100644 ubi-utils/inc/ubigen.h create mode 100644 ubi-utils/inc/ubimirror.h create mode 100644 ubi-utils/lib/Makefile.am create mode 100644 ubi-utils/scripts/Makefile create mode 100644 ubi-utils/scripts/README create mode 100644 ubi-utils/scripts/TODO create mode 100755 ubi-utils/scripts/jffs2_test.sh create mode 100644 ubi-utils/scripts/test.cfg create mode 100755 ubi-utils/scripts/ubi_test.sh create mode 100755 ubi-utils/scripts/ubi_tools_test.sh create mode 100644 ubi-utils/src/bin2nand/bin2nand.c create mode 100644 ubi-utils/src/bin2nand/nandecc.c create mode 100644 ubi-utils/src/bin2nand/nandecc.h create mode 100644 ubi-utils/src/libbootenv/bootenv.c create mode 100644 ubi-utils/src/libbootenv/hashmap.c create mode 100644 ubi-utils/src/libbootenv/hashmap.h create mode 100644 ubi-utils/src/libcrc32/crc32.c create mode 100644 ubi-utils/src/liberror/error.c create mode 100644 ubi-utils/src/liblist/list.c create mode 100644 ubi-utils/src/libpeb/peb.c create mode 100644 ubi-utils/src/libpfi/pfi.c create mode 100644 ubi-utils/src/libpfiflash/pfiflash.c create mode 100644 ubi-utils/src/libreader/reader.c create mode 100644 ubi-utils/src/libubi/libubi.c create mode 100644 ubi-utils/src/libubi/libubi_int.h create mode 100644 ubi-utils/src/libubi/libubi_sysfs.c create mode 100644 ubi-utils/src/libubi/libubi_sysfs.h create mode 100644 ubi-utils/src/libubigen/ubigen.c create mode 100644 ubi-utils/src/libubimirror/ubimirror.c create mode 100644 ubi-utils/src/mkbootenv/mkbootenv.c create mode 100644 ubi-utils/src/mkpfi/f128_nand_sample.cfg create mode 100644 ubi-utils/src/mkpfi/f64_nor_sample.cfg create mode 100755 ubi-utils/src/mkpfi/mkpfi create mode 100644 ubi-utils/src/nand2bin/nand2bin.c create mode 100644 ubi-utils/src/nand2bin/nandcorr.c create mode 100644 ubi-utils/src/pddcustomize/pddcustomize.c create mode 100644 ubi-utils/src/pfi2bin/pfi2bin.c create mode 100644 ubi-utils/src/pfiflash/pfiflash.c create mode 100644 ubi-utils/src/ubicrc32/ubicrc32.c create mode 100755 ubi-utils/src/ubicrc32/ubicrc32.pl create mode 100644 ubi-utils/src/ubigen/ubigen_main.c create mode 100644 ubi-utils/src/ubiinfo/ubiflash.h create mode 100644 ubi-utils/src/ubiinfo/ubiinfo.c create mode 100644 ubi-utils/src/ubiinfo/ubiipl.h create mode 100644 ubi-utils/src/ubimirror/ubimirror.c create mode 100644 ubi-utils/src/ubimkvol/ubimkvol.c create mode 100644 ubi-utils/src/ubirmvol/ubirmvol.c create mode 100644 ubi-utils/src/ubiwritevol/ubiwritevol.c create mode 100644 ubi-utils/src/unubi/unubi.c create mode 100644 ubi-utils/testcases.txt diff --git a/ubi-utils/Makefile b/ubi-utils/Makefile new file mode 100644 index 0000000..686aa60 --- /dev/null +++ b/ubi-utils/Makefile @@ -0,0 +1,79 @@ +# +# This makefile simplifies the build process for a toolchain user. +# A toolchain developer should prefer a manual build process which +# fits to his original needs. +# + +X86_PREFIX?=/usr/local +x86_path=./build_x86 +x86_status=$(x86_path)/config.status + +PPC_PREFIX?=/opt/ppcnf/crossroot +ppc_path=./build_ppc +ppc_status=$(ppc_path)/config.status + +all: x86 ppc + +install: install_x86 install_ppc +uninstall: uninstall_x86 uninstall_ppc + +install_x86: x86 + make -C $(x86_path) install + +install_ppc: ppc + make -C $(ppc_path) install + + +uninstall_x86: x86 + make -C $(x86_path) uninstall + +uninstall_ppc: ppc + make -C $(ppc_path) uninstall + + + +x86: $(x86_status) + make -C $(x86_path) + +ppc: $(ppc_status) + make -C $(ppc_path) + +$(x86_status): $(x86_path) Makefile.in + cd $(x86_path) && ./config.status || ../configure \ + --prefix=$(X86_PREFIX) + +$(ppc_status): $(ppc_path) Makefile.in + cd $(ppc_path) && ./config.status || ../configure \ + --build=i686-pc-linux-gnu \ + --host=ppc-linux \ + --prefix=$(PPC_PREFIX) \ + --exec-prefix=$(PPC_PREFIX) + +Makefile.in: Makefile.am + ./bootstrap + +$(x86_path): + mkdir -p $(x86_path) + +$(ppc_path): + mkdir -p $(ppc_path) + +clean: + rm -rf depcomp install-sh missing .deps \ + config.log config.status \ + inc/Makefile.in lib/Makefile.in + find . -type f -name "*~" -print | xargs $(RM) + rm -f Makefile.in + rm -f aclocal.m4 + rm -rf autom4te.cache + rm -f config.guess + rm -f config.sub + rm -f configure + rm -f depcomp + rm -f install-sh + rm -f ltmain.sh + rm -f missing + rm -f lib/Makefile.in + rm -f inc/Makefile.in + rm -rf $(x86_path) + rm -rf $(ppc_path) diff --git a/ubi-utils/Makefile.am b/ubi-utils/Makefile.am new file mode 100644 index 0000000..a5c9252 --- /dev/null +++ b/ubi-utils/Makefile.am @@ -0,0 +1,96 @@ +AUTOMAKE_OPTIONS = foreign +SUBDIRS=lib inc + + +# ----------------------------------------------------------------------------- +# Scripts (Perl/Bash) which shall be installed. +# +bin_SCRIPTS = $(top_srcdir)/src/mkpfi/mkpfi + +# ----------------------------------------------------------------------------- +# C programs which shall be build and installed. +# +INCLUDES=-I$(top_srcdir)/inc -I$(top_srcdir)/../../kernel/include + +bin_PROGRAMS = bin/pfi2bin \ + bin/bin2nand \ + bin/ubicrc32 \ + bin/mkbootenv \ + bin/ubimirror \ + bin/ubimkvol \ + bin/ubirmvol \ + bin/ubiwritevol \ + bin/pfiflash \ + bin/pddcustomize \ + bin/ubiinfo \ + bin/nand2bin \ + bin/unubi + +# ----------------------------------------------------------------------------- +# C programs which shall be build and NOT installed. (FLD dependency...) +# +noinst_PROGRAMS = bin/ubigen + +bin_ubigen_SOURCES = $(top_srcdir)/src/ubigen/ubigen_main.c +bin_ubigen_LDADD = $(top_builddir)/lib/libubigen.la \ + $(top_builddir)/lib/libcrc32.la + +bin_pfiflash_SOURCES = $(top_srcdir)/src/pfiflash/pfiflash.c +bin_pfiflash_LDADD = $(top_builddir)/lib/libpfiflash.la \ + $(top_builddir)/lib/liberror.la +bin_pfiflash_LDFLAGS = -static + +bin_pddcustomize_SOURCES= $(top_srcdir)/src/pddcustomize/pddcustomize.c +bin_pddcustomize_LDADD = $(top_builddir)/lib/libbootenv.la \ + $(top_builddir)/lib/liberror.la \ + $(top_builddir)/lib/libubi.la \ + $(top_builddir)/lib/libubimirror.la +bin_pddcustomize_LDFLAGS= -static + +bin_pfi2bin_SOURCES = $(top_srcdir)/src/pfi2bin/pfi2bin.c +bin_pfi2bin_LDADD = $(top_builddir)/lib/libubigen.la \ + $(top_builddir)/lib/liberror.la \ + $(top_builddir)/lib/liblist.la \ + $(top_builddir)/lib/libreader.la + +bin_bin2nand_SOURCES = $(top_srcdir)/src/bin2nand/bin2nand.c \ + $(top_srcdir)/src/bin2nand/nandecc.c +bin_bin2nand_LDADD = $(top_builddir)/lib/liberror.la + + +bin_ubicrc32_SOURCES = $(top_srcdir)/src/ubicrc32/ubicrc32.c +bin_ubicrc32_LDADD = $(top_builddir)/lib/libcrc32.la + +bin_mkbootenv_SOURCES = $(top_srcdir)/src/mkbootenv/mkbootenv.c +bin_mkbootenv_LDADD = $(top_builddir)/lib/libbootenv.la \ + $(top_builddir)/lib/liberror.la + + +bin_ubimirror_SOURCES = $(top_srcdir)/src/ubimirror/ubimirror.c +bin_ubimirror_LDADD = $(top_builddir)/lib/liberror.la \ + $(top_builddir)/lib/libubimirror.la +bin_ubimirror_LDFLAGS= -static + +bin_ubimkvol_SOURCES = $(top_srcdir)/src/ubimkvol/ubimkvol.c +bin_ubimkvol_LDADD = $(top_builddir)/lib/libubi.la +bin_ubimkvol_LDFLAGS = -static + +bin_ubirmvol_SOURCES = $(top_srcdir)/src/ubirmvol/ubirmvol.c +bin_ubirmvol_LDADD = $(top_builddir)/lib/libubi.la +bin_ubirmvol_LDFLAGS = -static + +bin_ubiwritevol_SOURCES = $(top_srcdir)/src/ubiwritevol/ubiwritevol.c +bin_ubiwritevol_LDADD = $(top_builddir)/lib/libubi.la +bin_ubiwritevol_LDFLAGS = -static + +bin_ubiinfo_SOURCES = $(top_srcdir)/src/ubiinfo/ubiinfo.c + +bin_nand2bin_SOURCES = $(top_srcdir)/src/nand2bin/nand2bin.c \ + $(top_srcdir)/src/bin2nand/nandecc.c \ + $(top_srcdir)/src/nand2bin/nandcorr.c + +bin_unubi_SOURCES = $(top_srcdir)/src/unubi/unubi.c +bin_unubi_LDADD = $(top_builddir)/lib/libcrc32.la + +clean-local: + rm -rf bin/ 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/bootstrap b/ubi-utils/bootstrap new file mode 100755 index 0000000..f543912 --- /dev/null +++ b/ubi-utils/bootstrap @@ -0,0 +1,15 @@ +#!/bin/bash +LIBTOOLM4="/usr/local/share/libtool/libltdl" + +if test -d "$LIBTOOLM4"; then + echo "+ aclocal ${LIBTOOLM4}" + aclocal -I ${LIBTOOLM4} +else + echo "+ aclocal" + aclocal +fi +set -x +libtoolize --force +autoheader +automake --foreign --add-missing --copy +autoconf diff --git a/ubi-utils/configure.ac b/ubi-utils/configure.ac new file mode 100644 index 0000000..9f45176 --- /dev/null +++ b/ubi-utils/configure.ac @@ -0,0 +1,52 @@ +# Don't remove this check. +AC_PREREQ(2.59) + +# AC_INIT: Package, Version, Bugs +AC_INIT([flashutils],[0.1],[arnez@de.ibm.com]) +AC_CONFIG_HEADERS([inc/config.h:inc/config-h.in]) +AC_CANONICAL_SYSTEM +AM_INIT_AUTOMAKE([1.8 foreign]) +AM_MAINTAINER_MODE + +# Check debug options +AS_HELP_STRING +AC_ARG_ENABLE([debug], + AS_HELP_STRING([--enable-debug], + [build with debug information [default=yes]]),,enable_debug="yes") + +# Check for programs. +AM_PROG_LIBTOOL +AC_PROG_CC +AC_PROG_INSTALL +AC_PROG_MAKE_SET + +# Checks for header files. +AC_HEADER_STDC +# FIXME: Use AC_CHECK_HEADERS for UBI stuff. +# AC_CHECK_HEADERS([errno.h mtd/ubi-user.h]) +AC_CHECK_HEADERS([errno.h]) + +# Set build flags +if test "x$enable_debug" = "xyes"; then + CFLAGS="$CFLAGS -g -DDEBUG " +fi + +AC_DEFINE_UNQUOTED(HOST_OS, "${host}", [Host OS]) +AC_DEFINE_UNQUOTED(HOST_CPU, "${host_os}", [Host CPU]) +AC_DEFINE_UNQUOTED(BUILD_OS, "${build_os}", [Build OS]) +AC_DEFINE_UNQUOTED(BUILD_CPU, "${build_cpu}", [Build CPU]) + +# Additional Config +AC_C_BIGENDIAN + +# CFLAGS +CFLAGS="-std=gnu99 -Wundef -Wall $CFLAGS" + +# Init output. +AC_CONFIG_FILES([ + Makefile + lib/Makefile + inc/Makefile +]) + +AC_OUTPUT diff --git a/ubi-utils/inc/Makefile.am b/ubi-utils/inc/Makefile.am new file mode 100644 index 0000000..ca22c37 --- /dev/null +++ b/ubi-utils/inc/Makefile.am @@ -0,0 +1,5 @@ +AUTOMAKE_OPTIONS = foreign + +# You can export headers if necessary: +nobase_include_HEADERS = pfiflash.h \ + libubi.h diff --git a/ubi-utils/inc/bootenv.h b/ubi-utils/inc/bootenv.h new file mode 100644 index 0000000..86743ed --- /dev/null +++ b/ubi-utils/inc/bootenv.h @@ -0,0 +1,415 @@ +#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 /* FILE */ +#include +#include + +/* 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: + * @verbatimkey1=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); + + +/** + * @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); + +/** + * @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/inc/config-h.in b/ubi-utils/inc/config-h.in new file mode 100644 index 0000000..ce4998b --- /dev/null +++ b/ubi-utils/inc/config-h.in @@ -0,0 +1,74 @@ +/* inc/config-h.in. Generated from configure.ac by autoheader. */ + +/* Build CPU */ +#undef BUILD_CPU + +/* Build OS */ +#undef BUILD_OS + +/* Define to 1 if you have the header file. */ +#undef HAVE_DLFCN_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_ERRNO_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Host CPU */ +#undef HOST_CPU + +/* Host OS */ +#undef HOST_OS + +/* Name of package */ +#undef PACKAGE + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Version number of package */ +#undef VERSION + +/* Define to 1 if your processor stores words with the most significant byte + first (like Motorola and SPARC, unlike Intel and VAX). */ +#undef WORDS_BIGENDIAN diff --git a/ubi-utils/inc/crc32.h b/ubi-utils/inc/crc32.h new file mode 100644 index 0000000..31362b0 --- /dev/null +++ b/ubi-utils/inc/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 + +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/inc/error.h b/ubi-utils/inc/error.h new file mode 100644 index 0000000..25a1cee --- /dev/null +++ b/ubi-utils/inc/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 + +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) +#elif +#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) +#elif +#define dbg_msg(fmt, ...) +#endif + +#endif /* __ERROR_H__ */ diff --git a/ubi-utils/inc/example_ubi.h b/ubi-utils/inc/example_ubi.h new file mode 100644 index 0000000..23c7b54 --- /dev/null +++ b/ubi-utils/inc/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/inc/libubi.h b/ubi-utils/inc/libubi.h new file mode 100644 index 0000000..f13d812 --- /dev/null +++ b/ubi-utils/inc/libubi.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 +#include + +#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/inc/list.h b/ubi-utils/inc/list.h new file mode 100644 index 0000000..e8452a2 --- /dev/null +++ b/ubi-utils/inc/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 + +#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/inc/nandecc.h b/ubi-utils/inc/nandecc.h new file mode 100644 index 0000000..fb5d529 --- /dev/null +++ b/ubi-utils/inc/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 + +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/inc/peb.h b/ubi-utils/inc/peb.h new file mode 100644 index 0000000..246bce8 --- /dev/null +++ b/ubi-utils/inc/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 +#include + +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/inc/pfi.h b/ubi-utils/inc/pfi.h new file mode 100644 index 0000000..8c5cc07 --- /dev/null +++ b/ubi-utils/inc/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 + * Andreas Arnez + * Joern Engel + * Frank Haverkamp + * + * @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 /* 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 . + */ +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 . + * + * 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/inc/pfiflash.h b/ubi-utils/inc/pfiflash.h new file mode 100644 index 0000000..fc2eede --- /dev/null +++ b/ubi-utils/inc/pfiflash.h @@ -0,0 +1,62 @@ +#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 + * + * @brief The pfiflash library offers an interface for using the + * pfiflash * utility. + */ + +#include /* 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 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/inc/reader.h b/ubi-utils/inc/reader.h new file mode 100644 index 0000000..93c15e3 --- /dev/null +++ b/ubi-utils/inc/reader.h @@ -0,0 +1,84 @@ +#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 +#include + +#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 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; +}; + +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) */ +}; + +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/inc/ubigen.h b/ubi-utils/inc/ubigen.h new file mode 100644 index 0000000..9e9e8ec --- /dev/null +++ b/ubi-utils/inc/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 /* FILE */ +#include +#include + +#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/inc/ubimirror.h b/ubi-utils/inc/ubimirror.h new file mode 100644 index 0000000..893f5ce --- /dev/null +++ b/ubi-utils/inc/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 + +/** + * @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, size_t ids_size, + char *err_buf, size_t err_buf_size); + +#endif /* __UBIMIRROR_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/scripts/Makefile b/ubi-utils/scripts/Makefile new file mode 100644 index 0000000..6d014ea --- /dev/null +++ b/ubi-utils/scripts/Makefile @@ -0,0 +1,57 @@ +# +# Makefile +# +# Testcase for UBI pfi update. +# +# Author: Frank Haverkamp +# + +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 + +dd = dd +sed = sed +bin2nand = bin2nand +ubigen = ubigen +mkpfi = mkpfi +pfi2bin = pfi2bin + +vmlinux_bin ?= test_vmlinux.bin +rootfs_bin ?= test_rootfs.bin +spl_bin ?= test_u-boot.bin + +compl ?= $(card)_complete +compl_pfi ?= $(compl).pfi + +all: $(compl_pfi) + +$(compl_pfi): $(vmlinux_bin) $(rootfs_bin) $(spl_bin) + $(mkpfi) -c $(mkpfi_cfg) + +# +# 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 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/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_test.sh b/ubi-utils/scripts/ubi_test.sh new file mode 100755 index 0000000..9017148 --- /dev/null +++ b/ubi-utils/scripts/ubi_test.sh @@ -0,0 +1,318 @@ +#!/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. +# + +export PATH=$PATH:~/bin:/usr/local/bin:/home/dedekind/work/prj/ubi/tools/flashutils/bin/ + +UBIMKVOL=ubimkvol +UBIRMVOL=ubirmvol +UBIWRITEVOL=ubiwritevol + +# 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 ]; 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 +} + +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 ... " + + $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 -n "*** Now writing data to volume ... " + ls -l testdata.bin + $UBIWRITEVOL -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 114218D /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 "***********************************************************************" + +#for size in 31313 ; do +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 \ No newline at end of file diff --git a/ubi-utils/scripts/ubi_tools_test.sh b/ubi-utils/scripts/ubi_tools_test.sh new file mode 100755 index 0000000..43fc2bb --- /dev/null +++ b/ubi-utils/scripts/ubi_tools_test.sh @@ -0,0 +1,244 @@ +#!/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. +# + +export PATH=$PATH:~/bin:/usr/local/bin:/home/dedekind/work/prj/ubi/tools/flashutils/bin/ + +UBIMKVOL=ubimkvol +UBIRMVOL=ubirmvol +UBIWRITEVOL=ubiwritevol +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 ]; 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 114218D /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 ... " + $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 "***********************************************************************" + +pfiflash_basic +pfiflash_advanced + +echo "***********************************************************************" +echo "* Congratulations, no errors found! *" +echo "* Have fun with your cool UBI system! *" +echo "***********************************************************************" + +exit_success \ No newline at end of file diff --git a/ubi-utils/src/bin2nand/bin2nand.c b/ubi-utils/src/bin2nand/bin2nand.c new file mode 100644 index 0000000..5224e3b --- /dev/null +++ b/ubi-utils/src/bin2nand/bin2nand.c @@ -0,0 +1,343 @@ +/* + * 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 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) + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "error.h" +#include "config.h" +#include "nandecc.h" + +#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 + +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" + "bin2nand - a tool for adding OOB information to a " + "binary input file.\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: "pagesize", key: 'p', arg: "", flags: 0, + doc: "Pagesize in Byte/Mi/ki. Default: 2048", + group: 1 }, + + { name: "padding", key: 'j', arg: "", flags: 0, + doc: "Padding in Byte/Mi/ki. Default: no padding", + group: 1 }, + + /* Output options */ + { name: NULL, key: 0, arg: NULL, flags: 0, + doc: "Output settings:", + group: 2 }, + + { name: "output", key: 'o', arg: "", flags: 0, + doc: "Output filename. Interleaved Data/OOB if output-oob not " + "specified.", + group: 2 }, + + { name: "output-oob", key: 'q', arg: "", flags: 0, + doc: "Write OOB data in separate file.", + group: 2 }, + + { name: NULL, key: 0, arg: NULL, flags: 0, doc: NULL, group: 0 }, +}; + +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 error_t +parse_opt(int key, char *arg, struct argp_state *state) +{ + int err = 0; + char* endp; + + myargs *args = state->input; + + switch (key) { + case 'p': /* pagesize */ + args->pagesize = (size_t) ustrtoull(arg, &endp, 0); + CHECK_ENDP("p", endp); + break; + case 'j': /* padding */ + args->padding = (size_t) ustrtoull(arg, &endp, 0); + CHECK_ENDP("j", endp); + break; + case 'o': /* output */ + args->file_out_data = arg; + break; + case 'q': /* output oob */ + args->file_out_oob = arg; + break; + case ARGP_KEY_ARG: /* input file */ + args->fp_in = fopen(arg, "rb"); + if ((args->fp_in) == NULL) { + err_quit("Cannot open file %s for input\n", arg); + } + args->arg1 = arg; + args->options = &state->argv[state->next]; + state->next = state->argc; + break; + case ARGP_KEY_END: + if (err) { + err_msg("\n"); + argp_usage(state); + exit(EXIT_FAILURE); + } + break; + default: + return(ARGP_ERR_UNKNOWN); + } + + return 0; +} + +static struct argp argp = { + options: options, + parser: parse_opt, + args_doc: 0, + doc: doc, + children: NULL, + help_filter: NULL, + argp_domain: NULL, +}; + +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, read; + myargs args = { + .action = ACT_NORMAL, + .pagesize = PAGESIZE, + .padding = PADDING, + .fp_in = NULL, + .file_out_data = "", + .file_out_oob = "", + }; + + FILE* fp_out_data = stdout; + FILE* fp_out_oob = NULL; + + argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &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 (strcmp(args.file_out_data, "") != 0) { + 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 (strcmp(args.file_out_oob, "") != 0) { + 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); + remove(args.file_out_data); + remove(args.file_out_oob); + } + return rc; +} diff --git a/ubi-utils/src/bin2nand/nandecc.c b/ubi-utils/src/bin2nand/nandecc.c new file mode 100644 index 0000000..71660ef --- /dev/null +++ b/ubi-utils/src/bin2nand/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/bin2nand/nandecc.h b/ubi-utils/src/bin2nand/nandecc.h new file mode 100644 index 0000000..8ae8a66 --- /dev/null +++ b/ubi-utils/src/bin2nand/nandecc.h @@ -0,0 +1,11 @@ +/* + * NAND ecc functions + */ +#ifndef _NAND_ECC_H +#define _NAND_ECC_H + +#include + +extern int nand_calculate_ecc(const uint8_t *dat, uint8_t *ecc_code); + +#endif diff --git a/ubi-utils/src/libbootenv/bootenv.c b/ubi-utils/src/libbootenv/bootenv.c new file mode 100644 index 0000000..b6a1191 --- /dev/null +++ b/ubi-utils/src/libbootenv/bootenv.c @@ -0,0 +1,959 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include "hashmap.h" +#include "error.h" + +#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 = 0; /* current length */ + uint32_t j = 0; /* processed chars */ + uint32_t items = 0; /* processed items */ + int rc = 0; + + if (buf[size-1] != '\0') { + return BOOTENV_EFMT; + } + + while ((i = strlen(curr)) != 0) { + /* there is a key value pair remaining */ + rc = extract_pair(curr, env); + if (rc != 0) { + rc = BOOTENV_EINVAL; + return rc; + } + items++; + + j += i; + if (j >= size) + return 0; /* finished, end of buffer */ + curr += i + 1; + } + + return 0; +} + +/** + * 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) +{ + int rc; + char *buf = NULL; + size_t i = 0; + + 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) { + buf[i++] = '\0'; + break; /* we have enough */ + } + + /* log_msg("%c", c); */ /* FIXME DBG */ + + buf[i++] = c; + if (ferror(fp)) { + rc = -EIO; + goto err; + } + } + + /* transfer to hashmap */ + rc = rd_buffer(env, buf, size); + + /* FIXME DBG */ + /* log_msg("\n%s:%d rc=%d\n", __func__, __LINE__, rc); */ + +err: + free(buf); + return rc; +} + + + +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, + 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(FILE* fp, bootenv_t env) +{ + int rc = 0; + size_t size = 0; + char *buf = NULL; + + 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; + + if (fwrite(buf, size, 1, fp) != 1) { + rc = -EIO; + goto err; + } + +err: + if (buf != NULL) + free(buf); + return rc; +} + +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) +{ + /* @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, size_t err_buf_size) +{ + 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, + char *err_buf, size_t err_buf_size) +{ + 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, 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: "); + + 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/libbootenv/hashmap.c b/ubi-utils/src/libbootenv/hashmap.c new file mode 100644 index 0000000..250f71f --- /dev/null +++ b/ubi-utils/src/libbootenv/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 +#include +#include +#include +#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("[%d]: ", 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/libbootenv/hashmap.h b/ubi-utils/src/libbootenv/hashmap.h new file mode 100644 index 0000000..1b13e95 --- /dev/null +++ b/ubi-utils/src/libbootenv/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 +#include + +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/libcrc32/crc32.c b/ubi-utils/src/libcrc32/crc32.c new file mode 100644 index 0000000..666e217 --- /dev/null +++ b/ubi-utils/src/libcrc32/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 +#include + +/* 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/liberror/error.c b/ubi-utils/src/liberror/error.c new file mode 100644 index 0000000..c8c623c --- /dev/null +++ b/ubi-utils/src/liberror/error.c @@ -0,0 +1,177 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include "error.h" + +#define MAXLINE 4096 + +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; + + 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) +{ + 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 */ +} + + +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); + } + + fputs(buf, fpout); + fflush(fpout); + if (fpout != stderr) + fclose(fpout); + + return; +} diff --git a/ubi-utils/src/liblist/list.c b/ubi-utils/src/liblist/list.c new file mode 100644 index 0000000..6eb716b --- /dev/null +++ b/ubi-utils/src/liblist/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 +#include +#include + +#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/libpeb/peb.c b/ubi-utils/src/libpeb/peb.c new file mode 100644 index 0000000..08b770f --- /dev/null +++ b/ubi-utils/src/libpeb/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 +#include +#include +#include +#include +#include + +#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/libpfi/pfi.c b/ubi-utils/src/libpfi/pfi.c new file mode 100644 index 0000000..c8d5ee4 --- /dev/null +++ b/ubi-utils/src/libpfi/pfi.c @@ -0,0 +1,461 @@ +/* + * 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. + * + * Wed Feb 8 11:38:22 CET 2006: Initial creation. + */ + +#include +#include +#include +#include +#include +#include +#include + +#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 */ +}; + +static const char* modes[] = {"raw", "ubi", NULL}; /* 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) +{ + const char* ptr = modes[0]; + int i = 0; + while (ptr != NULL) { + if(strcmp(ptr, mode) == 0) { + return i; + } + ptr++; + 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 ((uint32_t)value == (uint32_t)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/libpfiflash/pfiflash.c b/ubi-utils/src/libpfiflash/pfiflash.c new file mode 100644 index 0000000..0859a22 --- /dev/null +++ b/ubi-utils/src/libpfiflash/pfiflash.c @@ -0,0 +1,617 @@ +/* + * 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 pfiflash.c + * + * @author Oliver Lohmann + * + * @brief This library is provides an interface to the pfiflash utility. + * + * Wed Mar 15 11:39:19 CET 2006 Initial creation. + * + * @TODO Comare 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 +#include +#include +#define __USE_GNU +#include + +#include +#include + +#include /* FIXME Is this ok here!!?? */ + +#include "ubimirror.h" +#include "error.h" +#include "reader.h" +#include "example_ubi.h" +#include "bootenv.h" + +static const char copyright [] __attribute__((unused)) = + "Copyright (c) International Business Machines Corp., 2006"; + +#define EBUF(fmt...) do { \ + snprintf(err_buf, err_buf_size, fmt); \ + } while (0) + +static pdd_func_t pdd_funcs[PDD_HANDLING_NUM] = + { + &bootenv_pdd_keep, + &bootenv_pdd_merge, + &bootenv_pdd_overwrite + }; +/**< An array of PDD function pointers indexed by the algorithm. */ + + +typedef enum ubi_update_process_t { + UBI_REMOVE = 0, + UBI_WRITE, +} ubi_update_process_t; + +static int +skip_raw_sections(FILE* pfi, list_t pfi_raws, + char* err_buf, size_t err_buf_size) +{ + int rc = 0; + + void *i; + list_t ptr; + size_t j, skip_size; + + if (is_empty(pfi_raws)) + return 0; + + foreach(i, ptr, pfi_raws) { + skip_size = ((pfi_raw_t)i)->data_size; + for(j = 0; j < skip_size; j++) { + fgetc(pfi); + if (ferror(pfi)) { + EBUF("Cannot skip raw section in PFI."); + rc = -EIO; + goto err; + } + } + } + err: + return rc; +} + +/** + * @brief Wraps the ubi_mkvol functions and implements a hook for the bootenv + * update. + * @param devno UBI device number. + * @param s Current seqnum. + * @param u Information about the UBI volume from the PFI. + * @param err_buf An error buffer. + * @param err_buf_size The size of the error buffer. + * @return 0 On Sucess. + * @return else Error. + */ +static int +my_ubi_mkvol(int devno, int s, pfi_ubi_t u, char *err_buf, size_t err_buf_size) +{ + int rc = 0; + int type; + ubi_lib_t ulib = NULL; + + log_msg("%s(vol_id=%d, size=%d, data_size=%d, type=%d, " + "alig=%d, nlen=%d, name=%s)", __func__, + 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) { + goto err; + } + + switch (u->type) { + case pfi_ubi_static: + type = UBI_STATIC_VOLUME; break; + case pfi_ubi_dynamic: + type = UBI_DYNAMIC_VOLUME; break; + default: + type = UBI_DYNAMIC_VOLUME; + } + + rc = ubi_mkvol(ulib, devno, u->ids[s], type, u->size, u->alignment, + u->names[s]); + if (rc != 0) { + EBUF("Cannot create volume: %d", u->ids[s]); + goto err; + } + + err: + if (ulib != NULL) + ubi_close(&ulib); + return rc; +} + +/** + * @brief A wrapper around the UBI library function ubi_rmvol. + * @param devno UBI device number. + * @param s Current seqnum. + * @param u Information about the UBI volume from the PFI. + * @param err_buf An error buffer. + * @param err_buf_size The size of the error buffer. + * + * If the volume does not exist, the function will return success. + */ +static int +my_ubi_rmvol(int devno, uint32_t id, char *err_buf, size_t err_buf_size) +{ + int rc = 0; + ubi_lib_t ulib = NULL; + int fd; + + log_msg("%s(id=%d)", __func__, id); + + rc = ubi_open(&ulib); + if (rc != 0) + goto err; + + /** + * Truncate if it exist or not. + */ + fd = ubi_vol_open(ulib, devno, id, O_RDWR); + if (fd == -1) + return 0; /* not existent, return */ + + rc = ubi_vol_update(fd, 0); + if (rc < 0) { + fprintf(stderr, "update failed rc=%d errno=%d\n", rc, errno); + ubi_vol_close(fd); + goto err; /* if EBUSY than empty device, continue */ + } + ubi_vol_close(fd); + + rc = ubi_rmvol(ulib, devno, id); + if (rc != 0) { + /* @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 */ + + dbg_msg("Remove UBI volume %d returned with error: %d " + "errno=%d", id, rc, errno); + goto err; + } + err: + if (ulib != NULL) + ubi_close(&ulib); + return rc; +} + +static int +read_bootenv_volume(int devno, uint32_t id, bootenv_t bootenv_old, + char *err_buf, size_t err_buf_size) +{ + int rc = 0; + ubi_lib_t ulib = NULL; + FILE* fp_in = NULL; + + rc = ubi_open(&ulib); + if (rc) + return rc; + + fp_in = ubi_vol_fopen_read(ulib, devno, id); + if (!fp_in) { + EBUF("Cannot open bootenv volume"); + rc = -EIO; + goto err; + } + + log_msg("%s reading old bootenvs", __func__); + + /* Save old bootenvs for reference */ + rc = bootenv_read(fp_in, bootenv_old, BOOTENV_MAXSIZE); + if (rc) + EBUF("Cannot read bootenv_old"); + err: + if (fp_in) + fclose(fp_in); + if (ulib) + ubi_close(&ulib); + return rc; +} + +static int +write_bootenv_volume(int devno, uint32_t id, bootenv_t bootenv_old, + pdd_func_t pdd_f, + FILE* fp_in, /* new pdd data contained in pfi */ + size_t fp_in_size, /* data size of new pdd data in pfi */ + char *err_buf, size_t err_buf_size) +{ + int rc = 0; + int warnings = 0; + ubi_lib_t ulib = NULL; + bootenv_t bootenv_new = NULL; + bootenv_t bootenv_res = NULL; + size_t update_size = 0; + FILE *fp_out = NULL; + + log_msg("%s(id=%d, fp_in=%p)", __func__, 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) { + goto err; + } + + rc = bootenv_create(&bootenv_new); + if (rc != 0) + goto err; + rc = bootenv_create(&bootenv_res); + if (rc != 0) + goto err; + + rc = bootenv_read(fp_in, bootenv_new, fp_in_size); + if (rc != 0) + goto err; + + rc = pdd_f(bootenv_old, bootenv_new, &bootenv_res, &warnings, + err_buf, err_buf_size); + if (rc != 0) + goto err; + if (warnings) { + /* @TODO Do sth with the warning */ + dbg_msg("A warning in the PDD operation occured: %d", + warnings); + } + log_msg("... (2)"); + + rc = bootenv_size(bootenv_res, &update_size); + if (rc != 0) + goto err; + + fp_out = ubi_vol_fopen_update(ulib, devno, id, update_size); + if (fp_out == NULL) + goto err; + + rc = bootenv_write(fp_out, bootenv_res); + if (rc != 0) { + EBUF("Write operation on ubi%d_%d failed.", devno, id); + rc = -EIO; + 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; +} + +static int +write_normal_volume(int devno, uint32_t id, size_t update_size, FILE* fp_in, + char *err_buf, size_t err_buf_size) +{ + int rc = 0; + ubi_lib_t ulib = NULL; + FILE* fp_out = NULL; + int c; + size_t i; + + log_msg("%s(id=%d, update_size=%d fp_in=%p)", + __func__, id, update_size, fp_in); + + rc = ubi_open(&ulib); + if (rc) + return rc; + + fp_out = ubi_vol_fopen_update(ulib, devno, id, update_size); + if (fp_out == NULL) { + rc = -1; + goto err; + } + + log_msg("starting the update ... "); /* FIXME DBG */ + for (i = 0; i < update_size; i++) { + c = getc(fp_in); + if (c == EOF && ferror(fp_in)) { + rc = -EIO; + goto err; + } + if (putc(c, fp_out) == EOF) { + rc = -EIO; + goto err; + } + /* FIXME DBG */ + /* if ((i & 0xFFF) == 0xFFF) log_msg("."); */ + } + /* log_msg("\n"); */ /* FIXME DBG */ + err: + if (fp_out) + fclose(fp_out); + if (ulib) + ubi_close(&ulib); + return rc; +} + + +/** + * @brief ... + * @precondition The PFI file contains at least one ubi_id entry. + * This is assured by the PFI read process. + * @postcondition The used seqnum number is set in the UBI PFI + * header list. + * The UBI volumes specified by seqnum are processed. + */ +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 = 0; + pfi_ubi_t u; + list_t ptr; + + foreach(u, ptr, pfi_ubis) { + int s = seqnum; + if (seqnum > (u->ids_size - 1)) { + s = 0; /* per default use the first */ + } + u->curr_seqnum = s; + + switch (ubi_update_process) { + case UBI_REMOVE: + 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); + 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) + 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, + err_buf, + err_buf_size); + } + else { + rc = write_normal_volume(EXAMPLE_UBI_DEVICE, + u->ids[s], + u->data_size, pfi, + err_buf, + err_buf_size); + } + if (rc != 0) + goto err; + break; + default: + EBUF("Invoked unknown UBI operation."); + rc = -1; + goto err; + + } + if (rc != 0) { + goto err; + } + } + err: + return rc; + +} + +static int +erase_unmapped_ubi_volumes(int devno, list_t pfi_ubis, + char *err_buf, size_t err_buf_size) +{ + int rc = 0; + list_t ptr; + pfi_ubi_t u; + size_t i; + uint8_t ubi_volumes[PFI_UBI_MAX_VOLUMES]; + + 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) { + EBUF("PFI file contains an invalid " + "volume id: %d", 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) + goto err; + } + } + err: + return rc; +} + +static int +mirror_ubi_volumes(uint32_t devno, list_t pfi_ubis, + char *err_buf, size_t err_buf_size) +{ + int rc = 0; + list_t ptr; + uint32_t j; + pfi_ubi_t i; + ubi_lib_t ulib = NULL; + + log_msg("%s(...)", __func__); + + rc = ubi_open(&ulib); + if (rc != 0) + 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; +} + +int +pfiflash(FILE* pfi, int complete, int seqnum, pdd_handling_t pdd_handling, + char *err_buf, size_t err_buf_size) +{ + int rc = 0; + pdd_func_t pdd_f = NULL; + + if (pfi == NULL) + return -EINVAL; + + /** + * 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 */ + + bootenv_t bootenv; + rc = bootenv_create(&bootenv); + if (rc != 0) { + EBUF("Cannot create bootenv variable"); + } + + rc = read_pfi_headers(&pfi_raws, &pfi_ubis, pfi, + err_buf, err_buf_size); + if (rc != 0) { + EBUF("Cannot read PFI headers."); + goto err; + } + + /* @TODO: If you want to implement an IPL update - start here. */ + rc = skip_raw_sections(pfi, pfi_raws, err_buf, err_buf_size); + if (rc != 0) { + goto err; + } + + if (complete) { + rc = erase_unmapped_ubi_volumes(EXAMPLE_UBI_DEVICE, pfi_ubis, + err_buf, err_buf_size); + if (rc != 0) { + EBUF("Cannot delete unmapped UBI volumes."); + goto err; + } + } + + if ((pdd_handling >= 0) && (pdd_handling < PDD_HANDLING_NUM)) { + pdd_f = pdd_funcs[pdd_handling]; + } + else { + EBUF("Used unknown PDD handling algorithm (pdd_handling)"); + } + + rc = process_ubi_volumes(pfi, curr_seqnum, pfi_ubis, bootenv, pdd_f, + UBI_REMOVE, err_buf, err_buf_size); + if (rc != 0) { + goto err; + } + rc = process_ubi_volumes(pfi, curr_seqnum, pfi_ubis, bootenv, pdd_f, + UBI_WRITE, err_buf, err_buf_size); + if (rc != 0) { + 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) + 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; +} diff --git a/ubi-utils/src/libreader/reader.c b/ubi-utils/src/libreader/reader.c new file mode 100644 index 0000000..c8242df --- /dev/null +++ b/ubi-utils/src/libreader/reader.c @@ -0,0 +1,442 @@ +/* + * 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 +#include +#include +#include +#include + +#include "bootenv.h" +#include "reader.h" + +/* @FIXME hard coded offsets right now - get them from Artem? */ +#define NAND_DEFAULT_VID_HDR_OFF 1984 +#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) { + res->flash_type = NAND_FLASH; + res->vid_hdr_offset = NAND_DEFAULT_VID_HDR_OFF; + } + 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, 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; + + 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_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, + &(res->starts_size), &(res->starts)); + 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, 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; + + 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_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, &(res->ids_size), + &(res->ids)); + 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, &(res->names_size), + &(tmp_names)); + 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/libubi/libubi.c b/ubi-utils/src/libubi/libubi.c new file mode 100644 index 0000000..9b9a793 --- /dev/null +++ b/ubi-utils/src/libubi/libubi.c @@ -0,0 +1,773 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libubi.h" +#include "libubi_int.h" +#include "libubi_sysfs.h" + +/** + * struct ubi_lib - UBI library descriptor. + * + * @ubi general UBI information + * + * @sysfs_root sysfs root directory + * @ubi_root UBI root directory in sysfs + * + * @nlen_max full path to the "maximum volume name length" sysfs file + * @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 *nlen_max; + 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 n = 1; + char *path; + struct stat stat; + + err = sysfs_read_int(desc->version, (int*) &ubi->version); + if (err) + return -1; + + err = sysfs_read_int(desc->nlen_max, (int*) &ubi->nlen_max); + if (err) + return -1; + + /* Calculate number of UBI devices */ + do { + char dir[20]; + + sprintf(&dir[0], "ubi%d", n); + path = mkpath(desc->sysfs_root, dir); + if (!path) + return ENOMEM; + + err = lstat(path, &stat); + if (err == 0) + n += 1; + free(path); + } while (err == 0); + + if (errno != ENOENT) + return -1; + + if (n == 0) { + ubi_err("no UBI devices found"); + errno = EINVAL; + return -1; + } + + errno = 0; + ubi->dev_count = n; + return 0; +} + +void +ubi_dump_handler(ubi_lib_t desc) +{ + ubi_lib_t d = desc; + printf( "UBI Library Descriptor:\n" + "ubi_root: %s\n" + "nlen_max: %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->nlen_max, 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->nlen_max = mkpath(res->ubi_root, UBI_NLEN_MAX); + if (!res->nlen_max) + goto error; + + res->version = mkpath(res->ubi_root, UBI_VERSION); + 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->udev_path, "%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->nlen_max); + 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[desc->ubi.nlen_max]; + + 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], + desc->ubi.nlen_max, 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; + + 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; + req.name_len = strlen(name); + req.name = name; + + /* 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(%08x) err=%d errno=%d\n", + __func__, 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/libubi/libubi_int.h b/ubi-utils/src/libubi/libubi_int.h new file mode 100644 index 0000000..1640010 --- /dev/null +++ b/ubi-utils/src/libubi/libubi_int.h @@ -0,0 +1,119 @@ +#ifndef __UBI_INT_H__ +#define __UBI_INT_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. + * + * Author: Artem B. Bityutskiy + */ + +/* + * 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, ...) +#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_VERSION + * @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_VERSION "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/libubi/libubi_sysfs.c b/ubi-utils/src/libubi/libubi_sysfs.c new file mode 100644 index 0000000..f7ecebc --- /dev/null +++ b/ubi-utils/src/libubi/libubi_sysfs.c @@ -0,0 +1,231 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libubi_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, unsigned long long *num) +{ + 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, unsigned int *num) +{ + 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/libubi/libubi_sysfs.h b/ubi-utils/src/libubi/libubi_sysfs.h new file mode 100644 index 0000000..2fb6072 --- /dev/null +++ b/ubi-utils/src/libubi/libubi_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/libubigen/ubigen.c b/ubi-utils/src/libubigen/ubigen.c new file mode 100644 index 0000000..0cfa687 --- /dev/null +++ b/ubi-utils/src/libubigen/ubigen.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 +#include +#include +#include +#include +#include + +#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) +{ +#ifdef DEBUG + int err = 0; + if (!u) { + fprintf(stderr, ""); + return; + } + if (!u->ec) { + fprintf(stderr, ""); + err = 1; + } + if (!u->v) { + fprintf(stderr, ""); + 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/ubimirror.c b/ubi-utils/src/libubimirror/ubimirror.c new file mode 100644 index 0000000..bf6b37c --- /dev/null +++ b/ubi-utils/src/libubimirror/ubimirror.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 +#include +#include +#include +#include + +#include +#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, size_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, size_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, size_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 (ulib != NULL) + ubi_close(&ulib); + if (fd_in != -1) + ubi_vol_close(fd_in); + if (fd_out != -1) + ubi_vol_close(fd_out); + return rc; +} diff --git a/ubi-utils/src/mkbootenv/mkbootenv.c b/ubi-utils/src/mkbootenv/mkbootenv.c new file mode 100644 index 0000000..49ce597 --- /dev/null +++ b/ubi-utils/src/mkbootenv/mkbootenv.c @@ -0,0 +1,173 @@ +/* + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "bootenv.h" +#include "error.h" + +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" + "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 struct argp_option options[] = { + { .name = "copyright", + .key = 'c', + .arg = NULL, + .flags = 0, + .doc = "Print copyright information.", + .group = 1 }, + { .name = "output", + .key = 'o', + .arg = "", + .flags = 0, + .doc = "Write the the output data to instead of stdout.", + .group = 1 }, + { .name = NULL, + .key = 0, + .arg = NULL, + .flags = 0, + .doc = NULL, + .group = 0 }, +}; + +typedef struct myargs { + FILE* fp_in; + FILE* fp_out; + + 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 'o': + args->fp_out = fopen(arg, "wb"); + if ((args->fp_out) == NULL) { + fprintf(stderr, + "Cannot open file %s for output\n", arg); + exit(1); + } + 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 = "[bootenv-txt-file]", + .doc = doc, + .children = NULL, + .help_filter = NULL, + .argp_domain = NULL, +}; + +int +main(int argc, char **argv) { + int rc = 0; + bootenv_t env; + + myargs args = { + .fp_in = stdin, + .fp_out = stdout, + .arg1 = NULL, + .options = NULL, + }; + + argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &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/mkpfi/f128_nand_sample.cfg b/ubi-utils/src/mkpfi/f128_nand_sample.cfg new file mode 100644 index 0000000..e468d9d --- /dev/null +++ b/ubi-utils/src/mkpfi/f128_nand_sample.cfg @@ -0,0 +1,38 @@ +[targets] +complete=ipl,spl,bootenv,kernel,rootfs +bootcode=spl,bootenv + +# Build sections +[ipl] +image=ipl.bin +raw_starts=0x00000000 +raw_total_size=128kiB + +[spl] +image=u-boot.bin +ubi_ids=2,3 +ubi_size=2MiB +ubi_type=static +ubi_names=spl_0,spl_1 + +[bootenv] +bootenv_file=bootenv_complete.txt +ubi_ids=4,5 +ubi_size=128kiB +ubi_type=static +ubi_names=bootenv_0,bootenv_1 + +[kernel] +image=vmlinux.bin +ubi_ids=6,7 +ubi_size=6MiB +ubi_type=static +ubi_names=kernel_0,kernel_1 + +[rootfs] +image=rootfs.bin +ubi_ids=8,9 +ubi_alignment=2kiB +ubi_size=16MiB +ubi_type=dynamic +ubi_names=rootfs_0,rootfs_1 diff --git a/ubi-utils/src/mkpfi/f64_nor_sample.cfg b/ubi-utils/src/mkpfi/f64_nor_sample.cfg new file mode 100644 index 0000000..fd44e27 --- /dev/null +++ b/ubi-utils/src/mkpfi/f64_nor_sample.cfg @@ -0,0 +1,39 @@ +[targets] +complete=ipl,spl,bootenv,kernel,rootfs +bootcode=spl,bootenv +rootfs=rootfs + +# Build sections +[ipl] +image=ipl.bin +raw_starts=0x02FE0000, 0x03FE0000 +raw_total_size=128kiB + +[spl] +image=u-boot.bin +ubi_ids=2,3 +ubi_size=2MiB +ubi_type=static +ubi_names=spl_0,spl_1 + +[bootenv] +bootenv_file=bootenv_complete.txt +ubi_ids=4,5 +ubi_size=128kiB +ubi_type=static +ubi_names=bootenv_0,bootenv_1 + +[kernel] +image=vmlinux.bin +ubi_ids=6,7 +ubi_size=6MiB +ubi_type=static +ubi_names=kernel_0,kernel_1 + +[rootfs] +image=rootfs.bin +ubi_ids=8,9 +ubi_alignment=2kiB +ubi_size=16128kiB +ubi_type=dynamic +ubi_names=rootfs_0,rootfs_1 diff --git a/ubi-utils/src/mkpfi/mkpfi b/ubi-utils/src/mkpfi/mkpfi new file mode 100755 index 0000000..2cce587 --- /dev/null +++ b/ubi-utils/src/mkpfi/mkpfi @@ -0,0 +1,723 @@ +#!/usr/bin/perl +# +# Copyright (c) International Business Machines Corp., 2006 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See +# the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# + +# +# mkpfi +# +# This perl program is assembles PFI files from a config file. +# +# Author: Oliver Lohmann (oliloh@de.ibm.com) +# +use warnings; +use strict; +use lib "/usr/lib/perl5"; # Please change this path as you need it, or + # make a proposal how this could be done + # nicer. +use Getopt::Long; +use Pod::Usage; +use Config::IniFiles; +use File::Temp; + +# ---------------------------------------------------------------------------- +# Versions +our $version : unique = "0.1"; +our $pfi_version : unique = "0x1"; + +# ---------------------------------------------------------------------------- +# Globals +my $verbose = 0; +my $cfg; + +my %opts = (); +my %files = (config => ""); +my @tmp_files; + +my %tools = (ubicrc32 => "ubicrc32"); + +# ---------------------------------------------------------------------------- +# Processing the input sections +# +# The idea is to combine each section entry with a function +# in order to allow some kind of preprocessing for the values +# before they are written into the PFI file. +# This is especially useful to be more verbose and +# user-friendly in the layout file. +# +# All key-function hashes are applied after the general +# validation of the configuration file. +# If any mandatory key is missing in a section the user +# will be informed and the PFI creation process is aborted. +# +# Default keys will be checked for their presence inside the config +# file. If they are missing, they will be generated with appr. values. + +# Mandatory keys for UBI volumes. +my %ubi_keys = ("ubi_ids" => \&check_id_list, + "ubi_size" => \&replace_num, + "ubi_type" => \&replace_type, + "ubi_names" => \&remove_spaces, + "ubi_alignment" => \&replace_num); + +# Mandatory keys for RAW sections. +my %raw_keys = ("raw_starts" => \&expand_starts, + "raw_total_size" => \&replace_num); + +# Common default keys for documentation and control purposes. +my %common_keys = ("flags" => \&replace_num, + "label" => \&do_nothing); + +# Define any defaults here. Values which maintained in this default +# region need not to be specified by the user explicitly. +my %def_ubi_keys = ("ubi_alignment" => [\&set_default, "0x1"]); +my %def_raw_keys = (); +my %def_common_keys = ("flags" => [\&set_default, "0x0"], + "label" => [\&generate_label, ""]); + +# ---------------------------------------------------------------------------- +# Input keys, actually the path to the input data. + +my %input_keys = ("image" => \&do_nothing); + +# Placeholder keys allow the replacement via a special +# purpose function. E.g. the bootenv_file key will be used +# to generate bootenv binary data from an text file and +# replace the bootenv_file key with an image key to handle it +# in the same way in the further creation process. +my %input_placeholder_keys = ("bootenv_file" => \&create_bootenv_image); + +# ---------------------------------------------------------------------------- +# Helper + +# @brief Get current time string. +sub get_date { + my $tmp = scalar localtime; + $tmp =~ s/ /_/g; + return $tmp; +} + +# @brief Print an info message to stdout. +sub INFO($) { + my $str = shift; + + if (!$verbose) { + return; + } + + print STDOUT $str; +} + +# @brief Print an error message to stderr. +sub ERR($) { + my $str = shift; + print STDERR $str; +} + +# @brief Print a warning message to stderr. +sub WARN($) { + my $str = shift; + print STDERR $str; +} + +sub parse_command_line($) { + my $opt = shift; + my $result = GetOptions( "help" => \$$opt{'help'}, + "man" => \$$opt{'man'}, + "config=s" => \$$opt{'config'}, + "verbose" => \$$opt{'verbose'}, + ) or pod2usage(2); + pod2usage(1) if defined ($$opt{help}); + pod2usage(-verbose => 2) if defined ($$opt{man}); + + $verbose = $$opt{verbose} if defined $$opt{verbose}; + + if (!defined $$opt{config}) { + ERR("[ ERROR: No config file specified. Aborting...\n"); + exit 1; + } + +} + +# @brief Check if all needed tools are in PATH. +sub check_tools { + my $err = 0; + my $key; + + foreach $key (keys %tools) { + if (`which $tools{$key}` eq "") { + ERR("\n") if ($err == 0); + ERR("! Please add the tool \'$tools{$key}\' " . + "to your path!\n"); + $err = 1; + } + } + die "[ ERROR: Did not find all needed tools!\n" if $err; +} + +sub open_cfg_file($) { + my $fname = shift; + my $res = new Config::IniFiles( -file => $fname ); + + die "[ ERROR: Cannot load your config file!\n" if (!defined $res); + return $res; +} + +sub set_default($$$$) { + my ($cfg, $section, $parameter, $def_value) = @_; + $cfg->newval($section, $parameter, $def_value); + return; +} + +sub generate_label($$$$) { + my ($cfg, $section, $parameter, $def_value) = @_; + my $new_label = $def_value . $section; + $new_label .= "_" . get_date; + $cfg->newval($section, $parameter, $new_label); + return; +} + +# @brief Converts any num to a unified hex string, i.e the resulting value +# always starts with "0x" and is aligned to 8 hexdigits. +# @return Returns 0 on success, otherwise an error occured. +# +sub any_num_to_hex($$) { + my $val = shift; + my $res = shift; + + # M(iB) + if ($val =~ m/([0-9]+)[Mm][i]?[Bb]?/g) { + $$res = sprintf("0x%08x", $1 * 1024 * 1024); + } + # k(iB) + elsif ($val =~ m/([0-9]+)[kK][i]?[Bb]?/g) { + $$res = sprintf("0x%08x", $1 * 1024); + } + # hex + elsif ($val =~ m/0x?([0-9a-fA-F]+)/g) { + $$res = sprintf("0x%08x", hex $1); + } + # decimal + elsif ($val =~ m/^([0-9]+)$/g) { + $$res = sprintf("0x%08x", $1); + } + else { + $$res = ""; + return -1; + } + + return 0; +} + +sub remove_spaces($$$) { + my ($cfg, $section, $parameter) = @_; + my ($start, @starts, @new_starts); + my $val = $cfg->val($section, $parameter); + my $res; + + $val =~ s/ //g; # spaces + $cfg->newval($section, $parameter, $val); +} + +sub expand_starts($$$) { + my ($cfg, $section, $parameter) = @_; + my ($start, @starts, @new_starts); + my $val = $cfg->val($section, $parameter); + my $res; + + $val =~ s/ //g; # spaces + @starts = split(/,/, $val); + + foreach $start (@starts) { + if (any_num_to_hex($start, \$res) != 0) { + ERR("[ ERROR: [$section]\n"); + ERR("[ Expecting a list of numeric " . + "values for parameter: $parameter\n"); + exit 1; + } + push (@new_starts, $res); + } + $res = join(',', @starts); + + $cfg->newval($section, $parameter, $res); +} + +sub check_id_list($$$) { + my ($cfg, $section, $parameter) = @_; + my $val = $cfg->val($section, $parameter); + my $res; + + if (!($val =~ m/^[0-9]+[,0-9]*/)) { + ERR("[ ERROR: Syntax error in 'ubi_ids' in " . + "section '$section': $val\n"); + ERR("[ Aborting... "); + exit 1; + } +} + +sub replace_type($$$) { + my ($cfg, $section, $parameter) = @_; + my $val = $cfg->val($section, $parameter); + my $res; + + $res = lc($val); + grep {$res eq $_} ('static', 'dynamic') + or die "[ ERROR: Unknown UBI Volume Type in " . + "section '$section': $val\n"; + + $cfg->newval($section, $parameter, $res); +} + + +sub replace_num($$$) { + my ($cfg, $section, $parameter) = @_; + my $val = $cfg->val($section, $parameter); + my $res = ""; + + if (any_num_to_hex($val, \$res) != 0) { + ERR("[ ERROR: [$section]\n"); + ERR("[ Expecting a numeric value " . + "for parameter: $parameter\n"); + exit 1; + } + $cfg->newval($section, $parameter, $res); +} + +sub do_nothing($$$) { + my ($cfg, $section, $parameter) = @_; + return; +} + +sub bootenv_sanity_check($) { + my $env = shift; # hash array containing bootenv + my %pdd = (); + + defined($$env{'pdd'}) or return "'pdd' not defined"; + foreach (split /,/, $$env{'pdd'}) { + defined($$env{$_}) or return "undefined '$_' in pdd"; + $pdd{$_} = 1; + } + + defined $$env{'pdd_preserve'} or + return ""; + foreach (split /,/, $$env{'pdd_preserve'}) { + defined($pdd{$_}) + or return "pdd_preserve field '$_' not in pdd"; + } + return ""; +} + +sub create_bootenv_image($$$) { + my ($cfg, $section, $parameter) = @_; + my $txt_fn = $cfg->val($section, "bootenv_file"); + my $in; + + my %value = (); + my @key = (); + + open $in, "<", $txt_fn + or die "[ ERROR: can't open bootenv file '$txt_fn'.\n"; + while (<$in>) { + next if (/^\s*(\#.*)?$/); # Skip comments/whitespace. + + if (/^(\S+?)\+\=(.*)$/) { + defined($value{$1}) or + die "$txt_fn:$.: error: appending to" . + " non-existent '$1'\n"; + $value{$1} .= $2; + } elsif (/^(\S+?)\=(.*)$/) { + not defined($value{$1}) or + die "$txt_fn:$.: error: trying to" . + " redefine '$1'\n"; + push @key, $1; + $value{$1} = $2; + } else { + die "$txt_fn:$.: error: unrecognized syntax\n"; + } + } + close $in; + + $_ = &bootenv_sanity_check(\%value) + and die "$txt_fn: error: $_\n"; + + my $tmp_file = new File::Temp(); + push (@tmp_files, $tmp_file); + + foreach (@key) { + print $tmp_file "$_=", $value{$_}, "\0"; + } + close $tmp_file; + + $cfg->newval($section, "image", $tmp_file-> filename); +} + +sub process_keys($$$) { + my ($cfg, $section, $keys) = @_; + my @parameters = $cfg->Parameters($section); + my $i; + + for ($i = 0 ; $i < scalar(@parameters) ; $i++ ) { + if (defined($$keys{$parameters[$i]})) { + $$keys{$parameters[$i]}->($cfg, $section, + $parameters[$i]); + } + } + +} + +sub is_in_keylist($$) { + my ($key, $keys) = @_; + my $i; + + for ($i = 0; $i < scalar(@$keys); $i++) { + if ($$keys[$i] eq $key) { + return 1; + } + } + + return 0; +} + +sub check_default_keys($$$) { + my ($cfg, $section, $keys) = @_; + my @parameters = $cfg->Parameters($section); + my $key; + + foreach $key (keys %$keys) { + if (!is_in_keylist($key, \@parameters)) { + $$keys{$key}[0]-> + ($cfg, $section, $key, $$keys{$key}[1]); + } + } + +} + + + +sub check_keys($$$) { + my ($cfg, $section, $keys) = @_; + my @parameters = $cfg->Parameters($section); + my ($i, $key, $err); + + $err = 0; + for ($i = 0 ; $i < scalar(@$keys) ; $i++ ) { + if (!is_in_keylist($$keys[$i], \@parameters)) { + ERR("[ ERROR: [$section]\n") if $err == 0; + $err = 1; + ERR("[ Missing key '$$keys[$i]'\n"); + } + } + + if ($err) { + ERR("[ Aborting...\n"); + exit 1; + } +} + +sub push_pfi_data($$$$$) { + my ($cfg, $section, $pfi_infos, $keys, $mode) = @_; + my ($tmp, $i, $hdr); + + my %pfi_info = (); + $pfi_info{'mode'} = $mode; + $pfi_info{'image'} = $cfg->val($section, "image"); + + # Build the PFI header + $hdr = sprintf("PFI!\n"); + $hdr .= sprintf("version=0x%08x\n", hex $pfi_version); + $hdr .= sprintf("mode=$mode\n"); + + # calculate the size of the binary data part + $tmp = -s $cfg->val($section, "image"); + if (!defined $tmp) { + ERR("[ ERROR: [$section]\n"); + ERR("[ Missing input image: " + . $cfg->val($section, "image") . "\n"); + exit 1; + } + # Check for the image to fit into the given space + my $quota; + if ($mode eq 'raw') { + $quota = oct $cfg->val($section, "raw_total_size"); + } elsif ($mode eq 'ubi') { + $quota = oct $cfg->val($section, "ubi_size"); + } + $tmp <= $quota + or die "[ERROR: image file too big: " . + $cfg->val($section, "image") . "\n"; + $pfi_info{'size'} = $tmp; + + $hdr .= sprintf("size=0x%08x\n", $tmp); + + my $img_file = $cfg->val($section, "image"); + my $crc32 = `$tools{'ubicrc32'} $img_file 2>&1`; + if (any_num_to_hex($crc32, \$tmp) != 0) { + die "[ ERROR: $tools{'ubicrc32'} returned with errors"; + } + $hdr .= sprintf("crc=$tmp\n"); + + + # Process all remaining keys + for ($i = 0; $i < scalar (@$keys); $i++) { + if ($$keys[$i] eq "image") { # special case image input file + if (! -e ($tmp = $cfg->val($section, "image"))) { + ERR("[ ERROR: [$section]\n"); + ERR("[ Cannot find input file $tmp\n"); + exit 1; + } + next; + } + $hdr .= sprintf("%s=%s\n", $$keys[$i], + $cfg->val($section, $$keys[$i])); + } + + $hdr .= sprintf("\n"); # end marker for PFI-header + + $pfi_info{'header'} = $hdr; + + # store in the header list + push @$pfi_infos, \%pfi_info; +} + +sub process_section($$$$$$) { + my ($cfg, $section, $pfi_infos, $custom_keys, + $def_custom_keys, $mode) = @_; + my @keys = (keys %common_keys, keys %$custom_keys); + my @complete_keys = (@keys, keys %input_keys); + + # set defaults if necessary + check_default_keys($cfg, $section, $def_custom_keys); + check_default_keys($cfg, $section, \%def_common_keys); + + # check for placeholders... + process_keys($cfg, $section, \%input_placeholder_keys); + + # VALIDATE layout.cfg entries + check_keys($cfg, $section, \@complete_keys); + + # execute linked functions (if any) + process_keys($cfg, $section, \%common_keys); + process_keys($cfg, $section, $custom_keys); + + push_pfi_data($cfg, $section, $pfi_infos, \@keys, $mode); +} + +sub get_section_info($$) { + my ($cfg, $section) = @_; + my @parameters = $cfg->Parameters($section); + my ($ubi, $raw, $i, @res); + + $ubi = $raw = 0; + for ($i = 0 ; $i < scalar(@parameters) ; $i++ ) { + if ($parameters[$i] =~ m/ubi_/gi) { + $ubi = 1; + @res = (\%ubi_keys, \%def_ubi_keys, "ubi"); + } + if ($parameters[$i] =~ m/raw_/gi) { + $raw = 1; + @res = (\%raw_keys, \%def_raw_keys, "raw"); + } + } + + if (($ubi + $raw) != 1) { # double definition in section + ERR("[ ERROR: Layout error in section '$section'\n"); + exit 1; + } + + return @res; +} + +sub mk_target_list($$) { + my $val = shift; + my $tmp = shift; + my $complete = 0; + + if ($val =~ m/\((.*)\)/g) { + $val = $1; + $complete = 1; + } + $val =~ s/ //g; # spaces + + @$tmp = split(/,/, $val); + + return $complete; +} + +sub copy_bytes($$$) { + my ($in, $out, $to_copy) = @_; + + while ($to_copy) { + my $buf; + my $bufsize = 1024*1024; + + $bufsize < $to_copy or $bufsize = $to_copy; + read($in, $buf, $bufsize) == $bufsize + or die "[ ERROR: Image file shrunk during operation\n"; + print $out $buf; + $to_copy -= $bufsize; + } +} + +sub write_target($$) { + my ($pfi_infos, $target) = @_; + my ($pfi_info); + + INFO("[ Writting target pfi file: '$target.pfi'...\n"); + if (-e "$target.pfi") { + WARN("! Replaced old pfi...\n"); + `rm -f $target.pfi`; + } + open(FILE, ">", "$target.pfi") + or die "[ ERROR: Cannot create output file: $target.pfi\n"; + binmode(FILE); + + # @FIXME sort by mode (first raw, then ubi) + # Currently this ordering is based on a string comparism. :-) + @$pfi_infos = sort {(lc $$a{'mode'}) cmp (lc $$b{'mode'})} @$pfi_infos; + + # Print all headers first + foreach $pfi_info (@$pfi_infos) { + print FILE $$pfi_info{'header'}; + + } + # Print the linked data sections + print FILE "DATA\n"; + foreach $pfi_info (@$pfi_infos) { + open(IMAGE, "<", $$pfi_info{'image'}) + or die "[ ERROR: Cannot open input image: " . + "$$pfi_info{'image'}" . "\n"; + binmode(IMAGE); + ©_bytes(\*IMAGE, \*FILE, $$pfi_info{'size'}); + close(IMAGE) or die "[ ERROR: Cannot close input image: " . + "$$pfi_info{'image'}" . "\n"; + } + close(FILE) or die "[ ERROR: Cannot close output file: $target.pfi\n"; +} + +sub process_config($) { + my $cfg = shift; + my @sections = $cfg->Sections; + my ($i, $j, $keylist, $def_keylist, $mode, $tmp, + @tlist, $complete,@pfi_infos); + + my @parameters = $cfg->Parameters("targets") or + die "[ ERROR: Config file has no 'targets' section!\n"; + + for ($i = 0 ; $i < scalar(@parameters) ; $i++ ) { + INFO("[ Processing target '$parameters[$i]'...\n"); + @pfi_infos = (); + + # get a list of subtargets + $complete = mk_target_list($cfg->val("targets", + $parameters[$i]), \@tlist); + # build all subtargets + for ($j = 0 ; $j < scalar(@tlist) ; $j++ ) { + ($keylist, $def_keylist, $mode) + = get_section_info($cfg, $tlist[$j]); + process_section($cfg, $tlist[$j], + \@pfi_infos, + $keylist, $def_keylist, $mode); + } + + write_target(\@pfi_infos, $parameters[$i]); + } + + INFO("[ Success.\n"); + + +} + +sub clear_files() { + # @FIXME: + # Works implicitly and Fedora seems to have removed + # the cleanup call. Thus for now, inactive. + # File::Temp::cleanup(); +} + +require 5.008_000; # Tested with version 5.8.0. +select STDOUT; $| = 1; # make STDOUT output unbuffered +select STDERR; $| = 1; # make STDERR output unbuffered + +parse_command_line(\%opts); +check_tools; +$cfg = open_cfg_file($opts{config}); +process_config($cfg); +clear_files; + +__END__ + + +=head1 NAME + +mkpfi - Using GetOpt::Long, Pod::Usage, Config::IniFiles + + +=head1 SYNOPSIS + +mkpfi [OPTIONS ...] + + + OPTION + + [--config] [--help] [--man] + + +=head1 ABSTRACT + +Perl script for generating pdd pfi files from given config files. + +=head1 OPTIONS + +=over + +=item B<--help> + +Print out brief help message. + +=item B<--usage> + +Print usage. + +=item B<--config> + +Config input file. + +=item B<--man> + +Print manual page, same as 'perldoc mkpfi'. + +=item B<--verbose> + +Be verbose! + +=back + +=head1 BUGS + +Report via MTD mailing list + + +=head1 SEE ALSO + +http://www.linux-mtd.infradead.org/ + + +=head1 AUTHOR + +Oliver Lohmann (oliloh@de.ibm.com) + +=cut diff --git a/ubi-utils/src/nand2bin/nand2bin.c b/ubi-utils/src/nand2bin/nand2bin.c new file mode 100644 index 0000000..a728fb5 --- /dev/null +++ b/ubi-utils/src/nand2bin/nand2bin.c @@ -0,0 +1,327 @@ +/* + * 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 ... + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nandecc.h" + +#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 error_t parse_opt (int key, char *arg, struct argp_state *state); + +const char *argp_program_bug_address = ""; + +static char doc[] = "\nVersion: " VERSION "\n\t" + HOST_OS" "HOST_CPU" at "__DATE__" "__TIME__"\n" + "\nSplit data and OOB.\n"; + +static struct argp_option options[] = { + { .name = "pagesize", + .key = 'p', + .arg = "", + .flags = 0, + .doc = "NAND pagesize", + .group = OPTION_ARG_OPTIONAL }, + + { .name = "output", + .key = 'o', + .arg = "", + .flags = 0, + .doc = "Data output file", + .group = OPTION_ARG_OPTIONAL }, + + { .name = "oob", + .key = 'O', + .arg = "", + .flags = 0, + .doc = "OOB output file", + .group = OPTION_ARG_OPTIONAL }, + + { .name = NULL, .key = 0, .arg = NULL, .flags = 0, + .doc = NULL, .group = 0 }, +}; + +static struct argp argp = { + .options = options, + .parser = parse_opt, + .args_doc = "input.mif", + .doc = doc, + .children = NULL, + .help_filter = NULL, + .argp_domain = NULL, +}; + +/* + * 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 key The parameter. + * @param arg Argument passed to parameter. + * @param state Location to put information on parameters. + * + * @return error + * + * Get the `input' argument from `argp_parse', which we know is a + * pointer to our arguments structure. + */ +static error_t +parse_opt(int key, char *arg, struct argp_state *state) +{ + struct args *args = state->input; + + switch (key) { + case 'p': /* --pagesize */ + args->pagesize = str_to_num(arg); break; + + case 'o': /* --output= */ + args->output_file = arg; + break; + + case 'O': /* --oob= */ + args->output_file = arg; + break; + + case ARGP_KEY_NO_ARGS: + /* argp_usage(state); */ + break; + + case ARGP_KEY_ARG: + args->arg1 = arg; + /* Now we consume all the rest of the arguments. + `state->next' is the index in `state->argv' of the + next argument to be parsed, which is the first STRING + we're interested in, so we can just use + `&state->argv[state->next]' as the value for + arguments->strings. + + _In addition_, by setting `state->next' to the end + of the arguments, we can force argp to stop parsing here and + return. */ + + args->options = &state->argv[state->next]; + state->next = state->argc; + break; + + case ARGP_KEY_END: + /* argp_usage(state); */ + break; + + default: + return(ARGP_ERR_UNKNOWN); + } + + 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, size_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: %d\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; + + argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &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/nand2bin/nandcorr.c b/ubi-utils/src/nand2bin/nandcorr.c new file mode 100644 index 0000000..7f1a547 --- /dev/null +++ b/ubi-utils/src/nand2bin/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/pddcustomize/pddcustomize.c b/ubi-utils/src/pddcustomize/pddcustomize.c new file mode 100644 index 0000000..f71d916 --- /dev/null +++ b/ubi-utils/src/pddcustomize/pddcustomize.c @@ -0,0 +1,496 @@ +/* + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "bootenv.h" +#include "error.h" +#include "example_ubi.h" +#include "libubi.h" +#include "ubimirror.h" + +typedef enum action_t { + ACT_NORMAL = 0, + ACT_LIST, + ACT_ARGP_ABORT, + ACT_ARGP_ERR, +} action_t; + +#define ABORT_ARGP do { \ + state->next = state->argc; \ + args->action = ACT_ARGP_ABORT; \ +} while (0) + +#define ERR_ARGP do { \ + state->next = state->argc; \ + args->action = ACT_ARGP_ERR; \ +} while (0) + +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" + "pddcustomize - customize bootenv and pdd values.\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: "input", key: 'i', arg: "", flags: 0, + doc: "Binary input file. For debug purposes.", + group: 1 }, + + { name: "output", key: 'o', arg: "", flags: 0, + doc: "Binary output file. For debug purposes.", + group: 1 }, + + { name: "list", key: 'l', arg: NULL, flags: 0, + doc: "List card bootenv/pdd values.", + group: 1 }, + + { name: "both", key: 'b', arg: NULL, flags: 0, + doc: "Mirror updated PDD to redundand copy.", + group: 1 }, + + { name: "side", key: 's', arg: "", flags: 0, + doc: "The side/seqnum to update.", + group: 1 }, + + { name: "host", key: 'x', arg: NULL, flags: 0, + doc: "use x86 platform for debugging.", + group: 1 }, + + { name: NULL, key: 0, arg: NULL, flags: 0, doc: NULL, group: 0 }, +}; + +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 error_t +parse_opt(int key, char *arg, struct argp_state *state) +{ + int rc = 0; + int err = 0; + + myargs *args = state->input; + + 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(arg); + if (args->side < 0) { + err_msg("Unsupported seqnum: %d.\n" + "Supported seqnums are '0' and '1'\n", + args->side, arg); + ERR_ARGP; + } + break; + case 'i': + args->file_in = arg; + break; + case 'o': + args->file_out = arg; + break; + case ARGP_KEY_ARG: + rc = extract_pair(args->env_in, arg); + if (rc != 0) + ERR_ARGP; + break; + case ARGP_KEY_END: + if (err) { + err_msg("\n"); + argp_usage(state); + ERR_ARGP; + } + break; + default: + return(ARGP_ERR_UNKNOWN); + } + + return 0; +} + +static struct argp argp = { + options: options, + parser: parse_opt, + args_doc: "[key=value] [...]", + doc: doc, + children: NULL, + help_filter: NULL, + argp_domain: NULL, +}; + + +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; + } + + argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &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/pfi2bin/pfi2bin.c b/ubi-utils/src/pfi2bin/pfi2bin.c new file mode 100644 index 0000000..6536c19 --- /dev/null +++ b/ubi-utils/src/pfi2bin/pfi2bin.c @@ -0,0 +1,678 @@ +/* + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "config.h" +#include "list.h" +#include "error.h" +#include "reader.h" +#include "peb.h" +#include "crc32.h" + +#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)) = + "Licensed Materials - Property of IBM\n" + "IBM Flexible Support Processor Licensed Material\n" + "(c) Copyright IBM Corp 2006 All Rights Reserved.\n" + "US Government Users Restricted Rights - Use, duplication\n" + "or disclosure restricted by GSA ADP Schedule Contract\n" + "with IBM Corp."; + +static error_t parse_opt (int key, char *arg, struct argp_state *state); + +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" + "pfi2bin - a tool to convert PFI files into binary images.\n"; + +static struct argp_option options[] = { + /* COMMON */ + { name: NULL, key: 0, arg: NULL, flags: 0, + doc: "Common settings:", + group: OPTION_ARG_OPTIONAL}, + + { name: "verbose", key: 'v', arg: NULL, flags: 0, + doc: "Print more information.", + group: OPTION_ARG_OPTIONAL }, + + { name: "copyright", key: 'c', arg: NULL, flags: 0, + group: OPTION_ARG_OPTIONAL }, + + + /* INPUT */ + { name: NULL, key: 0, arg: NULL, flags: 0, + doc: "Input:", + group: 4}, + + { name: "platform", key: 'j', arg: "pdd-file", flags: 0, + doc: "PDD information which contains the card settings.", + group: 4 }, + + /* OUTPUT */ + { name: NULL, key: 0, arg: NULL, flags: 0, + doc: "Output:", + group: 5}, + + { name: "output", key: 'o', arg: "filename", flags: 0, + doc: "Outputfile, default: stdout.", + group: 5 }, + + { name: NULL, key: 0, arg: NULL, flags: 0, doc: NULL, group: 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 struct argp argp = { + options: options, + parser: parse_opt, + args_doc: "pfifile", + doc: doc, + children: NULL, + help_filter: NULL, + argp_domain: NULL, +}; + +static error_t +parse_opt(int key, char *arg, struct argp_state *state) +{ + myargs *args = state->input; + + switch (key) { + /* common settings */ + case 'v': /* --verbose= */ + args->verbose = 1; + break; + + case 'c': /* --copyright */ + fprintf(stderr, "%s\n", copyright); + exit(0); + break; + + case 'j': /* --platform */ + args->f_in_pdd = arg; + break; + + case 'o': /* --output */ + args->f_out = arg; + break; + + case ARGP_KEY_ARG: + args->f_in_pfi = arg; + /* args->arg1 = arg; */ + args->options = &state->argv[state->next]; + state->next = state->argc; + break; + + case ARGP_KEY_END: + if (args->action == ACT_NOTHING) { + argp_usage(state); + exit(1); + } + break; + + default: + return(ARGP_ERR_UNKNOWN); + } + + 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; + + 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); + + 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); + 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 */ + argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &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/pfiflash.c b/ubi-utils/src/pfiflash/pfiflash.c new file mode 100644 index 0000000..18b3aa2 --- /dev/null +++ b/ubi-utils/src/pfiflash/pfiflash.c @@ -0,0 +1,243 @@ +/* + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "error.h" +#include "config.h" + +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" + "pfiflash - a tool for updating a controller with PFI files.\n"; + +static const char copyright [] __attribute__((unused)) = + "FIXME: insert license type."; /* FIXME */ + +static struct argp_option options[] = { + /* Output options */ + { name: NULL, key: 0, arg: NULL, flags: 0, + doc: "Standard options:", + group: 1 }, + + { name: "copyright", key: 'c', arg: NULL, flags: 0, + doc: "Print copyright information.", + group: 1 }, + + { name: "verbose", key: 'v', arg: NULL, flags: 0, + doc: "Be verbose during program execution.", + group: 1 }, + + { name: "logfile", key: 'l', arg: "", flags: 0, + doc: "Write a logfile to .", + group: 1 }, + + /* Output options */ + { name: NULL, key: 0, arg: NULL, flags: 0, + doc: "Process options:", + group: 2 }, + + { name: "complete", key: 'C', arg: NULL, flags: 0, + doc: "Execute a complete system update. Updates both sides.", + group: 2 }, + + { name: "side", key: 's', arg: "", flags: 0, + doc: "Select the side which shall be updated.", + group: 2 }, + + { name: "pdd-update", key: 'p', arg: "", flags: 0, + doc: "Specify the pdd-update algorithm. is either " + "'keep', 'merge' or 'overwrite'.", + group: 2 }, + + { name: NULL, key: 0, arg: NULL, flags: 0, doc: NULL, group: 0 }, +}; + +typedef struct myargs { + int verbose; + const char *logfile; + + 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 error_t +parse_opt(int key, char *arg, struct argp_state *state) +{ + int err = 0; + + myargs *args = state->input; + + switch (key) { + /* standard options */ + case 'c': + err_msg("%s\n", copyright); + exit(0); + break; + case 'v': + args->verbose = 1; + break; + case 'l': + args->logfile = arg; + break; + /* process options */ + case 'C': + args->complete = 1; + break; + case 'p': + args->pdd_handling = get_pdd_handling(arg); + if (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(arg); + if (args->seqnum < 0) { + err_quit("Unsupported side: %s.\n" + "Supported sides are '0' and '1'\n", arg); + } + break; + + case ARGP_KEY_ARG: /* input file */ + args->fp_in = fopen(arg, "r"); + if ((args->fp_in) == NULL) { + err_sys("Cannot open PFI file %s for input", arg); + } + args->arg1 = arg; + args->options = &state->argv[state->next]; + state->next = state->argc; + break; + case ARGP_KEY_END: + if (err) { + err_msg("\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: "[pfifile]", + doc: doc, + children: NULL, + help_filter: NULL, + argp_domain: NULL, +}; + +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 = "/tmp/pfiflash.log", + .pdd_handling = PDD_KEEP, + .fp_in = stdin, + }; + + argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &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; + } + + rc = pfiflash(args.fp_in, args.complete, args.seqnum, + args.pdd_handling, 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("Error: %s\nrc: %d\n", err_buf, rc); + return rc; +} diff --git a/ubi-utils/src/ubicrc32/ubicrc32.c b/ubi-utils/src/ubicrc32/ubicrc32.c new file mode 100644 index 0000000..fb4ef49 --- /dev/null +++ b/ubi-utils/src/ubicrc32/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 +#include +#include +#include +#include +#include +#include +#include + +#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/ubicrc32/ubicrc32.pl b/ubi-utils/src/ubicrc32/ubicrc32.pl new file mode 100755 index 0000000..add5f9d --- /dev/null +++ b/ubi-utils/src/ubicrc32/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/src/ubigen/ubigen_main.c b/ubi-utils/src/ubigen/ubigen_main.c new file mode 100644 index 0000000..8a464dd --- /dev/null +++ b/ubi-utils/src/ubigen/ubigen_main.c @@ -0,0 +1,369 @@ +/* + * 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 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ubigen.h" +#include "config.h" + +typedef enum action_t { + ACT_NORMAL = 0x00000001, + ACT_BROKEN_UPDATE = 0x00000002, +} action_t; + + +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" + "ubigen - a tool for adding UBI information to a binary input file.\n"; + +static const char copyright [] __attribute__((unused)) = + "FIXME: insert license type"; /* FIXME */ + +#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) + +static struct argp_option options[] = { + /* COMMON */ + { name: NULL, key: 0, arg: NULL, flags: 0, + doc: "Common settings:", + group: 1}, + + { name: "copyright", key: 'c', arg: NULL, flags: 0, + doc: "Print copyright information.", + group: 1 }, + + { name: "verbose", key: 'v', arg: NULL, flags: 0, + doc: "Print more progress information.", + group: 1 }, + + { name: "debug", key: 'd', arg: NULL, flags: 0, + group: 1 }, + + + /* INPUT */ + { name: NULL, key: 0, arg: NULL, flags: 0, + doc: "UBI Settings:", + group: 4}, + + { name: "alignment", key: 'A', arg: "", flags: 0, + doc: "Set the alignment size to (default 1).\n" + "Values can be specified as bytes, 'ki' or 'Mi'.", + group: 4 }, + + { name: "blocksize", key: 'B', arg: "", flags: 0, + doc: "Set the eraseblock size to (default 128 KiB).\n" + "Values can be specified as bytes, 'ki' or 'Mi'.", + group: 4 }, + + { name: "erasecount", key: 'E', arg: "", flags: 0, + doc: "Set the erase count to (default 0)", + group: 4 }, + + { name: "setver", key: 'X', arg: "", flags: 0, + doc: "Set UBI version number to (default 1)", + group: 4 }, + + { name: "id", key: 'I', arg: "", flags: 0, + doc: "The UBI volume id.", + group: 4 }, + + + { name: "offset", key: 'O', arg: "", flags: 0, + doc: "Offset from start of an erase block to the UBI volume header.", + group: 4 }, + + { name: "type", key: 'T', arg: "", flags: 0, + doc: "The UBI volume type:\n1 = dynamic, 2 = static", + group: 4 }, + + /* INPUT/OUTPUT */ + { name: NULL, key: 0, arg: NULL, flags: 0, + doc: "Input/Output:", + group: 5 }, + + { name: "infile", key: 'i', arg: "", flags: 0, + doc: "Read input from file.", + group: 5 }, + + { name: "outfile", key: 'o', arg: "", flags: 0, + doc: "Write output to file (default is stdout).", + group: 5 }, + + /* Special options */ + { name: NULL, key: 0, arg: NULL, flags: 0, + doc: "Special options:", + group: 6 }, + + { name: "broken-update", key: 'U', arg: "", flags: 0, + doc: "Create an ubi image which simulates a broken update.\n" + " specifies the logical eraseblock number to update.\n", + group: 6 }, + + { name: NULL, key: 0, arg: NULL, flags: 0, doc: NULL, group: 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 error_t +parse_opt(int key, char *arg, struct argp_state *state) +{ + int err = 0; + char* endp; + + myargs *args = state->input; + + switch (key) { + case 'c': + fprintf(stderr, "%s\n", copyright); + exit(0); + break; + case 'o': /* output */ + args->fp_out = fopen(arg, "wb"); + if ((args->fp_out) == NULL) { + fprintf(stderr, "Cannot open file %s for output\n", + arg); + exit(1); + } + break; + case 'i': /* input */ + args->fp_in = fopen(arg, "rb"); + if ((args->fp_in) == NULL) { + fprintf(stderr, "Cannot open file %s for input\n", + arg); + exit(1); + } + break; + case 'v': /* verbose */ + args->verbose = 1; + break; + + case 'B': /* eb_size */ + args->eb_size = (uint32_t) ustrtoul(arg, &endp, 0); + CHECK_ENDP("B", endp); + break; + case 'E': /* erasecount */ + args->ec = (uint64_t) strtoul(arg, &endp, 0); + CHECK_ENDP("E", endp); + break; + case 'I': /* id */ + args->id = (uint16_t) strtoul(arg, &endp, 0); + CHECK_ENDP("I", endp); + break; + case 'T': /* type */ + args->type = (uint16_t) strtoul(arg, &endp, 0); + CHECK_ENDP("T", endp); + break; + case 'X': /* versionnr */ + args->version = (uint8_t) strtoul(arg, &endp, 0); + CHECK_ENDP("X", endp); + break; + case 'O': /* offset for volume hdr */ + args->hdr_offset = + (uint32_t) strtoul(arg, &endp, 0); + CHECK_ENDP("O", endp); + break; + + case 'U': /* broken update */ + args->action = ACT_BROKEN_UPDATE; + args->update_block = + (uint32_t) strtoul(arg, &endp, 0); + CHECK_ENDP("U", endp); + break; + + case ARGP_KEY_ARG: + if (!args->fp_in) { + 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 (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, "\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: 0, + doc: doc, + children: NULL, + help_filter: NULL, + argp_domain: NULL, +}; + + +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 */ + argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &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/ubiinfo/ubiflash.h b/ubi-utils/src/ubiinfo/ubiflash.h new file mode 100644 index 0000000..6883879 --- /dev/null +++ b/ubi-utils/src/ubiinfo/ubiflash.h @@ -0,0 +1,185 @@ +#ifndef _UBI_FLASH_H +#define _UBI_FLASH_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. + */ + +/* + * FLASH related data structures and constants for UBI. + * UBI scan analysis. + * + * IPL Initial Program Loader + * SPL Secondary Program Loader + */ + +#include +#include +#include + +#define UBI_BLOCK_IDENT_MAX 16 + +/* Block status information constants */ +enum blockstat { + /* IO Error */ + STAT_IO_FAILED = 1, /* 0xffffffff */ + /* Block is bad */ + STAT_BLOCK_BAD = 2, /* 0xfffffffe */ + /* ECC unrecoverable error */ + STAT_ECC_ERROR = 3, /* 0xfffffffd */ + /* CRC checksum failed */ + STAT_CRC_ERROR = 4, /* 0xfffffffc */ + /* Magic number not available */ + STAT_NO_MAGIC = 5, /* 0xfffffffb */ + /* No image available */ + STAT_NO_IMAGE = 6, + /* Image is invalid */ + STAT_INVALID_IMAGE = 7, + /* Image is defect */ + STAT_DEFECT_IMAGE = 8, +}; + +/* + * Flash types + */ +enum flashtypes { + FLASH_TYPE_NAND = 1, + FLASH_TYPE_NOR, +}; + +/* Nand read buffer size: 2KiB + 64byte spare */ +#define NAND_READ_BUF_SIZE (2048 + 64) + +/* Size of the CRC table */ +#define CRC32_TABLE_SIZE 256 + +/* Image is not available marker for image offs */ +#define UBI_IMAGE_NOT_AVAILABLE 0xFFFFFFFF + +/* Increment this number, whenever you change the structure */ +#define UBI_SCAN_INFO_VERSION 2 + +/* Time measurement as far as the code size allows us to do this */ +#define UBI_TIMESTAMPS 16 + +/** + * struct ubi_scan_info - RAM table filled by IPL scan + * + * @version: Version of the structure + * @bootstatus: Boot status of the current boot + * @flashtype: Flash type (NAND/NOR) + * @flashid: ID of the flash chip + * @flashmfr: Manufacturer ID of the flash chip + * @flashsize: Size of the FLASH + * @blocksize: Eraseblock size + * @blockshift: Shift count to calc block number from offset + * @nrblocks: Number of erase blocks on flash + * @pagesize: Pagesize (NAND) + * @blockinfo: Pointer to an array of block status information + * filled by FLASH scan + * @images: Pointer to FLASH block translation table sorted + * by image type and load order + * @imageblocks: Number of blocks found per image + * @imageoffs: Offset per imagetype to the first + * block in the translation table + * @imagedups duplicate blocks (max. one per volume) + * @imagelen: Length of the loaded image + * @crc32_table: CRC32 table buffer + * @page_buf: Page buffer for NAND FLASH + */ +struct ubi_scan_info { + int version; + unsigned int bootstatus; + unsigned int flashtype; + unsigned int flashid; + unsigned int flashmfr; + unsigned int flashsize; + unsigned int blocksize; + unsigned int blockshift; + unsigned int nrblocks; + unsigned int pagesize; + + struct ubi_vid_hdr *blockinfo; + struct ubi_vid_hdr **images; + unsigned int imageblocks[UBI_BLOCK_IDENT_MAX]; + unsigned int imageoffs[UBI_BLOCK_IDENT_MAX]; + struct ubi_vid_hdr *imagedups[UBI_BLOCK_IDENT_MAX]; + unsigned int imagelen; + uint32_t crc32_table[CRC32_TABLE_SIZE]; + uint8_t page_buf[NAND_READ_BUF_SIZE]; + unsigned int times[UBI_TIMESTAMPS]; +}; + +/* External function definition */ +extern int flash_read(void *buf, unsigned int offs, int len); +extern int flash_read_slice(struct ubi_scan_info *fi, void *buf, + unsigned int offs, int len); +extern void ipl_main(struct ubi_scan_info *fi); + +#ifndef CFG_EXAMPLE_IPL +extern int ipl_scan(struct ubi_scan_info *fi); +extern int ipl_load(struct ubi_scan_info *fi, int nr, uint8_t *loadaddr); + +#define IPL_STATIC + +#else +#define IPL_STATIC static +#endif + +/** + * get_boot_status - get the boot status register + * + * Shift the lower 16 bit into the upper 16 bit and return + * the result. + */ +uint32_t get_boot_status(void); + +/** + * set_boot_status - Set the boot status register + * + * @status: The status value to set + * + */ +void set_boot_status(uint32_t status); + +static inline unsigned int ubi_vid_offset(struct ubi_scan_info *fi) +{ + if (fi->flashtype == FLASH_TYPE_NOR) + return UBI_EC_HDR_SIZE; + else + return fi->pagesize - UBI_VID_HDR_SIZE; +} + +static inline unsigned int ubi_data_offset(struct ubi_scan_info *fi) +{ + if (fi->flashtype == FLASH_TYPE_NOR) + return UBI_EC_HDR_SIZE + UBI_VID_HDR_SIZE; + else + return fi->pagesize; +} + +/** + * IPL checkpoints + */ +#define CHKP_HWINIT 0x3030 +#define CHKP_IPLSCAN_FAILED 0x3034 +#define CHKP_SPL_START 0x3037 +#define CHKP_SPLLOAD_STATUS 0x3130 + +extern void checkpoint(uint32_t cpoint); +extern void switch_watchdog(void); + +#endif diff --git a/ubi-utils/src/ubiinfo/ubiinfo.c b/ubi-utils/src/ubiinfo/ubiinfo.c new file mode 100644 index 0000000..6f7443b --- /dev/null +++ b/ubi-utils/src/ubiinfo/ubiinfo.c @@ -0,0 +1,406 @@ +/* + * 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. + */ + +/* + * Print out information about the UBI table this IPL is using. This + * can be used afterwards to analyze misbehavior of the IPL code. The + * input this program requires is the last 1 MiB DDRAM of our system + * where the scanning table is placed into. + * + * Author: Frank Haverkamp + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define __unused __attribute__((unused)) + +/* This should hopefully be constant and the same in all + * configurations. + */ +#define CFG_IPLSIZE 512 +#define CFG_SPLCODE 512 +#define MEMTOP 0x06600000 /* Sunray 102 MiB */ +#define MEMSIZE 0x00100000 /* 1 MiB */ +#define CODE_SIZE (64 * 1024) + +/* FIXME Except of the memory size this should be defined via + * parameters + * + * CFG_MEMTOP_BAMBOO 0x02000000 + * CFG_MEMTOP_SUNRAY 0x06600000 + */ + +#include "ubiipl.h" +#include "ubiflash.h" + +#define MIN(x,y) ((x)<(y)?(x):(y)) + +#define ERR_RET(rc) { \ + fprintf(stderr, "%s:%d failed rc=%d\n", __func__, \ + __LINE__, (rc)); \ + return (rc); \ + } + +#define VERSION "1.3" + +static error_t parse_opt (int key, char *arg, struct argp_state *state); +const char *argp_program_version = VERSION; +const char *argp_program_bug_address = ""; + +static char doc[] = "\nVersion: " VERSION "\n\t" + " at "__DATE__" "__TIME__"\n" + "\n" + "Test program\n"; + +static struct argp_option options[] = { + /* common settings */ + { .name = "verbose", + .key = 'v', + .arg = "", + .flags = 0, + .doc = "Set verbosity level to ", + .group = OPTION_ARG_OPTIONAL }, + + { .name = "memtop", + .key = 'm', + .arg = "", + .flags = 0, + .doc = "Set top of memory, 102 MiB for Sunray and 16 MiB for Bamboo", + .group = OPTION_ARG_OPTIONAL }, + + { .name = NULL, + .key = 0, + .arg = NULL, + .flags = 0, + .doc = NULL, + .group = 0 }, +}; + +typedef struct test_args { + int verbose; + unsigned long memtop; + char *arg1; + char **options; +} test_args; + +static struct test_args g_args = { + .memtop = MEMTOP, + .verbose = 0, + .arg1 = NULL, + .options = NULL, +}; + +static struct argp argp = { + options: options, + parser: parse_opt, + args_doc: "[last_1MiB_memory.bin]", + doc: doc, + children: NULL, + help_filter: NULL, + argp_domain: NULL, +}; + +static int verbose = 0; + +/** + * @brief Parse the arguments passed into the test case. + * + * @param key The parameter. + * @param arg Argument passed to parameter. + * @param state Location to put information on parameters. + * + * @return error_t + */ +static error_t +parse_opt(int key, char *arg, struct argp_state *state) +{ + /* Get the `input' argument from `argp_parse', which we + know is a pointer to our arguments structure. */ + test_args *args = state->input; + + switch (key) { + /* common settings */ + case 'v': /* --verbose= */ + verbose = args->verbose = strtoul(arg, (char **)NULL, 0); + break; + + case 'm': /* --memtop */ + args->memtop = strtoul(arg, (char **)NULL, 0); + break; + + case ARGP_KEY_NO_ARGS: + /* argp_usage(state); */ + break; + + case ARGP_KEY_ARG: + args->arg1 = arg; + /* Now we consume all the rest of the arguments. + `state->next' is the index in `state->argv' of the + next argument to be parsed, which is the first STRING + we're interested in, so we can just use + `&state->argv[state->next]' as the value for + arguments->strings. + + _In addition_, by setting `state->next' to the end + of the arguments, we can force argp to stop parsing + here and return. */ + + args->options = &state->argv[state->next]; + state->next = state->argc; + break; + + case ARGP_KEY_END: + /* print out message if no arguments are given but PFI + write should be done */ + break; + + default: + return(ARGP_ERR_UNKNOWN); + } + return 0; +} + +static void +hexdump(const char *buf, int len) +{ + char line[16]; + char str[256]; + char dummy[256]; + int j = 0; + + while (len > 0) { + int i; + + strcpy(str, " "); + + for (j = 0; j < MIN(16, len); j++) + line[j] = *buf++; + + for (i = 0; i < j; i++) { + if (!(i & 3)) { + sprintf(dummy, " %.2x", line[i] & 0xff); + strcat(str, dummy); + } else { + sprintf(dummy, "%.2x", line[i] & 0xff); + strcat(str, dummy); + } + } + + /* Print empty space */ + for (; i < 16; i++) + if (!(i & 1)) + strcat(str, " "); + else + strcat(str, " "); + + strcat(str, " "); + for (i = 0; i < j; i++) { + if (isprint(line[i])) { + sprintf(dummy, "%c", line[i]); + strcat(str, dummy); + } else { + strcat(str, "."); + } + } + printf("%s\n", str); + len -= 16; + } +} + +static void +print_status_help(void) +{ + printf("Error Codes from IPL\n"); + printf(" IO Error %d\n", STAT_IO_FAILED); + printf(" Block is bad %d\n", STAT_BLOCK_BAD); + printf(" ECC unrec error %d\n", STAT_ECC_ERROR); + printf(" CRC csum failed %d\n", STAT_CRC_ERROR); + printf(" Magic not avail %d\n", STAT_NO_MAGIC); + printf(" No image avail %d\n", STAT_NO_IMAGE); + printf(" Image is invalid %d\n", STAT_INVALID_IMAGE); + printf(" Image is defect %d\n\n", STAT_DEFECT_IMAGE); + +} + +static void +print_ubi_scan_info(struct ubi_scan_info *fi) +{ + int i; + + printf("ubi_scan_info\n"); + printf(" version %08x\n", ntohl(fi->version)); + printf(" bootstatus %08x\n", ntohl(fi->bootstatus)); + printf(" flashtype %08x\n", ntohl(fi->flashtype)); + printf(" flashid %08x\n", ntohl(fi->flashid)); + printf(" flashmfgr %08x\n", ntohl(fi->flashmfr)); + printf(" flashsize %d bytes (%dM)\n", + ntohl(fi->flashsize), ntohl(fi->flashsize) / (1024 * 1024)); + printf(" blocksize %d bytes\n", ntohl(fi->blocksize)); + printf(" blockshift %d\n", ntohl(fi->blockshift)); + printf(" nrblocks %d\n", ntohl(fi->nrblocks)); + printf(" pagesize %d\n", ntohl(fi->pagesize)); + printf(" imagelen %d\n", ntohl(fi->imagelen)); + printf(" blockinfo %08x\n", ntohl((int)fi->blockinfo)); + + printf(" nr imageblocks imageoffs\n"); + for (i = 0; i < UBI_BLOCK_IDENT_MAX; i++) + printf(" [%2d] %08x %08x\n", i, + ntohl(fi->imageblocks[i]), + ntohl(fi->imageoffs[i])); + + for (i = 0; i < UBI_TIMESTAMPS; i++) { + if (!ntohl(fi->times[i])) + continue; + printf("time[%3d] = %08x %.3f sec\n", i, ntohl(fi->times[i]), + (double)ntohl(fi->times[i]) / 500000000.0); + } + + printf("crc32_table\n"); + hexdump((char *)&fi->crc32_table, sizeof(fi->crc32_table)); + printf("\npage_buf\n"); + hexdump((char *)&fi->page_buf, sizeof(fi->page_buf)); + + printf("\n"); + +} + +static void +print_ubi_block_info(struct ubi_scan_info *fi, + struct ubi_vid_hdr *bi, int nr) +{ + int i; + int unknown = 0; + + printf("\nBINFO\n"); + + for (i = 0; i < nr; i++) { + if ((int)ubi32_to_cpu(bi[i].magic) != UBI_VID_HDR_MAGIC) { + printf("block=%d %08x\n", + i, i * ntohl(fi->blocksize)); +#if 0 + printf("."); + if ((unknown & 0x3f) == 0x3f) + printf("\n"); + unknown++; +#else + hexdump((char *)&bi[i], + sizeof(struct ubi_vid_hdr)); +#endif + } else { + if (unknown) + printf("\n"); + printf("block=%d %08x\n" + " leb_ver=0x%x data_size=%d " + "lnum=%d used_ebs=0x%x\n" + " data_crc=%08x hdr_crc=%08x\n", + i, i * ntohl(fi->blocksize), + ubi32_to_cpu(bi[i].leb_ver), + ubi32_to_cpu(bi[i].data_size), + ubi32_to_cpu(bi[i].lnum), + ubi32_to_cpu(bi[i].used_ebs), + ubi32_to_cpu(bi[i].data_crc), + ubi32_to_cpu(bi[i].hdr_crc)); + hexdump((char *)&bi[i], + sizeof(struct ubi_vid_hdr)); + unknown = 0; + } + } +} + +static int do_read(unsigned int memtop, char *buf, int buf_len __unused) +{ + unsigned long finfo_addr; + unsigned long binfo_addr; + unsigned long images_addr; + unsigned long nrblocks; + unsigned long bi_size; + unsigned long images_size; + struct ubi_scan_info *fi; + struct ubi_vid_hdr *bi; + char *images; + unsigned long memaddr = memtop - MEMSIZE; + + print_status_help(); + + /* Read and print FINFO */ + finfo_addr = MEMSIZE - CFG_IPLSIZE * 1024; + + printf("read info at addr %08lx\n", finfo_addr); + fi = (struct ubi_scan_info *)(buf + finfo_addr); + + binfo_addr = ntohl((unsigned long)fi->blockinfo) - memaddr; + images_addr = ntohl((unsigned long)fi->images) - memaddr; + nrblocks = ntohl(fi->nrblocks); + + printf("BINFO %08lx\n", binfo_addr); + + bi_size = nrblocks * sizeof(struct ubi_vid_hdr); + images_size = nrblocks * sizeof(unsigned int); + + printf("FINFO\n"); + print_ubi_scan_info(fi); + /* hexdump((char *)fi, sizeof(*fi)); */ + + /* Read and print BINFO */ + bi = (struct ubi_vid_hdr *)(buf + binfo_addr); + print_ubi_block_info(fi, bi, nrblocks); + + /* Read and print IMAGES */ + images = buf + images_addr; + printf("\nIMAGES\n"); + hexdump(images, images_size); + + return 0; +} + +int main(int argc, char *argv[]) +{ + char buf[MEMSIZE]; + FILE *fp; + int rc; + + argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &g_args); + + if (!g_args.arg1) { + fprintf(stderr, "Please specify a file " + "name for memory dump!\n"); + exit(EXIT_FAILURE); + } + + memset(buf, 0xAB, sizeof(buf)); + + fp = fopen(g_args.arg1, "r"); + if (!fp) + exit(EXIT_FAILURE); + rc = fread(buf, 1, sizeof(buf), fp); + if (rc != sizeof(buf)) + exit(EXIT_FAILURE); + fclose(fp); + do_read(g_args.memtop, buf, sizeof(buf)); + + exit(EXIT_SUCCESS); +} diff --git a/ubi-utils/src/ubiinfo/ubiipl.h b/ubi-utils/src/ubiinfo/ubiipl.h new file mode 100644 index 0000000..3a8b900 --- /dev/null +++ b/ubi-utils/src/ubiinfo/ubiipl.h @@ -0,0 +1,87 @@ +#ifndef _UBI_IPL_H +#define _UBI_IPL_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. + */ + +/* + * Constants calculated from the CFG_XXX defines + * + * Declaration of the loader function which is invoked by the + * assembler part of the IPL + */ + +/* Size of IPL - is 4K for NAND and can also be 4K for NOR */ +#define IPL_SIZE 4096 + +/* Needed in asm code to upload the data, needed in C-code for CRC32 */ +#define IPL_RAMADDR (CFG_MEMTOP - IPL_SIZE) + +#if !defined(__ASSEMBLY__) + +#include +#include + +/* Address of the flash info structure */ +#define FINFO_ADDR (struct ubi_scan_info *) (CFG_MEMTOP - CFG_IPLSIZE * 1024) + +/* Size of the flash info structure */ +#define FINFO_SIZE sizeof(struct ubi_scan_info) + +/* Blockinfo array address */ +#define BINFO_ADDR (struct ubi_vid_hdr *) ((void *)FINFO_ADDR + FINFO_SIZE) + +/* Number of erase blocks */ +#define NR_ERASE_BLOCKS ((CFG_FLASHSIZE * 1024) / CFG_BLOCKSIZE) + +/* Blockinfo size */ +#define BINFO_SIZE (NR_ERASE_BLOCKS * sizeof(struct ubi_vid_hdr)) + +/* Images array address */ +#define IMAGES_ADDR (struct ubi_vid_hdr **) ((void *)BINFO_ADDR + BINFO_SIZE) + +/* Images array size */ +#define IMAGES_SIZE (NR_ERASE_BLOCKS * sizeof(unsigned int)) + +/* Total size of flash info + blockinfo + images */ +#define INFO_SIZE ((FINFO_SIZE + BINFO_SIZE + IMAGES_SIZE) / sizeof(uint32_t)) + +/* Load address of the SPL */ +#define SPL_ADDR (void *) ((void *)FINFO_ADDR - CFG_SPLCODE * 1024) + +#define IPL_SIZE_CRC32 (IPL_SIZE - sizeof(uint32_t)) +#define IPL_RAMADDR_CRC32 ((void *)(IPL_RAMADDR + sizeof(uint32_t))) + +/* + * Linker script magic to ensure that load_spl() is linked to the + * right place + */ +#define __crc32 __attribute__((__section__(".crc32"))) +#define __entry __attribute__((__section__(".entry.text"))) +#define __unused __attribute__((unused)) + +#define MIN(x,y) ((x)<(y)?(x):(y)) + +#define stop_on_error(x) \ + { while (1); } + +void __entry load_spl(void); +void hardware_init(void); + +#endif /* __ASSEMBLY__ */ + +#endif diff --git a/ubi-utils/src/ubimirror/ubimirror.c b/ubi-utils/src/ubimirror/ubimirror.c new file mode 100644 index 0000000..e43ba10 --- /dev/null +++ b/ubi-utils/src/ubimirror/ubimirror.c @@ -0,0 +1,206 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "error.h" +#include "example_ubi.h" +#include "ubimirror.h" + +typedef enum action_t { + ACT_NORMAL = 0, + ACT_ARGP_ABORT, + ACT_ARGP_ERR, +} action_t; + +#define ABORT_ARGP do { \ + state->next = state->argc; \ + args->action = ACT_ARGP_ABORT; \ +} while (0) + +#define ERR_ARGP do { \ + state->next = state->argc; \ + args->action = ACT_ARGP_ERR; \ +} while (0) + +#define VOL_ARGS_MAX 2 + + +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" + "ubimirror - mirrors ubi volumes.\n"; + +static const char copyright [] __attribute__((unused)) = + "(C) IBM Coorporation 2007"; + + +static struct argp_option options[] = { + { name: "copyright", key: 'c', arg: NULL, flags: 0, + doc: "Print copyright information.", + group: 1 }, + + { name: "side", key: 's', arg: "", flags: 0, + doc: "Use the side as source.", + group: 1 }, + + { name: NULL, key: 0, arg: NULL, flags: 0, doc: NULL, group: 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 error_t +parse_opt(int key, char *arg, struct argp_state *state) +{ + int err = 0; + + myargs *args = state->input; + + switch (key) { + case 'c': + err_msg("%s\n", copyright); + ABORT_ARGP; + break; + case 's': + args->side = get_update_side(arg); + if (args->side < 0) { + err_msg("Unsupported seqnum: %s.\n" + "Supported seqnums are '0' and '1'\n", arg); + ERR_ARGP; + } + break; + case ARGP_KEY_ARG: + /* only two entries allowed */ + if (args->vol_no >= VOL_ARGS_MAX) { + err_msg("\n"); + argp_usage(state); + ERR_ARGP; + } + args->vol[(args->vol_no)++] = arg; + break; + case ARGP_KEY_END: + if (err) { + err_msg("\n"); + argp_usage(state); + ERR_ARGP; + } + break; + default: + return(ARGP_ERR_UNKNOWN); + } + + return 0; +} + +static struct argp argp = { + options: options, + parser: parse_opt, + args_doc: " ", + doc: doc, + children: NULL, + help_filter: NULL, + argp_domain: NULL, +}; + + +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, + }; + + argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &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/ubimkvol/ubimkvol.c b/ubi-utils/src/ubimkvol/ubimkvol.c new file mode 100644 index 0000000..f929252 --- /dev/null +++ b/ubi-utils/src/ubimkvol/ubimkvol.c @@ -0,0 +1,252 @@ +/* + * 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 + * + * 1.0 Initial release + * 1.1 Does not support erase blocks anymore. This is replaced by + * the number of bytes. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#define VERSION "1.1" + +static void usage(void); +static int param_sanity_check(ubi_lib_t lib); +static int parse_options(int argc, char * const argv[]); + +/* + * The variables below are set by command line arguments. + */ +static int vol_type = UBI_DYNAMIC_VOLUME; +static int devn = -1; +static long long bytes = 0; +static int alignment = 1; +static int vol_id = UBI_VOL_NUM_AUTO; +static char *name = NULL; +static int nlen = 0; + +int main(int argc, char * const argv[]) +{ + int err; + ubi_lib_t lib; + + err = parse_options(argc, argv); + if (err) { + fprintf(stderr, "Wrong options ...\n"); + return err == 1 ? 0 : -1; + } + + if (devn == -1) { + fprintf(stderr, "Device number was not specified\n"); + fprintf(stderr, "Use -h option for help\n"); + return -1; + } + + err = ubi_open(&lib); + if (err) { + perror("Cannot open libubi"); + return -1; + } + + err = param_sanity_check(lib); + if (err) { + perror("Input parameters check"); + fprintf(stderr, "Use -h option for help\n"); + goto out_libubi; + } + + err = ubi_mkvol(lib, devn, vol_id, vol_type, bytes, alignment, name); + 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); */ + + vol_id = err; + ubi_close(&lib); + return 0; + +out_libubi: + ubi_close(&lib); + return -1; +} + +/* 'getopt()' option string */ +static const char *optstring = "ht:s:n:N:d:a:"; + +static int parse_options(int argc, char * const argv[]) +{ + int opt = 0; + + while (opt != -1) { + char *endp; + + opt = getopt(argc, argv, optstring); + + switch (opt) { + case 'h': + usage(); + return 1; + case 't': + if (!strcmp(optarg, "dynamic")) + vol_type = UBI_DYNAMIC_VOLUME; + else if (!strcmp(optarg, "static")) + vol_type = UBI_STATIC_VOLUME; + else { + fprintf(stderr, "Bad volume type: \"%s\"\n", + optarg); + goto out; + } + break; + case 's': + bytes = strtoull(optarg, &endp, 0); + if (endp == optarg || bytes < 0) { + fprintf(stderr, "Bad volume size: \"%s\"\n", + optarg); + goto out; + } + if (endp != '\0') { + if (strcmp(endp, "KiB") == 0) + bytes *= 1024; + else if (strcmp(endp, "MiB") == 0) + bytes *= 1024*1024; + } + break; + case 'a': + alignment = strtoul(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || + alignment <= 0) { + fprintf(stderr, "Bad volume alignment: " + "\"%s\"\n", optarg); + goto out; + } + break; + case 'd': + devn = strtoul(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || devn < 0) { + fprintf(stderr, "Bad UBI device number: " + "\"%s\"\n", optarg); + goto out; + } + break; + case 'n': + vol_id = strtoul(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || + (vol_id < 0 && vol_id != UBI_DYNAMIC_VOLUME)) { + fprintf(stderr, "Bad volume ID: " + "\"%s\"\n", optarg); + goto out; + } + break; + case 'N': + name = optarg; + nlen = strlen(name); + break; + + case ':': + fprintf(stderr, "Parameter is missing\n"); + goto out; + case '?': + fprintf(stderr, "Unknown parameter\n"); + goto out; + case -1: + break; + default: + fprintf(stderr, "Internal error\n"); + goto out; + } + } + + return 0; + + out: + errno = EINVAL; + return -1; +} + +static int param_sanity_check(ubi_lib_t lib) +{ + int err, len; + struct ubi_info ubi; + + if (bytes == 0) { + fprintf(stderr, "Volume size was not specified\n"); + goto out; + } + + if (name == NULL) { + fprintf(stderr, "Volume name was not specified\n"); + goto out; + } + + err = ubi_get_info(lib, &ubi); + if (err) + return -1; + + if (devn >= ubi.dev_count) { + fprintf(stderr, "Device %d does not exist\n", devn); + goto out; + } + + len = strlen(name); + if (len > ubi.nlen_max) { + fprintf(stderr, "Too long name (%d symbols), max is %d\n", + len, ubi.nlen_max); + goto out; + } + + return 0; + +out: + errno = EINVAL; + return -1; +} + +static void usage(void) +{ + printf("Usage: ubi_mkvol OPTIONS\n" + "Version: " VERSION "\n" + "The command line options:\n" + "\t-h - this help message\n" + "\t-d - UBI device number\n" + "\t-t TYPE - volume type (dynamic, static) " + "(default is dynamic)\n" + "\t-n VOLID - volume ID to assign to the new volume. If not" + "specified, \n" + "\t the volume ID will be assigned automatically\n" + "\t-s BYTES - volume size in bytes, " + "kilobytes (KiB) or megabytes (MiB)\n" + "\t-N NAME - volume name\n" + "\t-a ALIGNMENT - volume alignment (default is 1)\n"); +} diff --git a/ubi-utils/src/ubirmvol/ubirmvol.c b/ubi-utils/src/ubirmvol/ubirmvol.c new file mode 100644 index 0000000..fc5ada5 --- /dev/null +++ b/ubi-utils/src/ubirmvol/ubirmvol.c @@ -0,0 +1,172 @@ +/* + * 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. + * + * Autor: Artem B. Bityutskiy + */ + +#include +#include +#include +#include +#include +#include +#include + +static void usage(void); +static int param_sanity_check(ubi_lib_t lib); +static int parse_options(int argc, char * const argv[]); + +/* + * The below variables are set by command line options. + */ +static int vol_id = -1; +static int devn = -1; + +int main(int argc, char * const argv[]) +{ + int err, old_errno; + ubi_lib_t lib; + + err = parse_options(argc, argv); + if (err) + return err == 1 ? 0 : -1; + + if (devn == -1) { + fprintf(stderr, "Device number was not specified\n"); + fprintf(stderr, "Use -h option for help\n"); + return -1; + } + + err = ubi_open(&lib); + if (err) { + perror("Cannot open libubi"); + return -1; + } + + err = param_sanity_check(lib); + if (err) { + perror("Input parameters check"); + fprintf(stderr, "Use -h option for help\n"); + goto out_libubi; + } + + err = ubi_rmvol(lib, devn, 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; + } + + return 0; + +out_libubi: + ubi_close(&lib); + return -1; +} + +/* 'getopt()' option string */ +static const char *optstring = "hd:n:"; + +static int parse_options(int argc, char * const argv[]) +{ + int opt = 0; + + while (opt != -1) { + char *endp; + + opt = getopt(argc, argv, optstring); + + switch (opt) { + case 'h': + usage(); + return 1; + case 'n': + vol_id = strtoul(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || vol_id < 0) { + fprintf(stderr, "Bad volume " + "number: \"%s\"\n", optarg); + goto out; + } + break; + case 'd': + devn = strtoul(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || devn < 0) { + fprintf(stderr, "Bad UBI device " + "number: \"%s\"\n", optarg); + goto out; + } + break; + case ':': + fprintf(stderr, "Parameter is missing\n"); + goto out; + case '?': + fprintf(stderr, "Unknown parameter\n"); + goto out; + case -1: + break; + default: + fprintf(stderr, "Internal error\n"); + goto out; + } + } + + return 0; + +out: + errno = EINVAL; + return -1; +} + +static int param_sanity_check(ubi_lib_t lib) +{ + int err; + struct ubi_info ubi; + + if (vol_id == -1) { + fprintf(stderr, "Volume ID was not specified\n"); + goto out; + } + + err = ubi_get_info(lib, &ubi); + if (err) + return -1; + + if (devn >= ubi.dev_count) { + fprintf(stderr, "Device %d does not exist\n", devn); + goto out; + } + + return 0; + +out: + errno = EINVAL; + return -1; +} + +static void usage(void) +{ + printf("Usage: ubi_rmvol OPTIONS\n" + "Command line options:\n" + "\t-h - this help message\n" + "\t-d - UBI device number\n" + "\t-n VOLNUM - volume number to remove\n"); +} diff --git a/ubi-utils/src/ubiwritevol/ubiwritevol.c b/ubi-utils/src/ubiwritevol/ubiwritevol.c new file mode 100644 index 0000000..8fdbe37 --- /dev/null +++ b/ubi-utils/src/ubiwritevol/ubiwritevol.c @@ -0,0 +1,352 @@ +/* + * 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define MAXPATH 1024 +#define BUFSIZE 128 * 1024 +#define MIN(x,y) ((x)<(y)?(x):(y)) + +struct args { + int device; + int volume; + int truncate; + int broken_update; + int bufsize; + + /* special stuff needed to get additional arguments */ + char *arg1; + char **options; /* [STRING...] */ +}; + +static struct args myargs = { + .device = -1, + .volume = -1, + .truncate = 0, + .broken_update = 0, + .bufsize = BUFSIZE, + .arg1 = NULL, + .options = NULL, +}; + +static int verbose = 0; + +static error_t parse_opt (int key, char *arg, struct argp_state *state); + +const char *argp_program_bug_address = ""; + +static char doc[] = "\nVersion: " VERSION "\n\t" + HOST_OS" "HOST_CPU" at "__DATE__" "__TIME__"\n" + "\nWrite to UBI Volume.\n"; + +static struct argp_option options[] = { + { .name = "device", + .key = 'd', + .arg = "", + .flags = 0, + .doc = "UBI device", + .group = OPTION_ARG_OPTIONAL }, + + { .name = "volume", + .key = 'n', + .arg = "", + .flags = 0, + .doc = "UBI volume id", + .group = OPTION_ARG_OPTIONAL }, + + { .name = "truncate", + .key = 't', + .arg = NULL, + .flags = 0, + .doc = "truncate volume", + .group = OPTION_ARG_OPTIONAL }, + + { .name = "broken-update", + .key = 'B', + .arg = NULL, + .flags = 0, + .doc = "broken update, this is for testing", + .group = OPTION_ARG_OPTIONAL }, + + { .name = NULL, .key = 0, .arg = NULL, .flags = 0, + .doc = NULL, .group = 0 }, +}; + +static struct argp argp = { + .options = options, + .parser = parse_opt, + .args_doc = 0, + .doc = doc, + .children = NULL, + .help_filter = NULL, + .argp_domain = NULL, +}; + +/* + * @brief Parse the arguments passed into the test case. + * + * @param key The parameter. + * @param arg Argument passed to parameter. + * @param state Location to put information on parameters. + * + * @return error + * + * Get the `input' argument from `argp_parse', which we know is a + * pointer to our arguments structure. + */ +static error_t +parse_opt(int key, char *arg, struct argp_state *state) +{ + struct args *args = state->input; + + switch (key) { + case 'v': /* --verbose= */ + verbose = strtoul(arg, (char **)NULL, 0); + break; + + case 'd': /* --device= */ + args->device = strtol(arg, (char **)NULL, 0); + break; + + case 'b': /* --bufsize= */ + args->bufsize = strtol(arg, (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 'n': /* --volume= */ + args->volume = strtol(arg, (char **)NULL, 0); + break; + + case ARGP_KEY_NO_ARGS: + /* argp_usage(state); */ + break; + + case ARGP_KEY_ARG: + args->arg1 = arg; + /* Now we consume all the rest of the arguments. + `state->next' is the index in `state->argv' of the + next argument to be parsed, which is the first STRING + we're interested in, so we can just use + `&state->argv[state->next]' as the value for + arguments->strings. + + _In addition_, by setting `state->next' to the end + of the arguments, we can force argp to stop parsing here and + return. */ + + args->options = &state->argv[state->next]; + state->next = state->argc; + break; + + case ARGP_KEY_END: + /* argp_usage(state); */ + break; + + default: + return(ARGP_ERR_UNKNOWN); + } + + 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) +{ + int rc, ofd; + char path[MAXPATH]; + int old_errno; + + snprintf(path, MAXPATH-1, "/dev/ubi%d_%d", args->device, args->volume); + path[MAXPATH-1] = '\0'; + + ofd = open(path, O_RDWR); + if (ofd < 0) { + fprintf(stderr, "Cannot open volume %s\n", path); + exit(EXIT_FAILURE); + } + rc = ioctl(ofd, UBI_IOCVOLUP, &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) +{ + 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 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->device, args->volume); + 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 = ioctl(ofd, UBI_IOCVOLUP, &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; + + argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &myargs); + + if (myargs.truncate) { + rc = ubi_truncate_volume(&myargs, 0LL); + if (rc < 0) + exit(EXIT_FAILURE); + exit(EXIT_SUCCESS); + } + if (myargs.broken_update) { + rc = ubi_truncate_volume(&myargs, 1LL); + if (rc < 0) + exit(EXIT_FAILURE); + exit(EXIT_SUCCESS); + } + rc = ubi_update_volume(&myargs); + if (rc < 0) + exit(EXIT_FAILURE); + + exit(EXIT_SUCCESS); +} diff --git a/ubi-utils/src/unubi/unubi.c b/ubi-utils/src/unubi/unubi.c new file mode 100644 index 0000000..9cb1354 --- /dev/null +++ b/ubi-utils/src/unubi/unubi.c @@ -0,0 +1,391 @@ +/* + * 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 UBI images. Not yet finished ... + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "crc32.h" +#include + +#define MAXPATH 1024 +#define MIN(x,y) ((x)<(y)?(x):(y)) + +static uint32_t crc32_table[256]; + +struct args { + const char *output_dir; + uint32_t hdr_offs; + uint32_t data_offs; + uint32_t blocksize; + + /* special stuff needed to get additional arguments */ + char *arg1; + char **options; /* [STRING...] */ +}; + +static struct args myargs = { + .output_dir = "unubi", + .hdr_offs = 64, + .data_offs = 128, + .blocksize = 128 * 1024, + .arg1 = NULL, + .options = NULL, +}; + +static error_t parse_opt (int key, char *arg, struct argp_state *state); + +const char *argp_program_bug_address = ""; + +static char doc[] = "\nVersion: " VERSION "\n\t" + HOST_OS" "HOST_CPU" at "__DATE__" "__TIME__"\n" + "\nWrite to UBI Volume.\n"; + +static struct argp_option options[] = { + { .name = "dir", + .key = 'd', + .arg = "", + .flags = 0, + .doc = "output directory", + .group = OPTION_ARG_OPTIONAL }, + + { .name = "blocksize", + .key = 'b', + .arg = "", + .flags = 0, + .doc = "blocksize", + .group = OPTION_ARG_OPTIONAL }, + + { .name = "data-offs", + .key = 'x', + .arg = "", + .flags = 0, + .doc = "data offset", + .group = OPTION_ARG_OPTIONAL }, + + { .name = "hdr-offs", + .key = 'x', + .arg = "", + .flags = 0, + .doc = "hdr offset", + .group = OPTION_ARG_OPTIONAL }, + + { .name = NULL, .key = 0, .arg = NULL, .flags = 0, + .doc = NULL, .group = 0 }, +}; + +static struct argp argp = { + .options = options, + .parser = parse_opt, + .args_doc = "image-file", + .doc = doc, + .children = NULL, + .help_filter = NULL, + .argp_domain = NULL, +}; + +/* + * 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 key The parameter. + * @param arg Argument passed to parameter. + * @param state Location to put information on parameters. + * + * @return error + * + * Get the `input' argument from `argp_parse', which we know is a + * pointer to our arguments structure. + */ +static error_t +parse_opt(int key, char *arg, struct argp_state *state) +{ + struct args *args = state->input; + + switch (key) { + case 'b': /* --blocksize */ + args->blocksize = str_to_num(arg); + break; + + case 'x': /* --data-offs= */ + args->data_offs = str_to_num(arg); + break; + + case 'y': /* --hdr-offs= */ + args->hdr_offs = str_to_num(arg); + break; + + case 'd': /* --dir= */ + args->output_dir = arg; + break; + + case ARGP_KEY_NO_ARGS: + /* argp_usage(state); */ + break; + + case ARGP_KEY_ARG: + args->arg1 = arg; + /* Now we consume all the rest of the arguments. + `state->next' is the index in `state->argv' of the + next argument to be parsed, which is the first STRING + we're interested in, so we can just use + `&state->argv[state->next]' as the value for + arguments->strings. + + _In addition_, by setting `state->next' to the end + of the arguments, we can force argp to stop parsing + here and return. */ + + args->options = &state->argv[state->next]; + state->next = state->argc; + break; + + case ARGP_KEY_END: + /* argp_usage(state); */ + break; + + default: + return(ARGP_ERR_UNKNOWN); + } + + return 0; +} + +static inline void +hexdump(FILE *fp, const void *p, size_t size) +{ + int k; + const uint8_t *buf = p; + + for (k = 0; k < size; k++) { + fprintf(fp, "%02x ", buf[k]); + if ((k & 15) == 15) + fprintf(fp, "\n"); + } +} + +/* + * This was put together in 1.5 hours and this is exactly how it looks + * like! FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME + * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME + * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME + * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME + * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME + * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME + * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME + * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME + * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME + * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME + * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME + * FIXME FIXME FIXME FIXME FIXME FIXME! + */ +static int extract_volume(struct args *args, const uint8_t *buf, + int len, int volume, FILE *fp) +{ + int i, rc; + int nrblocks = len / args->blocksize; + int max_lnum = -1, lnum = 0; + const uint8_t *ptr; + uint8_t **vol_tab; + int *vol_len; + + vol_tab = calloc(nrblocks, sizeof(uint8_t *)); + vol_len = calloc(nrblocks, sizeof(int)); + + if (!buf || !vol_tab || !vol_len) + exit(EXIT_FAILURE); + + for (i = 0, ptr = buf; i < nrblocks; i++, ptr += args->blocksize) { + uint32_t crc; + struct ubi_ec_hdr *ec_hdr = (struct ubi_ec_hdr *)ptr; + struct ubi_vid_hdr *vid_hdr = NULL; + uint8_t *data; + + /* default */ + vol_len[lnum] = args->blocksize - (2 * 1024); + + /* Check UBI EC header */ + crc = clc_crc32(crc32_table, UBI_CRC32_INIT, ec_hdr, + UBI_EC_HDR_SIZE_CRC); + if (crc != ubi32_to_cpu(ec_hdr->hdr_crc)) + continue; + + vid_hdr = (struct ubi_vid_hdr *) + (ptr + ubi32_to_cpu(ec_hdr->vid_hdr_offset)); + data = (uint8_t *)(ptr + ubi32_to_cpu(ec_hdr->data_offset)); + + crc = clc_crc32(crc32_table, UBI_CRC32_INIT, vid_hdr, + UBI_VID_HDR_SIZE_CRC); + if (crc != ubi32_to_cpu(vid_hdr->hdr_crc)) + continue; + + if (volume == ubi32_to_cpu(vid_hdr->vol_id)) { + + printf("****** block %4d volume %2d **********\n", + i, volume); + + hexdump(stdout, ptr, 64); + + printf("--- vid_hdr\n"); + hexdump(stdout, vid_hdr, 64); + + printf("--- data\n"); + hexdump(stdout, data, 64); + + lnum = ubi32_to_cpu(vid_hdr->lnum); + vol_tab[lnum] = data; + if (max_lnum < lnum) + max_lnum = lnum; + if (vid_hdr->vol_type == UBI_VID_STATIC) + vol_len[lnum] = + ubi32_to_cpu(vid_hdr->data_size); + + } + } + + for (lnum = 0; lnum <= max_lnum; lnum++) { + if (vol_tab[lnum]) { + rc = fwrite(vol_tab[lnum], 1, vol_len[lnum], fp); + if (ferror(fp) || (vol_len[lnum] != rc)) { + perror("could not write file"); + exit(EXIT_FAILURE); + } + } else { + /* Fill up empty areas by 0xff, for static + * volumes this means they are broken! + */ + for (i = 0; i < vol_len[lnum]; i++) { + if (fputc(0xff, fp) == EOF) { + perror("could not write char"); + exit(EXIT_FAILURE); + } + } + } + } + + free(vol_tab); + free(vol_len); + return 0; +} + +int +main(int argc, char *argv[]) +{ + int len, rc; + FILE *fp; + struct stat file_info; + uint8_t *buf; + int i; + + init_crc32_table(crc32_table); + + argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &myargs); + + if (!myargs.arg1) { + fprintf(stderr, "Please specify input file!\n"); + exit(EXIT_FAILURE); + } + + fp = fopen(myargs.arg1, "r"); + if (!fp) { + perror("Cannot open file"); + exit(EXIT_FAILURE); + } + if (fstat(fileno(fp), &file_info) != 0) { + fprintf(stderr, "Cannot fetch file size " + "from input file.\n"); + } + len = file_info.st_size; + buf = malloc(len); + if (!buf) { + perror("out of memory!"); + exit(EXIT_FAILURE); + } + rc = fread(buf, 1, len, fp); + if (ferror(fp) || (len != rc)) { + perror("could not read file"); + exit(EXIT_FAILURE); + } + if (!myargs.output_dir) { + fprintf(stderr, "No output directory specified!\n"); + exit(EXIT_FAILURE); + } + + rc = mkdir(myargs.output_dir, 0777); + if (rc && errno != EEXIST) { + perror("Cannot create output directory"); + exit(EXIT_FAILURE); + } + for (i = 0; i < 32; i++) { + char fname[1024]; + FILE *fpout; + + printf("######### VOLUME %d ############################\n", + i); + + sprintf(fname, "%s/ubivol_%d.bin", myargs.output_dir, i); + fpout = fopen(fname, "w+"); + if (!fpout) { + perror("Cannot open file"); + exit(EXIT_FAILURE); + } + extract_volume(&myargs, buf, len, i, fpout); + fclose(fpout); + } + + fclose(fp); + free(buf); + exit(EXIT_SUCCESS); +} 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. -- cgit v1.2.3 From 6ccd7242c4c1404dafb64cd937adc3c65ce02385 Mon Sep 17 00:00:00 2001 From: Frank Haverkamp Date: Wed, 21 Jun 2006 14:26:02 +0200 Subject: [MTD] UBI: Removed automake, autoconf, added ubi userspace headers. Signed-off-by: Frank Haverkamp --- include/mtd/ubi-header.h | 390 ++++++++++++ include/mtd/ubi-user.h | 128 ++++ ubi-utils/Makefile | 126 ++-- ubi-utils/Makefile.am | 96 --- ubi-utils/bootstrap | 15 - ubi-utils/configure.ac | 52 -- ubi-utils/inc/Makefile.am | 5 - ubi-utils/inc/bootenv.h | 415 ------------- ubi-utils/inc/config-h.in | 74 --- ubi-utils/inc/crc32.h | 36 -- ubi-utils/inc/error.h | 84 --- ubi-utils/inc/example_ubi.h | 28 - ubi-utils/inc/list.h | 56 -- ubi-utils/inc/nandecc.h | 28 - ubi-utils/inc/peb.h | 41 -- ubi-utils/inc/pfi.h | 244 -------- ubi-utils/inc/pfiflash.h | 62 -- ubi-utils/inc/reader.h | 84 --- ubi-utils/inc/ubigen.h | 149 ----- ubi-utils/inc/ubimirror.h | 66 -- ubi-utils/perl/f128_nand_sample.cfg | 38 ++ ubi-utils/perl/f64_nor_sample.cfg | 39 ++ ubi-utils/perl/mkpfi | 723 ++++++++++++++++++++++ ubi-utils/perl/ubicrc32.pl | 74 +++ ubi-utils/scripts/ubi_test.sh | 2 +- ubi-utils/scripts/ubi_tools_test.sh | 2 +- ubi-utils/src/bin2nand.c | 356 +++++++++++ ubi-utils/src/bin2nand/bin2nand.c | 343 ----------- ubi-utils/src/bin2nand/nandecc.c | 159 ----- ubi-utils/src/bin2nand/nandecc.h | 11 - ubi-utils/src/bootenv.c | 961 ++++++++++++++++++++++++++++++ ubi-utils/src/bootenv.h | 415 +++++++++++++ ubi-utils/src/config.h | 28 + ubi-utils/src/crc32.c | 83 +++ ubi-utils/src/crc32.h | 36 ++ ubi-utils/src/error.c | 177 ++++++ ubi-utils/src/error.h | 84 +++ ubi-utils/src/example_ubi.h | 28 + ubi-utils/src/hashmap.c | 412 +++++++++++++ ubi-utils/src/hashmap.h | 49 ++ ubi-utils/src/libbootenv/bootenv.c | 959 ----------------------------- ubi-utils/src/libbootenv/hashmap.c | 412 ------------- ubi-utils/src/libbootenv/hashmap.h | 49 -- ubi-utils/src/libcrc32/crc32.c | 83 --- ubi-utils/src/liberror/error.c | 177 ------ ubi-utils/src/liblist/list.c | 149 ----- ubi-utils/src/libpeb/peb.c | 116 ---- ubi-utils/src/libpfi/pfi.c | 461 -------------- ubi-utils/src/libpfiflash.c | 619 +++++++++++++++++++ ubi-utils/src/libpfiflash/pfiflash.c | 617 ------------------- ubi-utils/src/libreader/reader.c | 442 -------------- ubi-utils/src/libubi.c | 773 ++++++++++++++++++++++++ ubi-utils/src/libubi/libubi.c | 773 ------------------------ ubi-utils/src/libubi/libubi_int.h | 119 ---- ubi-utils/src/libubi/libubi_sysfs.c | 231 ------- ubi-utils/src/libubi/libubi_sysfs.h | 109 ---- ubi-utils/src/libubi_int.h | 119 ++++ ubi-utils/src/libubi_sysfs.c | 232 ++++++++ ubi-utils/src/libubi_sysfs.h | 109 ++++ ubi-utils/src/libubigen.c | 486 +++++++++++++++ ubi-utils/src/libubigen/ubigen.c | 486 --------------- ubi-utils/src/libubimirror.c | 217 +++++++ ubi-utils/src/libubimirror/ubimirror.c | 217 ------- ubi-utils/src/list.c | 149 +++++ ubi-utils/src/list.h | 56 ++ ubi-utils/src/mkbootenv.c | 173 ++++++ ubi-utils/src/mkbootenv/mkbootenv.c | 173 ------ ubi-utils/src/mkpfi/f128_nand_sample.cfg | 38 -- ubi-utils/src/mkpfi/f64_nor_sample.cfg | 39 -- ubi-utils/src/mkpfi/mkpfi | 723 ---------------------- ubi-utils/src/nand2bin.c | 328 ++++++++++ ubi-utils/src/nand2bin/nand2bin.c | 327 ---------- ubi-utils/src/nand2bin/nandcorr.c | 85 --- ubi-utils/src/nandcorr.c | 85 +++ ubi-utils/src/nandecc.c | 159 +++++ ubi-utils/src/nandecc.h | 28 + ubi-utils/src/pddcustomize.c | 496 +++++++++++++++ ubi-utils/src/pddcustomize/pddcustomize.c | 496 --------------- ubi-utils/src/peb.c | 116 ++++ ubi-utils/src/peb.h | 41 ++ ubi-utils/src/pfi.c | 461 ++++++++++++++ ubi-utils/src/pfi.h | 244 ++++++++ ubi-utils/src/pfi2bin.c | 678 +++++++++++++++++++++ ubi-utils/src/pfi2bin/pfi2bin.c | 678 --------------------- ubi-utils/src/pfiflash.c | 243 ++++++++ ubi-utils/src/pfiflash.h | 62 ++ ubi-utils/src/pfiflash/pfiflash.c | 243 -------- ubi-utils/src/reader.c | 452 ++++++++++++++ ubi-utils/src/reader.h | 84 +++ ubi-utils/src/ubicrc32.c | 143 +++++ ubi-utils/src/ubicrc32/ubicrc32.c | 143 ----- ubi-utils/src/ubicrc32/ubicrc32.pl | 74 --- ubi-utils/src/ubigen.c | 369 ++++++++++++ ubi-utils/src/ubigen.h | 149 +++++ ubi-utils/src/ubigen/ubigen_main.c | 369 ------------ ubi-utils/src/ubiinfo/ubiflash.h | 185 ------ ubi-utils/src/ubiinfo/ubiinfo.c | 406 ------------- ubi-utils/src/ubiinfo/ubiipl.h | 87 --- ubi-utils/src/ubimirror.c | 206 +++++++ ubi-utils/src/ubimirror.h | 66 ++ ubi-utils/src/ubimirror/ubimirror.c | 206 ------- ubi-utils/src/ubimkvol.c | 251 ++++++++ ubi-utils/src/ubimkvol/ubimkvol.c | 252 -------- ubi-utils/src/ubirmvol.c | 174 ++++++ ubi-utils/src/ubirmvol/ubirmvol.c | 172 ------ ubi-utils/src/ubiupdatevol.c | 352 +++++++++++ ubi-utils/src/ubiwritevol/ubiwritevol.c | 352 ----------- ubi-utils/src/unubi.c | 392 ++++++++++++ ubi-utils/src/unubi/unubi.c | 391 ------------ 109 files changed, 12603 insertions(+), 12947 deletions(-) create mode 100644 include/mtd/ubi-header.h create mode 100644 include/mtd/ubi-user.h delete mode 100644 ubi-utils/Makefile.am delete mode 100755 ubi-utils/bootstrap delete mode 100644 ubi-utils/configure.ac delete mode 100644 ubi-utils/inc/Makefile.am delete mode 100644 ubi-utils/inc/bootenv.h delete mode 100644 ubi-utils/inc/config-h.in delete mode 100644 ubi-utils/inc/crc32.h delete mode 100644 ubi-utils/inc/error.h delete mode 100644 ubi-utils/inc/example_ubi.h delete mode 100644 ubi-utils/inc/list.h delete mode 100644 ubi-utils/inc/nandecc.h delete mode 100644 ubi-utils/inc/peb.h delete mode 100644 ubi-utils/inc/pfi.h delete mode 100644 ubi-utils/inc/pfiflash.h delete mode 100644 ubi-utils/inc/reader.h delete mode 100644 ubi-utils/inc/ubigen.h delete mode 100644 ubi-utils/inc/ubimirror.h create mode 100644 ubi-utils/perl/f128_nand_sample.cfg create mode 100644 ubi-utils/perl/f64_nor_sample.cfg create mode 100755 ubi-utils/perl/mkpfi create mode 100755 ubi-utils/perl/ubicrc32.pl create mode 100644 ubi-utils/src/bin2nand.c delete mode 100644 ubi-utils/src/bin2nand/bin2nand.c delete mode 100644 ubi-utils/src/bin2nand/nandecc.c delete mode 100644 ubi-utils/src/bin2nand/nandecc.h create mode 100644 ubi-utils/src/bootenv.c create mode 100644 ubi-utils/src/bootenv.h create mode 100644 ubi-utils/src/config.h create mode 100644 ubi-utils/src/crc32.c create mode 100644 ubi-utils/src/crc32.h create mode 100644 ubi-utils/src/error.c create mode 100644 ubi-utils/src/error.h create mode 100644 ubi-utils/src/example_ubi.h create mode 100644 ubi-utils/src/hashmap.c create mode 100644 ubi-utils/src/hashmap.h delete mode 100644 ubi-utils/src/libbootenv/bootenv.c delete mode 100644 ubi-utils/src/libbootenv/hashmap.c delete mode 100644 ubi-utils/src/libbootenv/hashmap.h delete mode 100644 ubi-utils/src/libcrc32/crc32.c delete mode 100644 ubi-utils/src/liberror/error.c delete mode 100644 ubi-utils/src/liblist/list.c delete mode 100644 ubi-utils/src/libpeb/peb.c delete mode 100644 ubi-utils/src/libpfi/pfi.c create mode 100644 ubi-utils/src/libpfiflash.c delete mode 100644 ubi-utils/src/libpfiflash/pfiflash.c delete mode 100644 ubi-utils/src/libreader/reader.c create mode 100644 ubi-utils/src/libubi.c delete mode 100644 ubi-utils/src/libubi/libubi.c delete mode 100644 ubi-utils/src/libubi/libubi_int.h delete mode 100644 ubi-utils/src/libubi/libubi_sysfs.c delete mode 100644 ubi-utils/src/libubi/libubi_sysfs.h create mode 100644 ubi-utils/src/libubi_int.h create mode 100644 ubi-utils/src/libubi_sysfs.c create mode 100644 ubi-utils/src/libubi_sysfs.h create mode 100644 ubi-utils/src/libubigen.c delete mode 100644 ubi-utils/src/libubigen/ubigen.c create mode 100644 ubi-utils/src/libubimirror.c delete mode 100644 ubi-utils/src/libubimirror/ubimirror.c create mode 100644 ubi-utils/src/list.c create mode 100644 ubi-utils/src/list.h create mode 100644 ubi-utils/src/mkbootenv.c delete mode 100644 ubi-utils/src/mkbootenv/mkbootenv.c delete mode 100644 ubi-utils/src/mkpfi/f128_nand_sample.cfg delete mode 100644 ubi-utils/src/mkpfi/f64_nor_sample.cfg delete mode 100755 ubi-utils/src/mkpfi/mkpfi create mode 100644 ubi-utils/src/nand2bin.c delete mode 100644 ubi-utils/src/nand2bin/nand2bin.c delete mode 100644 ubi-utils/src/nand2bin/nandcorr.c create mode 100644 ubi-utils/src/nandcorr.c create mode 100644 ubi-utils/src/nandecc.c create mode 100644 ubi-utils/src/nandecc.h create mode 100644 ubi-utils/src/pddcustomize.c delete mode 100644 ubi-utils/src/pddcustomize/pddcustomize.c create mode 100644 ubi-utils/src/peb.c create mode 100644 ubi-utils/src/peb.h create mode 100644 ubi-utils/src/pfi.c create mode 100644 ubi-utils/src/pfi.h create mode 100644 ubi-utils/src/pfi2bin.c delete mode 100644 ubi-utils/src/pfi2bin/pfi2bin.c create mode 100644 ubi-utils/src/pfiflash.c create mode 100644 ubi-utils/src/pfiflash.h delete mode 100644 ubi-utils/src/pfiflash/pfiflash.c create mode 100644 ubi-utils/src/reader.c create mode 100644 ubi-utils/src/reader.h create mode 100644 ubi-utils/src/ubicrc32.c delete mode 100644 ubi-utils/src/ubicrc32/ubicrc32.c delete mode 100755 ubi-utils/src/ubicrc32/ubicrc32.pl create mode 100644 ubi-utils/src/ubigen.c create mode 100644 ubi-utils/src/ubigen.h delete mode 100644 ubi-utils/src/ubigen/ubigen_main.c delete mode 100644 ubi-utils/src/ubiinfo/ubiflash.h delete mode 100644 ubi-utils/src/ubiinfo/ubiinfo.c delete mode 100644 ubi-utils/src/ubiinfo/ubiipl.h create mode 100644 ubi-utils/src/ubimirror.c create mode 100644 ubi-utils/src/ubimirror.h delete mode 100644 ubi-utils/src/ubimirror/ubimirror.c create mode 100644 ubi-utils/src/ubimkvol.c delete mode 100644 ubi-utils/src/ubimkvol/ubimkvol.c create mode 100644 ubi-utils/src/ubirmvol.c delete mode 100644 ubi-utils/src/ubirmvol/ubirmvol.c create mode 100644 ubi-utils/src/ubiupdatevol.c delete mode 100644 ubi-utils/src/ubiwritevol/ubiwritevol.c create mode 100644 ubi-utils/src/unubi.c delete mode 100644 ubi-utils/src/unubi/unubi.c diff --git a/include/mtd/ubi-header.h b/include/mtd/ubi-header.h new file mode 100644 index 0000000..1b325a1 --- /dev/null +++ b/include/mtd/ubi-header.h @@ -0,0 +1,390 @@ +/* + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: Artem B. Bityutskiy + * Thomas Gleixner + * Frank Haverkamp + * Oliver Lohmann + * Andreas Arnez + */ + +/* + * This file defines the layout of UBI headers and all the other UBI on-flash + * data structures. + */ + +#ifndef __UBI_HEADER_H__ +#define __UBI_HEADER_H__ + +#include + +/* The version of this UBI implementation */ +#define UBI_VERSION 1 + +/* The highest erase counter value supported by this implementation of UBI */ +#define UBI_MAX_ERASECOUNTER 0x7FFFFFFF + +/* The initial CRC32 value used when calculating CRC checksums */ +#define UBI_CRC32_INIT 0xFFFFFFFFU + +/** + * Magic numbers of the UBI headers. + * + * @UBI_EC_HDR_MAGIC: erase counter header magic number (ASCII "UBI#") + * @UBI_VID_HDR_MAGIC: volume identifier header magic number (ASCII "UBI!") + */ +enum { + UBI_EC_HDR_MAGIC = 0x55424923, + UBI_VID_HDR_MAGIC = 0x55424921 +}; + +/** + * Molume type constants used in volume identifier headers. + * + * @UBI_VID_DYNAMIC: dynamic volume + * @UBI_VID_STATIC: static volume + */ +enum { + UBI_VID_DYNAMIC = 1, + UBI_VID_STATIC = 2 +}; + +/** + * Compatibility constants used by internal volumes. + * + * @UBI_COMPAT_DELETE: delete this internal volume before anything is written + * to the flash + * @UBI_COMPAT_RO: attach this device in read-only mode + * @UBI_COMPAT_IGNORE: ignore this internal volume, but the UBI wear-leveling + * unit may still move these logical eraseblocks to ensure wear-leveling + * @UBI_COMPAT_PRESERVE: preserve this internal volume - do not touch its + * physical eraseblocks, don't even allow the wear-leveling unit to move + * them + * @UBI_COMPAT_REJECT: reject this UBI image + */ +enum { + UBI_COMPAT_DELETE = 1, + UBI_COMPAT_RO = 2, + UBI_COMPAT_IGNORE = 3, + UBI_COMPAT_PRESERVE = 4, + UBI_COMPAT_REJECT = 5 +}; + +/* + * ubi16_t/ubi32_t/ubi64_t - 16, 32, and 64-bit integers used in UBI on-flash + * data structures. + */ +typedef struct { + uint16_t int16; +} __attribute__ ((packed)) ubi16_t; + +typedef struct { + uint32_t int32; +} __attribute__ ((packed)) ubi32_t; + +typedef struct { + uint64_t int64; +} __attribute__ ((packed)) ubi64_t; + +/* + * In this implementation UBI uses the big-endian format for on-flash integers. + * The below are the corresponding endianess conversion macros. + */ +#define cpu_to_ubi16(x) ((ubi16_t){__cpu_to_be16(x)}) +#define ubi16_to_cpu(x) ((uint16_t)__be16_to_cpu((x).int16)) + +#define cpu_to_ubi32(x) ((ubi32_t){__cpu_to_be32(x)}) +#define ubi32_to_cpu(x) ((uint32_t)__be32_to_cpu((x).int32)) + +#define cpu_to_ubi64(x) ((ubi64_t){__cpu_to_be64(x)}) +#define ubi64_to_cpu(x) ((uint64_t)__be64_to_cpu((x).int64)) + +/* + * Sizes of UBI headers. + */ +#define UBI_EC_HDR_SIZE sizeof(struct ubi_ec_hdr) +#define UBI_VID_HDR_SIZE sizeof(struct ubi_vid_hdr) + +/* + * Sizes of UBI headers without the ending CRC. + */ +#define UBI_EC_HDR_SIZE_CRC (UBI_EC_HDR_SIZE - sizeof(ubi32_t)) +#define UBI_VID_HDR_SIZE_CRC (UBI_VID_HDR_SIZE - sizeof(ubi32_t)) + +/* + * How much private data may internal volumes store in the VID header. + */ +#define UBI_VID_HDR_IVOL_DATA_SIZE 12 + +/** + * struct ubi_ec_hdr - UBI erase counter header. + * + * @magic: the erase counter header magic number (%UBI_EC_HDR_MAGIC) + * @version: the version of UBI implementation which is supposed to accept this + * UBI image (%UBI_VERSION) + * @padding1: reserved for future, zeroes + * @ec: the erase counter + * @vid_hdr_offset: where the VID header begins + * @data_offset: where the user data begins + * @padding2: reserved for future, zeroes + * @hdr_crc: the erase counter header CRC checksum + * + * The erase counter header takes 64 bytes and has a plenty of unused space for + * future usage. The unused fields are zeroed. The @version field is used to + * indicate the version of UBI implementation which is supposed to be able to + * work with this UBI image. If @version is greater then the current UBI + * version, the image is rejecter. This may be useful in future if something + * is changed radically. This field is duplicated in the volume identifier + * header. + * + * The @vid_hdr_offset and @data_offset fields contain the offset of the the + * volume identifier header and user data, relative to the beginning of the + * eraseblock. These values have to be the same for all eraseblocks. + */ +struct ubi_ec_hdr { + ubi32_t magic; + uint8_t version; + uint8_t padding1[3]; + ubi64_t ec; /* Warning: the current limit is 31-bit anyway! */ + ubi32_t vid_hdr_offset; + ubi32_t data_offset; + uint8_t padding2[36]; + ubi32_t hdr_crc; +} __attribute__ ((packed)); + +/** + * struct ubi_vid_hdr - on-flash UBI volume identifier header. + * + * @magic: volume identifier header magic number (%UBI_VID_HDR_MAGIC) + * @version: UBI implementation version which is supposed to accept this UBI + * image (%UBI_VERSION) + * @vol_type: volume type (%UBI_VID_DYNAMIC or %UBI_VID_STATIC) + * @copy_flag: a flag indicating if this physical eraseblock was created by + * means of copying an original physical eraseblock to ensure wear-leveling. + * @compat: compatibility of this volume (%UBI_COMPAT_DELETE, + * %UBI_COMPAT_IGNORE, %UBI_COMPAT_PRESERVE, or %UBI_COMPAT_REJECT) + * @vol_id: volume ID + * @lnum: logical eraseblock number + * @leb_ver: eraseblock copy number + * @data_size: how many bytes of data this eraseblock contains. + * @used_ebs: total number of used logical eraseblocks in this volume + * @data_pad: how many bytes at the end of this eraseblock are not used + * @data_crc: CRC checksum of data containing in this eraseblock + * @padding1: reserved for future, zeroes + * @ivol_data: private data of internal volumes + * @hdr_crc: volume identifier header CRC checksum + * + * The @leb_ver and the @copy_flag fields are used to distinguish between older + * and newer copies of logical eraseblocks, as well as to guarantee robustness + * to unclean reboots. As UBI erases logical eraseblocks asynchronously, it has + * to distinguish between older and newer copies of eraseblocks. This is done + * using the @version field. On the other hand, when UBI moves an eraseblock, + * its version is also increased and the @copy_flag is set to 1. Additionally, + * when moving eraseblocks, UBI calculates data CRC and stores it in the + * @data_crc field, even for dynamic volumes. + * + * Thus, if there are 2 eraseblocks of the same volume and logical number, UBI + * uses the following algorithm to pick one of them. It first picks the one + * with larger version (say, A). If @copy_flag is not set, then A is picked. If + * @copy_flag is set, UBI checks the CRC of the eraseblock (@data_crc). This is + * needed to ensure that copying was finished. If the CRC is all right, A is + * picked. If not, the older eraseblock is picked. + * + * Note, the @leb_ver field may overflow. Thus, if you have 2 versions A and B, + * then A > B if abs(A-B) < 0x7FFFFFFF, and A < B otherwise. + * + * There are 2 sorts of volumes in UBI: user volumes and internal volumes. + * Internal volumes are not seen from outside and are used for different + * internal UBI purposes. In this implementation there are only two internal + * volumes: the layout volume and the update volume. Internal volumes are the + * main mechanism of UBI extensions. For example, in future one may introduce a + * journal internal volume. + * + * The @compat field is only used for internal volumes and contains the degree + * of their compatibility. This field is always zero for user volumes. This + * field provides a mechanism to introduce UBI extensions and to be still + * compatible with older UBI binaries. For example, if someone introduced an + * journal internal volume in future, he would probably use %UBI_COMPAT_DELETE + * compatibility. And in this case, older UBI binaries, which know nothing + * about the journal volume, would just delete this and work perfectly fine. + * This is somewhat similar to what Ext2fs does when it is fed by an Ext3fs + * image - it just ignores the Ext3fs journal. + * + * The @data_crc field contains the CRC checksum of the contents of the logical + * eraseblock if this is a static volume. In case of dynamic volumes, it does + * not contain the CRC checksum as a rule. The only exception is when the + * logical eraseblock was moved by the wear-leveling unit, then the + * wear-leveling unit calculates the eraseblocks' CRC and stores it at + * @data_crc. + * + * The @data_size field is always used for static volumes because we want to + * know about how many bytes of data are stored in this eraseblock. For + * dynamic eraseblocks, this field usually contains zero. The only exception is + * when the logical eraseblock is moved to another physical eraseblock due to + * wear-leveling reasons. In this case, UBI calculates CRC checksum of the + * contents and uses both @data_crc and @data_size fields. In this case, the + * @data_size field contains the size of logical eraseblock of this volume + * (which may vary owing to @alignment). + * + * The @used_ebs field is used only for static volumes and indicates how many + * eraseblocks the data of the volume takes. For dynamic volumes this field is + * not used and always contains zero. + * + * The @data_pad is calculated when volumes are created using the alignment + * parameter. So, effectively, the @data_pad field reduces the size of logical + * eraseblocks of this volume. This is very handy when one uses block-oriented + * software (say, cramfs) on top of the UBI volume. + * + * The @ivol_data contains private data of internal volumes. This might be very + * handy to store data in the VID header, not in the eraseblock's contents. For + * example it may make life of simple boot-loaders easier. The @ivol_data field + * contains zeroes for user volumes. + */ +struct ubi_vid_hdr { + ubi32_t magic; + uint8_t version; + uint8_t vol_type; + uint8_t copy_flag; + uint8_t compat; + ubi32_t vol_id; + ubi32_t lnum; + ubi32_t leb_ver; + ubi32_t data_size; + ubi32_t used_ebs; + ubi32_t data_pad; + ubi32_t data_crc; + uint8_t padding1[12]; + uint8_t ivol_data[UBI_VID_HDR_IVOL_DATA_SIZE]; + ubi32_t hdr_crc; +} __attribute__ ((packed)); + +/** + * struct ubi_vid_hdr_upd_vol - private data of the update internal volume + * stored in volume identifier headers. + * + * @vol_id: volume ID of the volume under update + * @padding: zeroes + */ +struct ubi_vid_hdr_upd_vol { + ubi32_t vol_id; + uint8_t padding[UBI_VID_HDR_IVOL_DATA_SIZE - 4]; +} __attribute__ ((packed)); + +/* + * Count of internal UBI volumes. + */ +#define UBI_INT_VOL_COUNT 2 + +/* + * Internal volume IDs start from this digit. There is a reserved room for 4096 + * internal volumes. + */ +#define UBI_INTERNAL_VOL_START (0x7FFFFFFF - 4096) + +/** + * enum ubi_internal_volume_numbers - volume IDs of internal UBI volumes. + * + * %UBI_LAYOUT_VOL_ID: volume ID of the layout volume + * %UBI_UPDATE_VOL_ID: volume ID of the update volume + */ +enum ubi_internal_volume_ids { + UBI_LAYOUT_VOL_ID = UBI_INTERNAL_VOL_START, + UBI_UPDATE_VOL_ID = UBI_INTERNAL_VOL_START + 1 +}; + +/* + * Number of logical eraseblocks reserved for internal volumes. + */ +#define UBI_LAYOUT_VOLUME_EBS 2 +#define UBI_UPDATE_VOLUME_EBS 1 + +/* + * Names of internal volumes + */ +#define UBI_LAYOUT_VOLUME_NAME "The layout volume" +#define UBI_UPDATE_VOLUME_NAME "The update volume" + +/* + * Compatibility flags of internal volumes. + */ +#define UBI_LAYOUT_VOLUME_COMPAT UBI_COMPAT_REJECT +#define UBI_UPDATE_VOLUME_COMPAT UBI_COMPAT_REJECT + +/* + * The maximum number of volumes per one UBI device. + */ +#define UBI_MAX_VOLUMES 128 + +/* + * The maximum volume name length. + */ +#define UBI_VOL_NAME_MAX 127 + +/* + * Size of volume table records. + */ +#define UBI_VTBL_RECORD_SIZE sizeof(struct ubi_vol_tbl_record) + +/* + * Size of volume table records without the ending CRC. + */ +#define UBI_VTBL_RECORD_SIZE_CRC (UBI_VTBL_RECORD_SIZE - sizeof(ubi32_t)) + +/** + * struct ubi_vol_tbl_record - a record in the volume table. + * + * @reserved_pebs: how many physical eraseblocks are reserved for this volume + * @alignment: volume alignment + * @data_pad: how many bytes are not used at the end of the eraseblocks to + * satisfy the requested alignment + * @padding1: reserved, zeroes + * @name_len: the volume name length + * @name: the volume name + * @padding2: reserved, zeroes + * @crc: a CRC32 checksum of the record + * + * The layout volume consists of 2 logical eraseblock, each of which contains + * the volume table (i.e., the volume table is duplicated). The volume table is + * an array of &struct ubi_vol_tbl_record objects indexed by the volume ID. + * + * If the size of the logical eraseblock is large enough to fit + * %UBI_MAX_VOLUMES, the volume table contains %UBI_MAX_VOLUMES records. + * Otherwise, it contains as much records as can be fit (i.e., size of logical + * eraseblock divided by sizeof(struct ubi_vol_tbl_record)). + * + * The @alignment field is specified when the volume is created and cannot be + * later changed. It may be useful, for example, when a block-oriented file + * system works on top of UBI. The @data_pad field is calculated using the + * logical eraseblock size and @alignment. The alignment must be multiple to the + * minimal flash I/O unit. If @alignment is 1, all the available space of + * eraseblocks is used. + * + * Empty records contain all zeroes and the CRC checksum of those zeroes. + */ +struct ubi_vol_tbl_record { + ubi32_t reserved_pebs; + ubi32_t alignment; + ubi32_t data_pad; + uint8_t vol_type; + uint8_t padding1; + ubi16_t name_len; + uint8_t name[UBI_VOL_NAME_MAX + 1]; + uint8_t padding2[24]; + ubi32_t crc; +} __attribute__ ((packed)); + +#endif /* !__UBI_HEADER_H__ */ diff --git a/include/mtd/ubi-user.h b/include/mtd/ubi-user.h new file mode 100644 index 0000000..161f674 --- /dev/null +++ b/include/mtd/ubi-user.h @@ -0,0 +1,128 @@ +/* + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem B. Bityutskiy + */ + +#ifndef __UBI_USER_H__ +#define __UBI_USER_H__ + +#ifndef __KERNEL__ +#define __user +#endif + +/* + * When a new volume is created, users may either specify the volume number they + * want to create or to let UBI automatically assign a volume number using this + * constant. + */ +#define UBI_VOL_NUM_AUTO (-1) + +/* + * IOCTL commands of UBI character devices + */ + +#define UBI_IOC_MAGIC 'o' + +/* Create an UBI volume */ +#define UBI_IOCMKVOL _IOW(UBI_IOC_MAGIC, 0, struct ubi_mkvol_req) +/* Remove an UBI volume */ +#define UBI_IOCRMVOL _IOW(UBI_IOC_MAGIC, 1, int32_t) +/* Re-size an UBI volume */ +#define UBI_IOCRSVOL _IOW(UBI_IOC_MAGIC, 2, struct ubi_rsvol_req) + +/* + * IOCTL commands of UBI volume character devices. + */ + +#define UBI_VOL_IOC_MAGIC 'O' + +/* Start UBI volume update */ +#define UBI_IOCVOLUP _IOW(UBI_VOL_IOC_MAGIC, 0, int64_t) +/* An eraseblock erasure command, used for debugging, disabled by dafault */ +#define UBI_IOCEBER _IOW(UBI_VOL_IOC_MAGIC, 0, int32_t) + +/** + * UBI volume type constants. + * + * @UBI_DYNAMIC_VOLUME: dynamic volume + * @UBI_STATIC_VOLUME: static volume + */ +enum { + UBI_DYNAMIC_VOLUME = 3, + UBI_STATIC_VOLUME = 4 +}; + +/** + * struct ubi_mkvol_req - volume description data structure used in + * volume creation requests. + * + * @vol_id: volume number + * @alignment: volume alignment + * @bytes: volume size in bytes + * @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) + * @padding: reserved for future, not used + * @name_len: volume name length + * @name: volume name + * + * This structure is used by userspace programs when creating new volumes. The + * @used_bytes field is only necessary when creating static volumes. + * + * The @alignment field specifies the required alignment of the volume logical + * eraseblock. This means, that the size of logical eraseblocks will be aligned + * to this number, i.e., + * (UBI device logical eraseblock size) mod (@alignment) = 0. + * + * To put it differently, the logical eraseblock of this volume may be slightly + * shortened in order to make it properly aligned. The alignment has to be + * multiple of the flash minimal input/output unit, or %1 to utilize the entire + * available space of logical eraseblocks. + * + * The @alignment field may be useful, for example, when one wants to maintain + * a block device on top of an UBI volume. In this case, it is desirable to fit + * an integer number of blocks in logical eraseblocks of this UBI volume. With + * alignment it is possible to update this volume using plane UBI volume image + * BLOBs, without caring about how to properly write them. + */ +struct ubi_mkvol_req { + int32_t vol_id; + int32_t alignment; + int64_t bytes; + int8_t vol_type; + int8_t padding[9]; + int16_t name_len; + __user const char *name; +} __attribute__ ((packed)); + +/** + * struct ubi_rsvol_req - a data structure used in volume re-size requests. + * + * @vol_id: ID of the volume to re-size + * @bytes: new size of the volume in bytes + * + * Re-sizing is possible for both dynamic and static volumes. But while dynamic + * volumes may be re-sized arbitrarily, static volumes cannot be made to be + * smaller then the number of bytes they bear. To arbitrarily shrink a static + * volume, it must be wiped out first (by means of volume update operation with + * zero number of bytes). + */ +struct ubi_rsvol_req { + int64_t bytes; + int32_t vol_id; +} __attribute__ ((packed)); + +#endif /* __UBI_USER_H__ */ diff --git a/ubi-utils/Makefile b/ubi-utils/Makefile index 686aa60..307da31 100644 --- a/ubi-utils/Makefile +++ b/ubi-utils/Makefile @@ -1,79 +1,89 @@ # -# This makefile simplifies the build process for a toolchain user. -# A toolchain developer should prefer a manual build process which -# fits to his original needs. +# Makefile for ubi-utils # -X86_PREFIX?=/usr/local -x86_path=./build_x86 -x86_status=$(x86_path)/config.status +HOST_OS_NAME := $(shell uname -s) +HOST_VERSION_NAME := $(shell uname -r) +BUILD_CPU := $(shell uname -m) +BUILD_OS := $(shell uname -o) -PPC_PREFIX?=/opt/ppcnf/crossroot -ppc_path=./build_ppc -ppc_status=$(ppc_path)/config.status +KERNELHDR := ../include # mtd-utils private headers +DESTDIR := /usr/local # default installation +SBINDIR := bin # default directory for binaries -all: x86 ppc +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)\" -install: install_x86 install_ppc -uninstall: uninstall_x86 uninstall_ppc +PERLPROGS = mkpfi ubicrc32.pl +TARGETS = ubiupdatevol ubimkvol ubirmvol pfiflash pddcustomize ubimirror \ + bin2nand nand2bin ubigen mkbootenv unubi pfi2bin -install_x86: x86 - make -C $(x86_path) install +vpath %.c ./src -install_ppc: ppc - make -C $(ppc_path) install +%: %.o + $(CC) $(LDFLAGS) -g -o $@ $^ +%.o: %.c + $(CC) $(CFLAGS) -g -c -o $@ $< -g -Wp,-MD,.$(shell basename $<).dep -uninstall_x86: x86 - make -C $(x86_path) uninstall +all: $(TARGETS) -uninstall_ppc: ppc - make -C $(ppc_path) uninstall +IGNORE=${wildcard .*.c.dep} +-include ${IGNORE} +clean: + rm -rf *.o $(TARGETS) .*.c.dep +ubiupdatevol: ubiupdatevol.o error.o libubi.o libubi_sysfs.o + $(CC) $(LDFLAGS) -o $@ $^ -x86: $(x86_status) - make -C $(x86_path) +ubimkvol: ubimkvol.o error.o libubi.o libubi_sysfs.o + $(CC) $(LDFLAGS) -o $@ $^ -ppc: $(ppc_status) - make -C $(ppc_path) +ubirmvol: ubirmvol.o error.o libubi.o libubi_sysfs.o + $(CC) $(LDFLAGS) -o $@ $^ -$(x86_status): $(x86_path) Makefile.in - cd $(x86_path) && ./config.status || ../configure \ - --prefix=$(X86_PREFIX) +pddcustomize: pddcustomize.o error.o libubimirror.o bootenv.o hashmap.o \ + libubi.o libubi_sysfs.o + $(CC) $(LDFLAGS) -o $@ $^ -$(ppc_status): $(ppc_path) Makefile.in - cd $(ppc_path) && ./config.status || ../configure \ - --build=i686-pc-linux-gnu \ - --host=ppc-linux \ - --prefix=$(PPC_PREFIX) \ - --exec-prefix=$(PPC_PREFIX) +pfiflash: pfiflash.o libpfiflash.o list.o reader.o error.o libubimirror.o \ + bootenv.o hashmap.o pfi.o libubi.o libubi_sysfs.o + $(CC) $(LDFLAGS) -o $@ $^ -Makefile.in: Makefile.am - ./bootstrap +ubimirror: ubimirror.o error.o libubimirror.o bootenv.o hashmap.o \ + libubi.o libubi_sysfs.o + $(CC) $(LDFLAGS) -o $@ $^ -$(x86_path): - mkdir -p $(x86_path) +nand2bin: nand2bin.o nandecc.o nandcorr.o + $(CC) $(LDFLAGS) -o $@ $^ -$(ppc_path): - mkdir -p $(ppc_path) +bin2nand: bin2nand.o error.o nandecc.o + $(CC) $(LDFLAGS) -o $@ $^ -clean: - rm -rf depcomp install-sh missing .deps \ - config.log config.status \ - inc/Makefile.in lib/Makefile.in - find . -type f -name "*~" -print | xargs $(RM) - rm -f Makefile.in - rm -f aclocal.m4 - rm -rf autom4te.cache - rm -f config.guess - rm -f config.sub - rm -f configure - rm -f depcomp - rm -f install-sh - rm -f ltmain.sh - rm -f missing - rm -f lib/Makefile.in - rm -f inc/Makefile.in - rm -rf $(x86_path) - rm -rf $(ppc_path) +ubigen: ubigen.o libubigen.o crc32.o + $(CC) $(LDFLAGS) -o $@ $^ + +mkbootenv: mkbootenv.o bootenv.o hashmap.o error.o + $(CC) $(LDFLAGS) -o $@ $^ + +unubi: unubi.o crc32.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 $@ $^ + +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/Makefile.am b/ubi-utils/Makefile.am deleted file mode 100644 index a5c9252..0000000 --- a/ubi-utils/Makefile.am +++ /dev/null @@ -1,96 +0,0 @@ -AUTOMAKE_OPTIONS = foreign -SUBDIRS=lib inc - - -# ----------------------------------------------------------------------------- -# Scripts (Perl/Bash) which shall be installed. -# -bin_SCRIPTS = $(top_srcdir)/src/mkpfi/mkpfi - -# ----------------------------------------------------------------------------- -# C programs which shall be build and installed. -# -INCLUDES=-I$(top_srcdir)/inc -I$(top_srcdir)/../../kernel/include - -bin_PROGRAMS = bin/pfi2bin \ - bin/bin2nand \ - bin/ubicrc32 \ - bin/mkbootenv \ - bin/ubimirror \ - bin/ubimkvol \ - bin/ubirmvol \ - bin/ubiwritevol \ - bin/pfiflash \ - bin/pddcustomize \ - bin/ubiinfo \ - bin/nand2bin \ - bin/unubi - -# ----------------------------------------------------------------------------- -# C programs which shall be build and NOT installed. (FLD dependency...) -# -noinst_PROGRAMS = bin/ubigen - -bin_ubigen_SOURCES = $(top_srcdir)/src/ubigen/ubigen_main.c -bin_ubigen_LDADD = $(top_builddir)/lib/libubigen.la \ - $(top_builddir)/lib/libcrc32.la - -bin_pfiflash_SOURCES = $(top_srcdir)/src/pfiflash/pfiflash.c -bin_pfiflash_LDADD = $(top_builddir)/lib/libpfiflash.la \ - $(top_builddir)/lib/liberror.la -bin_pfiflash_LDFLAGS = -static - -bin_pddcustomize_SOURCES= $(top_srcdir)/src/pddcustomize/pddcustomize.c -bin_pddcustomize_LDADD = $(top_builddir)/lib/libbootenv.la \ - $(top_builddir)/lib/liberror.la \ - $(top_builddir)/lib/libubi.la \ - $(top_builddir)/lib/libubimirror.la -bin_pddcustomize_LDFLAGS= -static - -bin_pfi2bin_SOURCES = $(top_srcdir)/src/pfi2bin/pfi2bin.c -bin_pfi2bin_LDADD = $(top_builddir)/lib/libubigen.la \ - $(top_builddir)/lib/liberror.la \ - $(top_builddir)/lib/liblist.la \ - $(top_builddir)/lib/libreader.la - -bin_bin2nand_SOURCES = $(top_srcdir)/src/bin2nand/bin2nand.c \ - $(top_srcdir)/src/bin2nand/nandecc.c -bin_bin2nand_LDADD = $(top_builddir)/lib/liberror.la - - -bin_ubicrc32_SOURCES = $(top_srcdir)/src/ubicrc32/ubicrc32.c -bin_ubicrc32_LDADD = $(top_builddir)/lib/libcrc32.la - -bin_mkbootenv_SOURCES = $(top_srcdir)/src/mkbootenv/mkbootenv.c -bin_mkbootenv_LDADD = $(top_builddir)/lib/libbootenv.la \ - $(top_builddir)/lib/liberror.la - - -bin_ubimirror_SOURCES = $(top_srcdir)/src/ubimirror/ubimirror.c -bin_ubimirror_LDADD = $(top_builddir)/lib/liberror.la \ - $(top_builddir)/lib/libubimirror.la -bin_ubimirror_LDFLAGS= -static - -bin_ubimkvol_SOURCES = $(top_srcdir)/src/ubimkvol/ubimkvol.c -bin_ubimkvol_LDADD = $(top_builddir)/lib/libubi.la -bin_ubimkvol_LDFLAGS = -static - -bin_ubirmvol_SOURCES = $(top_srcdir)/src/ubirmvol/ubirmvol.c -bin_ubirmvol_LDADD = $(top_builddir)/lib/libubi.la -bin_ubirmvol_LDFLAGS = -static - -bin_ubiwritevol_SOURCES = $(top_srcdir)/src/ubiwritevol/ubiwritevol.c -bin_ubiwritevol_LDADD = $(top_builddir)/lib/libubi.la -bin_ubiwritevol_LDFLAGS = -static - -bin_ubiinfo_SOURCES = $(top_srcdir)/src/ubiinfo/ubiinfo.c - -bin_nand2bin_SOURCES = $(top_srcdir)/src/nand2bin/nand2bin.c \ - $(top_srcdir)/src/bin2nand/nandecc.c \ - $(top_srcdir)/src/nand2bin/nandcorr.c - -bin_unubi_SOURCES = $(top_srcdir)/src/unubi/unubi.c -bin_unubi_LDADD = $(top_builddir)/lib/libcrc32.la - -clean-local: - rm -rf bin/ diff --git a/ubi-utils/bootstrap b/ubi-utils/bootstrap deleted file mode 100755 index f543912..0000000 --- a/ubi-utils/bootstrap +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash -LIBTOOLM4="/usr/local/share/libtool/libltdl" - -if test -d "$LIBTOOLM4"; then - echo "+ aclocal ${LIBTOOLM4}" - aclocal -I ${LIBTOOLM4} -else - echo "+ aclocal" - aclocal -fi -set -x -libtoolize --force -autoheader -automake --foreign --add-missing --copy -autoconf diff --git a/ubi-utils/configure.ac b/ubi-utils/configure.ac deleted file mode 100644 index 9f45176..0000000 --- a/ubi-utils/configure.ac +++ /dev/null @@ -1,52 +0,0 @@ -# Don't remove this check. -AC_PREREQ(2.59) - -# AC_INIT: Package, Version, Bugs -AC_INIT([flashutils],[0.1],[arnez@de.ibm.com]) -AC_CONFIG_HEADERS([inc/config.h:inc/config-h.in]) -AC_CANONICAL_SYSTEM -AM_INIT_AUTOMAKE([1.8 foreign]) -AM_MAINTAINER_MODE - -# Check debug options -AS_HELP_STRING -AC_ARG_ENABLE([debug], - AS_HELP_STRING([--enable-debug], - [build with debug information [default=yes]]),,enable_debug="yes") - -# Check for programs. -AM_PROG_LIBTOOL -AC_PROG_CC -AC_PROG_INSTALL -AC_PROG_MAKE_SET - -# Checks for header files. -AC_HEADER_STDC -# FIXME: Use AC_CHECK_HEADERS for UBI stuff. -# AC_CHECK_HEADERS([errno.h mtd/ubi-user.h]) -AC_CHECK_HEADERS([errno.h]) - -# Set build flags -if test "x$enable_debug" = "xyes"; then - CFLAGS="$CFLAGS -g -DDEBUG " -fi - -AC_DEFINE_UNQUOTED(HOST_OS, "${host}", [Host OS]) -AC_DEFINE_UNQUOTED(HOST_CPU, "${host_os}", [Host CPU]) -AC_DEFINE_UNQUOTED(BUILD_OS, "${build_os}", [Build OS]) -AC_DEFINE_UNQUOTED(BUILD_CPU, "${build_cpu}", [Build CPU]) - -# Additional Config -AC_C_BIGENDIAN - -# CFLAGS -CFLAGS="-std=gnu99 -Wundef -Wall $CFLAGS" - -# Init output. -AC_CONFIG_FILES([ - Makefile - lib/Makefile - inc/Makefile -]) - -AC_OUTPUT diff --git a/ubi-utils/inc/Makefile.am b/ubi-utils/inc/Makefile.am deleted file mode 100644 index ca22c37..0000000 --- a/ubi-utils/inc/Makefile.am +++ /dev/null @@ -1,5 +0,0 @@ -AUTOMAKE_OPTIONS = foreign - -# You can export headers if necessary: -nobase_include_HEADERS = pfiflash.h \ - libubi.h diff --git a/ubi-utils/inc/bootenv.h b/ubi-utils/inc/bootenv.h deleted file mode 100644 index 86743ed..0000000 --- a/ubi-utils/inc/bootenv.h +++ /dev/null @@ -1,415 +0,0 @@ -#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 /* FILE */ -#include -#include - -/* 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: - * @verbatimkey1=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); - - -/** - * @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); - -/** - * @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/inc/config-h.in b/ubi-utils/inc/config-h.in deleted file mode 100644 index ce4998b..0000000 --- a/ubi-utils/inc/config-h.in +++ /dev/null @@ -1,74 +0,0 @@ -/* inc/config-h.in. Generated from configure.ac by autoheader. */ - -/* Build CPU */ -#undef BUILD_CPU - -/* Build OS */ -#undef BUILD_OS - -/* Define to 1 if you have the header file. */ -#undef HAVE_DLFCN_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_ERRNO_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_INTTYPES_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_MEMORY_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_STDINT_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_STDLIB_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_STRINGS_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_STRING_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_STAT_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_TYPES_H - -/* Define to 1 if you have the header file. */ -#undef HAVE_UNISTD_H - -/* Host CPU */ -#undef HOST_CPU - -/* Host OS */ -#undef HOST_OS - -/* Name of package */ -#undef PACKAGE - -/* Define to the address where bug reports for this package should be sent. */ -#undef PACKAGE_BUGREPORT - -/* Define to the full name of this package. */ -#undef PACKAGE_NAME - -/* Define to the full name and version of this package. */ -#undef PACKAGE_STRING - -/* Define to the one symbol short name of this package. */ -#undef PACKAGE_TARNAME - -/* Define to the version of this package. */ -#undef PACKAGE_VERSION - -/* Define to 1 if you have the ANSI C header files. */ -#undef STDC_HEADERS - -/* Version number of package */ -#undef VERSION - -/* Define to 1 if your processor stores words with the most significant byte - first (like Motorola and SPARC, unlike Intel and VAX). */ -#undef WORDS_BIGENDIAN diff --git a/ubi-utils/inc/crc32.h b/ubi-utils/inc/crc32.h deleted file mode 100644 index 31362b0..0000000 --- a/ubi-utils/inc/crc32.h +++ /dev/null @@ -1,36 +0,0 @@ -#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 - -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/inc/error.h b/ubi-utils/inc/error.h deleted file mode 100644 index 25a1cee..0000000 --- a/ubi-utils/inc/error.h +++ /dev/null @@ -1,84 +0,0 @@ -#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 - -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) -#elif -#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) -#elif -#define dbg_msg(fmt, ...) -#endif - -#endif /* __ERROR_H__ */ diff --git a/ubi-utils/inc/example_ubi.h b/ubi-utils/inc/example_ubi.h deleted file mode 100644 index 23c7b54..0000000 --- a/ubi-utils/inc/example_ubi.h +++ /dev/null @@ -1,28 +0,0 @@ -#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/inc/list.h b/ubi-utils/inc/list.h deleted file mode 100644 index e8452a2..0000000 --- a/ubi-utils/inc/list.h +++ /dev/null @@ -1,56 +0,0 @@ -#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 - -#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/inc/nandecc.h b/ubi-utils/inc/nandecc.h deleted file mode 100644 index fb5d529..0000000 --- a/ubi-utils/inc/nandecc.h +++ /dev/null @@ -1,28 +0,0 @@ -#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 - -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/inc/peb.h b/ubi-utils/inc/peb.h deleted file mode 100644 index 246bce8..0000000 --- a/ubi-utils/inc/peb.h +++ /dev/null @@ -1,41 +0,0 @@ -#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 -#include - -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/inc/pfi.h b/ubi-utils/inc/pfi.h deleted file mode 100644 index 8c5cc07..0000000 --- a/ubi-utils/inc/pfi.h +++ /dev/null @@ -1,244 +0,0 @@ -#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 - * Andreas Arnez - * Joern Engel - * Frank Haverkamp - * - * @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 /* 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 . - */ -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 . - * - * 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/inc/pfiflash.h b/ubi-utils/inc/pfiflash.h deleted file mode 100644 index fc2eede..0000000 --- a/ubi-utils/inc/pfiflash.h +++ /dev/null @@ -1,62 +0,0 @@ -#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 - * - * @brief The pfiflash library offers an interface for using the - * pfiflash * utility. - */ - -#include /* 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 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/inc/reader.h b/ubi-utils/inc/reader.h deleted file mode 100644 index 93c15e3..0000000 --- a/ubi-utils/inc/reader.h +++ /dev/null @@ -1,84 +0,0 @@ -#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 -#include - -#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 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; -}; - -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) */ -}; - -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/inc/ubigen.h b/ubi-utils/inc/ubigen.h deleted file mode 100644 index 9e9e8ec..0000000 --- a/ubi-utils/inc/ubigen.h +++ /dev/null @@ -1,149 +0,0 @@ -#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 /* FILE */ -#include -#include - -#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/inc/ubimirror.h b/ubi-utils/inc/ubimirror.h deleted file mode 100644 index 893f5ce..0000000 --- a/ubi-utils/inc/ubimirror.h +++ /dev/null @@ -1,66 +0,0 @@ -#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 - -/** - * @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, size_t ids_size, - char *err_buf, size_t err_buf_size); - -#endif /* __UBIMIRROR_H__ */ 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); + ©_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/ubi_test.sh b/ubi-utils/scripts/ubi_test.sh index 9017148..622ec7e 100755 --- a/ubi-utils/scripts/ubi_test.sh +++ b/ubi-utils/scripts/ubi_test.sh @@ -10,7 +10,7 @@ export PATH=$PATH:~/bin:/usr/local/bin:/home/dedekind/work/prj/ubi/tools/flashut UBIMKVOL=ubimkvol UBIRMVOL=ubirmvol -UBIWRITEVOL=ubiwritevol +UBIWRITEVOL=ubiupdateevol # 128 KiB 131072 # 256 KiB 262144 diff --git a/ubi-utils/scripts/ubi_tools_test.sh b/ubi-utils/scripts/ubi_tools_test.sh index 43fc2bb..9163e0c 100755 --- a/ubi-utils/scripts/ubi_tools_test.sh +++ b/ubi-utils/scripts/ubi_tools_test.sh @@ -12,7 +12,7 @@ export PATH=$PATH:~/bin:/usr/local/bin:/home/dedekind/work/prj/ubi/tools/flashut UBIMKVOL=ubimkvol UBIRMVOL=ubirmvol -UBIWRITEVOL=ubiwritevol +UBIWRITEVOL=ubiupdatevol PFIFLASH=pfiflash CMP=cmp diff --git a/ubi-utils/src/bin2nand.c b/ubi-utils/src/bin2nand.c new file mode 100644 index 0000000..168f7dd --- /dev/null +++ b/ubi-utils/src/bin2nand.c @@ -0,0 +1,356 @@ +/* + * 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 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) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "error.h" +#include "config.h" +#include "nandecc.h" + +#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 + +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" + "bin2nand - a tool for adding OOB information to a " + "binary input file.\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 = "pagesize", + .key = 'p', + .arg = "", + .flags = 0, + .doc = "Pagesize in Byte/Mi/ki. Default = 2048", + .group = 1 }, + + { .name = "padding", + .key = 'j', + .arg = "", + .flags = 0, + .doc = "Padding in Byte/Mi/ki. Default = no padding", + .group = 1 }, + + { .name = "output", + .key = 'o', + .arg = "", + .flags = 0, + .doc = "Output filename. Interleaved Data/OOB if output-oob not " + "specified.", + .group = 2 }, + + { .name = "output-oob", + .key = 'q', + .arg = "", + .flags = 0, + .doc = "Write OOB data in separate file.", + .group = 2 }, + + { .name = NULL, + .key = 0, + .arg = NULL, + .flags = 0, + .doc = NULL, + .group = 0 }, +}; + +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 error_t +parse_opt(int key, char *arg, struct argp_state *state) +{ + int err = 0; + char* endp; + + myargs *args = state->input; + + switch (key) { + case 'p': /* pagesize */ + args->pagesize = (size_t) ustrtoull(arg, &endp, 0); + CHECK_ENDP("p", endp); + break; + case 'j': /* padding */ + args->padding = (size_t) ustrtoull(arg, &endp, 0); + CHECK_ENDP("j", endp); + break; + case 'o': /* output */ + args->file_out_data = arg; + break; + case 'q': /* output oob */ + args->file_out_oob = arg; + break; + case ARGP_KEY_ARG: /* input file */ + args->fp_in = fopen(arg, "rb"); + if ((args->fp_in) == NULL) { + err_quit("Cannot open file %s for input\n", arg); + } + args->arg1 = arg; + args->options = &state->argv[state->next]; + state->next = state->argc; + break; + case ARGP_KEY_END: + if (err) { + err_msg("\n"); + argp_usage(state); + exit(EXIT_FAILURE); + } + break; + default: + return(ARGP_ERR_UNKNOWN); + } + + return 0; +} + +static struct argp argp = { + options: options, + parser: parse_opt, + args_doc: 0, + doc: doc, + children: NULL, + help_filter: NULL, + argp_domain: NULL, +}; + +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, 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; + + argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &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 (strcmp(args.file_out_data, "") != 0) { + 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 (strcmp(args.file_out_oob, "") != 0) { + 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); + remove(args.file_out_data); + remove(args.file_out_oob); + } + return rc; +} diff --git a/ubi-utils/src/bin2nand/bin2nand.c b/ubi-utils/src/bin2nand/bin2nand.c deleted file mode 100644 index 5224e3b..0000000 --- a/ubi-utils/src/bin2nand/bin2nand.c +++ /dev/null @@ -1,343 +0,0 @@ -/* - * 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 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) - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "error.h" -#include "config.h" -#include "nandecc.h" - -#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 - -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" - "bin2nand - a tool for adding OOB information to a " - "binary input file.\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: "pagesize", key: 'p', arg: "", flags: 0, - doc: "Pagesize in Byte/Mi/ki. Default: 2048", - group: 1 }, - - { name: "padding", key: 'j', arg: "", flags: 0, - doc: "Padding in Byte/Mi/ki. Default: no padding", - group: 1 }, - - /* Output options */ - { name: NULL, key: 0, arg: NULL, flags: 0, - doc: "Output settings:", - group: 2 }, - - { name: "output", key: 'o', arg: "", flags: 0, - doc: "Output filename. Interleaved Data/OOB if output-oob not " - "specified.", - group: 2 }, - - { name: "output-oob", key: 'q', arg: "", flags: 0, - doc: "Write OOB data in separate file.", - group: 2 }, - - { name: NULL, key: 0, arg: NULL, flags: 0, doc: NULL, group: 0 }, -}; - -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 error_t -parse_opt(int key, char *arg, struct argp_state *state) -{ - int err = 0; - char* endp; - - myargs *args = state->input; - - switch (key) { - case 'p': /* pagesize */ - args->pagesize = (size_t) ustrtoull(arg, &endp, 0); - CHECK_ENDP("p", endp); - break; - case 'j': /* padding */ - args->padding = (size_t) ustrtoull(arg, &endp, 0); - CHECK_ENDP("j", endp); - break; - case 'o': /* output */ - args->file_out_data = arg; - break; - case 'q': /* output oob */ - args->file_out_oob = arg; - break; - case ARGP_KEY_ARG: /* input file */ - args->fp_in = fopen(arg, "rb"); - if ((args->fp_in) == NULL) { - err_quit("Cannot open file %s for input\n", arg); - } - args->arg1 = arg; - args->options = &state->argv[state->next]; - state->next = state->argc; - break; - case ARGP_KEY_END: - if (err) { - err_msg("\n"); - argp_usage(state); - exit(EXIT_FAILURE); - } - break; - default: - return(ARGP_ERR_UNKNOWN); - } - - return 0; -} - -static struct argp argp = { - options: options, - parser: parse_opt, - args_doc: 0, - doc: doc, - children: NULL, - help_filter: NULL, - argp_domain: NULL, -}; - -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, read; - myargs args = { - .action = ACT_NORMAL, - .pagesize = PAGESIZE, - .padding = PADDING, - .fp_in = NULL, - .file_out_data = "", - .file_out_oob = "", - }; - - FILE* fp_out_data = stdout; - FILE* fp_out_oob = NULL; - - argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &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 (strcmp(args.file_out_data, "") != 0) { - 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 (strcmp(args.file_out_oob, "") != 0) { - 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); - remove(args.file_out_data); - remove(args.file_out_oob); - } - return rc; -} diff --git a/ubi-utils/src/bin2nand/nandecc.c b/ubi-utils/src/bin2nand/nandecc.c deleted file mode 100644 index 71660ef..0000000 --- a/ubi-utils/src/bin2nand/nandecc.c +++ /dev/null @@ -1,159 +0,0 @@ -/* - * 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/bin2nand/nandecc.h b/ubi-utils/src/bin2nand/nandecc.h deleted file mode 100644 index 8ae8a66..0000000 --- a/ubi-utils/src/bin2nand/nandecc.h +++ /dev/null @@ -1,11 +0,0 @@ -/* - * NAND ecc functions - */ -#ifndef _NAND_ECC_H -#define _NAND_ECC_H - -#include - -extern int nand_calculate_ecc(const uint8_t *dat, uint8_t *ecc_code); - -#endif diff --git a/ubi-utils/src/bootenv.c b/ubi-utils/src/bootenv.c new file mode 100644 index 0000000..8871d90 --- /dev/null +++ b/ubi-utils/src/bootenv.c @@ -0,0 +1,961 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "hashmap.h" +#include "error.h" + +#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 = 0; /* current length */ + uint32_t j = 0; /* processed chars */ + uint32_t items = 0; /* processed items */ + int rc = 0; + + if (buf[size-1] != '\0') { + return BOOTENV_EFMT; + } + + while ((i = strlen(curr)) != 0) { + /* there is a key value pair remaining */ + rc = extract_pair(curr, env); + if (rc != 0) { + rc = BOOTENV_EINVAL; + return rc; + } + items++; + + j += i; + if (j >= size) + return 0; /* finished, end of buffer */ + curr += i + 1; + } + + return 0; +} + +/** + * 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) +{ + int rc; + char *buf = NULL; + size_t i = 0; + + 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) { + buf[i++] = '\0'; + break; /* we have enough */ + } + + /* log_msg("%c", c); */ /* FIXME DBG */ + + buf[i++] = c; + if (ferror(fp)) { + rc = -EIO; + goto err; + } + } + + /* transfer to hashmap */ + rc = rd_buffer(env, buf, size); + + /* FIXME DBG */ + /* log_msg("\n%s:%d rc=%d\n", __func__, __LINE__, rc); */ + +err: + free(buf); + return rc; +} + + + +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, + 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 > buf_size_max) { + rc = -ENOSPC; + goto err; + } + + rc = bootenv_get(env, keys[i], &val); + if (rc != 0) + goto err; + + wr += snprintf(buf + wr, buf_size_max - wr, + "%s=%s", keys[i], val); + wr++; /* for \0 */ + } + + *written = wr; + +err: + if (keys != NULL) + free(keys); + + return rc; +} + +int +bootenv_write(FILE* fp, bootenv_t env) +{ + int rc = 0; + size_t size = 0; + char *buf = NULL; + + 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; + + if (fwrite(buf, size, 1, fp) != 1) { + rc = -EIO; + goto err; + } + +err: + if (buf != NULL) + free(buf); + return rc; +} + +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: "); + + 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..86743ed --- /dev/null +++ b/ubi-utils/src/bootenv.h @@ -0,0 +1,415 @@ +#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 /* FILE */ +#include +#include + +/* 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: + * @verbatimkey1=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); + + +/** + * @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); + +/** + * @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..746fa3c --- /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_VERSION "1.0" +#define PACKAGE_BUGREPORT "dedekind@oktetlabs.ru, haver@vnet.ibm.com, 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 +#include + +/* 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 + +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/error.c b/ubi-utils/src/error.c new file mode 100644 index 0000000..c8c623c --- /dev/null +++ b/ubi-utils/src/error.c @@ -0,0 +1,177 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include "error.h" + +#define MAXLINE 4096 + +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; + + 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) +{ + 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 */ +} + + +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); + } + + 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..e8d7137 --- /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 + +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, ...) +#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..250f71f --- /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 +#include +#include +#include +#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("[%d]: ", 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 +#include + +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/libbootenv/bootenv.c b/ubi-utils/src/libbootenv/bootenv.c deleted file mode 100644 index b6a1191..0000000 --- a/ubi-utils/src/libbootenv/bootenv.c +++ /dev/null @@ -1,959 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#include -#include -#include - -#include "hashmap.h" -#include "error.h" - -#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 = 0; /* current length */ - uint32_t j = 0; /* processed chars */ - uint32_t items = 0; /* processed items */ - int rc = 0; - - if (buf[size-1] != '\0') { - return BOOTENV_EFMT; - } - - while ((i = strlen(curr)) != 0) { - /* there is a key value pair remaining */ - rc = extract_pair(curr, env); - if (rc != 0) { - rc = BOOTENV_EINVAL; - return rc; - } - items++; - - j += i; - if (j >= size) - return 0; /* finished, end of buffer */ - curr += i + 1; - } - - return 0; -} - -/** - * 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) -{ - int rc; - char *buf = NULL; - size_t i = 0; - - 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) { - buf[i++] = '\0'; - break; /* we have enough */ - } - - /* log_msg("%c", c); */ /* FIXME DBG */ - - buf[i++] = c; - if (ferror(fp)) { - rc = -EIO; - goto err; - } - } - - /* transfer to hashmap */ - rc = rd_buffer(env, buf, size); - - /* FIXME DBG */ - /* log_msg("\n%s:%d rc=%d\n", __func__, __LINE__, rc); */ - -err: - free(buf); - return rc; -} - - - -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, - 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(FILE* fp, bootenv_t env) -{ - int rc = 0; - size_t size = 0; - char *buf = NULL; - - 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; - - if (fwrite(buf, size, 1, fp) != 1) { - rc = -EIO; - goto err; - } - -err: - if (buf != NULL) - free(buf); - return rc; -} - -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) -{ - /* @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, size_t err_buf_size) -{ - 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, - char *err_buf, size_t err_buf_size) -{ - 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, 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: "); - - 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/libbootenv/hashmap.c b/ubi-utils/src/libbootenv/hashmap.c deleted file mode 100644 index 250f71f..0000000 --- a/ubi-utils/src/libbootenv/hashmap.c +++ /dev/null @@ -1,412 +0,0 @@ -/* - * 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 -#include -#include -#include -#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("[%d]: ", 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/libbootenv/hashmap.h b/ubi-utils/src/libbootenv/hashmap.h deleted file mode 100644 index 1b13e95..0000000 --- a/ubi-utils/src/libbootenv/hashmap.h +++ /dev/null @@ -1,49 +0,0 @@ -#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 -#include - -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/libcrc32/crc32.c b/ubi-utils/src/libcrc32/crc32.c deleted file mode 100644 index 666e217..0000000 --- a/ubi-utils/src/libcrc32/crc32.c +++ /dev/null @@ -1,83 +0,0 @@ -/* - * 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 -#include - -/* 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/liberror/error.c b/ubi-utils/src/liberror/error.c deleted file mode 100644 index c8c623c..0000000 --- a/ubi-utils/src/liberror/error.c +++ /dev/null @@ -1,177 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#include -#include "error.h" - -#define MAXLINE 4096 - -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; - - 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) -{ - 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 */ -} - - -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); - } - - fputs(buf, fpout); - fflush(fpout); - if (fpout != stderr) - fclose(fpout); - - return; -} diff --git a/ubi-utils/src/liblist/list.c b/ubi-utils/src/liblist/list.c deleted file mode 100644 index 6eb716b..0000000 --- a/ubi-utils/src/liblist/list.c +++ /dev/null @@ -1,149 +0,0 @@ -/* - * 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 -#include -#include - -#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/libpeb/peb.c b/ubi-utils/src/libpeb/peb.c deleted file mode 100644 index 08b770f..0000000 --- a/ubi-utils/src/libpeb/peb.c +++ /dev/null @@ -1,116 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#include - -#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/libpfi/pfi.c b/ubi-utils/src/libpfi/pfi.c deleted file mode 100644 index c8d5ee4..0000000 --- a/ubi-utils/src/libpfi/pfi.c +++ /dev/null @@ -1,461 +0,0 @@ -/* - * 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. - * - * Wed Feb 8 11:38:22 CET 2006: Initial creation. - */ - -#include -#include -#include -#include -#include -#include -#include - -#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 */ -}; - -static const char* modes[] = {"raw", "ubi", NULL}; /* 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) -{ - const char* ptr = modes[0]; - int i = 0; - while (ptr != NULL) { - if(strcmp(ptr, mode) == 0) { - return i; - } - ptr++; - 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 ((uint32_t)value == (uint32_t)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/libpfiflash.c b/ubi-utils/src/libpfiflash.c new file mode 100644 index 0000000..ed2af3c --- /dev/null +++ b/ubi-utils/src/libpfiflash.c @@ -0,0 +1,619 @@ +/* + * 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 pfiflash.c + * + * @author Oliver Lohmann + * + * @brief This library is provides an interface to the pfiflash utility. + * + * Wed Mar 15 11:39:19 CET 2006 Initial creation. + * + * @TODO Comare 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 +#include +#include +#define __USE_GNU +#include + +#include +#include + +//#include /* FIXME Is this ok here!!?? */ + +#include "config.h" +#include "ubimirror.h" +#include "error.h" +#include "reader.h" +#include "example_ubi.h" +#include "bootenv.h" + +static const char copyright [] __attribute__((unused)) = + "Copyright (c) International Business Machines Corp., 2006"; + +#define EBUF(fmt...) do { \ + snprintf(err_buf, err_buf_size, fmt); \ + } while (0) + +static pdd_func_t pdd_funcs[PDD_HANDLING_NUM] = + { + &bootenv_pdd_keep, + &bootenv_pdd_merge, + &bootenv_pdd_overwrite + }; +/**< An array of PDD function pointers indexed by the algorithm. */ + + +typedef enum ubi_update_process_t { + UBI_REMOVE = 0, + UBI_WRITE, +} ubi_update_process_t; + +static int +skip_raw_sections(FILE* pfi, list_t pfi_raws, + char* err_buf, size_t err_buf_size) +{ + int rc = 0; + + void *i; + list_t ptr; + size_t j, skip_size; + + if (is_empty(pfi_raws)) + return 0; + + foreach(i, ptr, pfi_raws) { + skip_size = ((pfi_raw_t)i)->data_size; + for(j = 0; j < skip_size; j++) { + fgetc(pfi); + if (ferror(pfi)) { + EBUF("Cannot skip raw section in PFI."); + rc = -EIO; + goto err; + } + } + } + err: + return rc; +} + +/** + * @brief Wraps the ubi_mkvol functions and implements a hook for the bootenv + * update. + * @param devno UBI device number. + * @param s Current seqnum. + * @param u Information about the UBI volume from the PFI. + * @param err_buf An error buffer. + * @param err_buf_size The size of the error buffer. + * @return 0 On Sucess. + * @return else Error. + */ +static int +my_ubi_mkvol(int devno, int s, pfi_ubi_t u, char *err_buf, size_t err_buf_size) +{ + int rc = 0; + int type; + ubi_lib_t ulib = NULL; + + log_msg("%s(vol_id=%d, size=%d, data_size=%d, type=%d, " + "alig=%d, nlen=%d, name=%s)", __func__, + 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) { + goto err; + } + + switch (u->type) { + case pfi_ubi_static: + type = UBI_STATIC_VOLUME; break; + case pfi_ubi_dynamic: + type = UBI_DYNAMIC_VOLUME; break; + default: + type = UBI_DYNAMIC_VOLUME; + } + + rc = ubi_mkvol(ulib, devno, u->ids[s], type, u->size, u->alignment, + u->names[s]); + if (rc != 0) { + EBUF("Cannot create volume: %d", u->ids[s]); + goto err; + } + + err: + if (ulib != NULL) + ubi_close(&ulib); + return rc; +} + +/** + * @brief A wrapper around the UBI library function ubi_rmvol. + * @param devno UBI device number. + * @param s Current seqnum. + * @param u Information about the UBI volume from the PFI. + * @param err_buf An error buffer. + * @param err_buf_size The size of the error buffer. + * + * If the volume does not exist, the function will return success. + */ +static int +my_ubi_rmvol(int devno, uint32_t id, + char *err_buf __unused, size_t err_buf_size __unused) +{ + int rc = 0; + ubi_lib_t ulib = NULL; + int fd; + + log_msg("%s(id=%d)", __func__, id); + + rc = ubi_open(&ulib); + if (rc != 0) + goto err; + + /** + * Truncate if it exist or not. + */ + fd = ubi_vol_open(ulib, devno, id, O_RDWR); + if (fd == -1) + return 0; /* not existent, return */ + + rc = ubi_vol_update(fd, 0); + if (rc < 0) { + fprintf(stderr, "update failed rc=%d errno=%d\n", rc, errno); + ubi_vol_close(fd); + goto err; /* if EBUSY than empty device, continue */ + } + ubi_vol_close(fd); + + rc = ubi_rmvol(ulib, devno, id); + if (rc != 0) { + /* @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 */ + + dbg_msg("Remove UBI volume %d returned with error: %d " + "errno=%d", id, rc, errno); + goto err; + } + err: + if (ulib != NULL) + ubi_close(&ulib); + return rc; +} + +static int +read_bootenv_volume(int devno, uint32_t id, bootenv_t bootenv_old, + char *err_buf, size_t err_buf_size) +{ + int rc = 0; + ubi_lib_t ulib = NULL; + FILE* fp_in = NULL; + + rc = ubi_open(&ulib); + if (rc) + return rc; + + fp_in = ubi_vol_fopen_read(ulib, devno, id); + if (!fp_in) { + EBUF("Cannot open bootenv volume"); + rc = -EIO; + goto err; + } + + log_msg("%s reading old bootenvs", __func__); + + /* Save old bootenvs for reference */ + rc = bootenv_read(fp_in, bootenv_old, BOOTENV_MAXSIZE); + if (rc) + EBUF("Cannot read bootenv_old"); + err: + if (fp_in) + fclose(fp_in); + if (ulib) + ubi_close(&ulib); + return rc; +} + +static int +write_bootenv_volume(int devno, uint32_t id, bootenv_t bootenv_old, + pdd_func_t pdd_f, + FILE* fp_in, /* new pdd data contained in pfi */ + size_t fp_in_size, /* data size of new pdd data in pfi */ + char *err_buf, size_t err_buf_size) +{ + int rc = 0; + int warnings = 0; + ubi_lib_t ulib = NULL; + bootenv_t bootenv_new = NULL; + bootenv_t bootenv_res = NULL; + size_t update_size = 0; + FILE *fp_out = NULL; + + log_msg("%s(id=%d, fp_in=%p)", __func__, 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) { + goto err; + } + + rc = bootenv_create(&bootenv_new); + if (rc != 0) + goto err; + rc = bootenv_create(&bootenv_res); + if (rc != 0) + goto err; + + rc = bootenv_read(fp_in, bootenv_new, fp_in_size); + if (rc != 0) + goto err; + + rc = pdd_f(bootenv_old, bootenv_new, &bootenv_res, &warnings, + err_buf, err_buf_size); + if (rc != 0) + goto err; + if (warnings) { + /* @TODO Do sth with the warning */ + dbg_msg("A warning in the PDD operation occured: %d", + warnings); + } + log_msg("... (2)"); + + rc = bootenv_size(bootenv_res, &update_size); + if (rc != 0) + goto err; + + fp_out = ubi_vol_fopen_update(ulib, devno, id, update_size); + if (fp_out == NULL) + goto err; + + rc = bootenv_write(fp_out, bootenv_res); + if (rc != 0) { + EBUF("Write operation on ubi%d_%d failed.", devno, id); + rc = -EIO; + 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; +} + +static int +write_normal_volume(int devno, uint32_t id, size_t update_size, FILE* fp_in, + char *err_buf __unused, size_t err_buf_size __unused) +{ + int rc = 0; + ubi_lib_t ulib = NULL; + FILE* fp_out = NULL; + int c; + size_t i; + + log_msg("%s(id=%d, update_size=%d fp_in=%p)", + __func__, id, update_size, fp_in); + + rc = ubi_open(&ulib); + if (rc) + return rc; + + fp_out = ubi_vol_fopen_update(ulib, devno, id, update_size); + if (fp_out == NULL) { + rc = -1; + goto err; + } + + log_msg("starting the update ... "); /* FIXME DBG */ + for (i = 0; i < update_size; i++) { + c = getc(fp_in); + if (c == EOF && ferror(fp_in)) { + rc = -EIO; + goto err; + } + if (putc(c, fp_out) == EOF) { + rc = -EIO; + goto err; + } + /* FIXME DBG */ + /* if ((i & 0xFFF) == 0xFFF) log_msg("."); */ + } + /* log_msg("\n"); */ /* FIXME DBG */ + err: + if (fp_out) + fclose(fp_out); + if (ulib) + ubi_close(&ulib); + return rc; +} + + +/** + * @brief ... + * @precondition The PFI file contains at least one ubi_id entry. + * This is assured by the PFI read process. + * @postcondition The used seqnum number is set in the UBI PFI + * header list. + * The UBI volumes specified by seqnum are processed. + */ +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 = 0; + pfi_ubi_t u; + list_t ptr; + + foreach(u, ptr, pfi_ubis) { + int s = seqnum; + if (seqnum > ((int)u->ids_size - 1)) { + s = 0; /* per default use the first */ + } + u->curr_seqnum = s; + + switch (ubi_update_process) { + case UBI_REMOVE: + 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); + 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) + 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, + err_buf, + err_buf_size); + } + else { + rc = write_normal_volume(EXAMPLE_UBI_DEVICE, + u->ids[s], + u->data_size, pfi, + err_buf, + err_buf_size); + } + if (rc != 0) + goto err; + break; + default: + EBUF("Invoked unknown UBI operation."); + rc = -1; + goto err; + + } + if (rc != 0) { + goto err; + } + } + err: + return rc; + +} + +static int +erase_unmapped_ubi_volumes(int devno, list_t pfi_ubis, + char *err_buf, size_t err_buf_size) +{ + int rc = 0; + list_t ptr; + pfi_ubi_t u; + size_t i; + uint8_t ubi_volumes[PFI_UBI_MAX_VOLUMES]; + + 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) { + EBUF("PFI file contains an invalid " + "volume id: %d", 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) + goto err; + } + } + err: + return rc; +} + +static int +mirror_ubi_volumes(uint32_t devno, list_t pfi_ubis, + char *err_buf, size_t err_buf_size) +{ + int rc = 0; + list_t ptr; + uint32_t j; + pfi_ubi_t i; + ubi_lib_t ulib = NULL; + + log_msg("%s(...)", __func__); + + rc = ubi_open(&ulib); + if (rc != 0) + 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; +} + +int +pfiflash(FILE* pfi, int complete, int seqnum, pdd_handling_t pdd_handling, + char *err_buf, size_t err_buf_size) +{ + int rc = 0; + pdd_func_t pdd_f = NULL; + + if (pfi == NULL) + return -EINVAL; + + /** + * 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 */ + + bootenv_t bootenv; + rc = bootenv_create(&bootenv); + if (rc != 0) { + EBUF("Cannot create bootenv variable"); + } + + rc = read_pfi_headers(&pfi_raws, &pfi_ubis, pfi, + err_buf, err_buf_size); + if (rc != 0) { + EBUF("Cannot read PFI headers."); + goto err; + } + + /* @TODO: If you want to implement an IPL update - start here. */ + rc = skip_raw_sections(pfi, pfi_raws, err_buf, err_buf_size); + if (rc != 0) { + goto err; + } + + if (complete) { + rc = erase_unmapped_ubi_volumes(EXAMPLE_UBI_DEVICE, pfi_ubis, + err_buf, err_buf_size); + if (rc != 0) { + EBUF("Cannot delete unmapped UBI volumes."); + goto err; + } + } + + if (((int)pdd_handling >= 0) && (pdd_handling < PDD_HANDLING_NUM)) { + pdd_f = pdd_funcs[pdd_handling]; + } + else { + EBUF("Used unknown PDD handling algorithm (pdd_handling)"); + } + + rc = process_ubi_volumes(pfi, curr_seqnum, pfi_ubis, bootenv, pdd_f, + UBI_REMOVE, err_buf, err_buf_size); + if (rc != 0) { + goto err; + } + rc = process_ubi_volumes(pfi, curr_seqnum, pfi_ubis, bootenv, pdd_f, + UBI_WRITE, err_buf, err_buf_size); + if (rc != 0) { + 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) + 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; +} diff --git a/ubi-utils/src/libpfiflash/pfiflash.c b/ubi-utils/src/libpfiflash/pfiflash.c deleted file mode 100644 index 0859a22..0000000 --- a/ubi-utils/src/libpfiflash/pfiflash.c +++ /dev/null @@ -1,617 +0,0 @@ -/* - * 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 pfiflash.c - * - * @author Oliver Lohmann - * - * @brief This library is provides an interface to the pfiflash utility. - * - * Wed Mar 15 11:39:19 CET 2006 Initial creation. - * - * @TODO Comare 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 -#include -#include -#define __USE_GNU -#include - -#include -#include - -#include /* FIXME Is this ok here!!?? */ - -#include "ubimirror.h" -#include "error.h" -#include "reader.h" -#include "example_ubi.h" -#include "bootenv.h" - -static const char copyright [] __attribute__((unused)) = - "Copyright (c) International Business Machines Corp., 2006"; - -#define EBUF(fmt...) do { \ - snprintf(err_buf, err_buf_size, fmt); \ - } while (0) - -static pdd_func_t pdd_funcs[PDD_HANDLING_NUM] = - { - &bootenv_pdd_keep, - &bootenv_pdd_merge, - &bootenv_pdd_overwrite - }; -/**< An array of PDD function pointers indexed by the algorithm. */ - - -typedef enum ubi_update_process_t { - UBI_REMOVE = 0, - UBI_WRITE, -} ubi_update_process_t; - -static int -skip_raw_sections(FILE* pfi, list_t pfi_raws, - char* err_buf, size_t err_buf_size) -{ - int rc = 0; - - void *i; - list_t ptr; - size_t j, skip_size; - - if (is_empty(pfi_raws)) - return 0; - - foreach(i, ptr, pfi_raws) { - skip_size = ((pfi_raw_t)i)->data_size; - for(j = 0; j < skip_size; j++) { - fgetc(pfi); - if (ferror(pfi)) { - EBUF("Cannot skip raw section in PFI."); - rc = -EIO; - goto err; - } - } - } - err: - return rc; -} - -/** - * @brief Wraps the ubi_mkvol functions and implements a hook for the bootenv - * update. - * @param devno UBI device number. - * @param s Current seqnum. - * @param u Information about the UBI volume from the PFI. - * @param err_buf An error buffer. - * @param err_buf_size The size of the error buffer. - * @return 0 On Sucess. - * @return else Error. - */ -static int -my_ubi_mkvol(int devno, int s, pfi_ubi_t u, char *err_buf, size_t err_buf_size) -{ - int rc = 0; - int type; - ubi_lib_t ulib = NULL; - - log_msg("%s(vol_id=%d, size=%d, data_size=%d, type=%d, " - "alig=%d, nlen=%d, name=%s)", __func__, - 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) { - goto err; - } - - switch (u->type) { - case pfi_ubi_static: - type = UBI_STATIC_VOLUME; break; - case pfi_ubi_dynamic: - type = UBI_DYNAMIC_VOLUME; break; - default: - type = UBI_DYNAMIC_VOLUME; - } - - rc = ubi_mkvol(ulib, devno, u->ids[s], type, u->size, u->alignment, - u->names[s]); - if (rc != 0) { - EBUF("Cannot create volume: %d", u->ids[s]); - goto err; - } - - err: - if (ulib != NULL) - ubi_close(&ulib); - return rc; -} - -/** - * @brief A wrapper around the UBI library function ubi_rmvol. - * @param devno UBI device number. - * @param s Current seqnum. - * @param u Information about the UBI volume from the PFI. - * @param err_buf An error buffer. - * @param err_buf_size The size of the error buffer. - * - * If the volume does not exist, the function will return success. - */ -static int -my_ubi_rmvol(int devno, uint32_t id, char *err_buf, size_t err_buf_size) -{ - int rc = 0; - ubi_lib_t ulib = NULL; - int fd; - - log_msg("%s(id=%d)", __func__, id); - - rc = ubi_open(&ulib); - if (rc != 0) - goto err; - - /** - * Truncate if it exist or not. - */ - fd = ubi_vol_open(ulib, devno, id, O_RDWR); - if (fd == -1) - return 0; /* not existent, return */ - - rc = ubi_vol_update(fd, 0); - if (rc < 0) { - fprintf(stderr, "update failed rc=%d errno=%d\n", rc, errno); - ubi_vol_close(fd); - goto err; /* if EBUSY than empty device, continue */ - } - ubi_vol_close(fd); - - rc = ubi_rmvol(ulib, devno, id); - if (rc != 0) { - /* @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 */ - - dbg_msg("Remove UBI volume %d returned with error: %d " - "errno=%d", id, rc, errno); - goto err; - } - err: - if (ulib != NULL) - ubi_close(&ulib); - return rc; -} - -static int -read_bootenv_volume(int devno, uint32_t id, bootenv_t bootenv_old, - char *err_buf, size_t err_buf_size) -{ - int rc = 0; - ubi_lib_t ulib = NULL; - FILE* fp_in = NULL; - - rc = ubi_open(&ulib); - if (rc) - return rc; - - fp_in = ubi_vol_fopen_read(ulib, devno, id); - if (!fp_in) { - EBUF("Cannot open bootenv volume"); - rc = -EIO; - goto err; - } - - log_msg("%s reading old bootenvs", __func__); - - /* Save old bootenvs for reference */ - rc = bootenv_read(fp_in, bootenv_old, BOOTENV_MAXSIZE); - if (rc) - EBUF("Cannot read bootenv_old"); - err: - if (fp_in) - fclose(fp_in); - if (ulib) - ubi_close(&ulib); - return rc; -} - -static int -write_bootenv_volume(int devno, uint32_t id, bootenv_t bootenv_old, - pdd_func_t pdd_f, - FILE* fp_in, /* new pdd data contained in pfi */ - size_t fp_in_size, /* data size of new pdd data in pfi */ - char *err_buf, size_t err_buf_size) -{ - int rc = 0; - int warnings = 0; - ubi_lib_t ulib = NULL; - bootenv_t bootenv_new = NULL; - bootenv_t bootenv_res = NULL; - size_t update_size = 0; - FILE *fp_out = NULL; - - log_msg("%s(id=%d, fp_in=%p)", __func__, 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) { - goto err; - } - - rc = bootenv_create(&bootenv_new); - if (rc != 0) - goto err; - rc = bootenv_create(&bootenv_res); - if (rc != 0) - goto err; - - rc = bootenv_read(fp_in, bootenv_new, fp_in_size); - if (rc != 0) - goto err; - - rc = pdd_f(bootenv_old, bootenv_new, &bootenv_res, &warnings, - err_buf, err_buf_size); - if (rc != 0) - goto err; - if (warnings) { - /* @TODO Do sth with the warning */ - dbg_msg("A warning in the PDD operation occured: %d", - warnings); - } - log_msg("... (2)"); - - rc = bootenv_size(bootenv_res, &update_size); - if (rc != 0) - goto err; - - fp_out = ubi_vol_fopen_update(ulib, devno, id, update_size); - if (fp_out == NULL) - goto err; - - rc = bootenv_write(fp_out, bootenv_res); - if (rc != 0) { - EBUF("Write operation on ubi%d_%d failed.", devno, id); - rc = -EIO; - 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; -} - -static int -write_normal_volume(int devno, uint32_t id, size_t update_size, FILE* fp_in, - char *err_buf, size_t err_buf_size) -{ - int rc = 0; - ubi_lib_t ulib = NULL; - FILE* fp_out = NULL; - int c; - size_t i; - - log_msg("%s(id=%d, update_size=%d fp_in=%p)", - __func__, id, update_size, fp_in); - - rc = ubi_open(&ulib); - if (rc) - return rc; - - fp_out = ubi_vol_fopen_update(ulib, devno, id, update_size); - if (fp_out == NULL) { - rc = -1; - goto err; - } - - log_msg("starting the update ... "); /* FIXME DBG */ - for (i = 0; i < update_size; i++) { - c = getc(fp_in); - if (c == EOF && ferror(fp_in)) { - rc = -EIO; - goto err; - } - if (putc(c, fp_out) == EOF) { - rc = -EIO; - goto err; - } - /* FIXME DBG */ - /* if ((i & 0xFFF) == 0xFFF) log_msg("."); */ - } - /* log_msg("\n"); */ /* FIXME DBG */ - err: - if (fp_out) - fclose(fp_out); - if (ulib) - ubi_close(&ulib); - return rc; -} - - -/** - * @brief ... - * @precondition The PFI file contains at least one ubi_id entry. - * This is assured by the PFI read process. - * @postcondition The used seqnum number is set in the UBI PFI - * header list. - * The UBI volumes specified by seqnum are processed. - */ -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 = 0; - pfi_ubi_t u; - list_t ptr; - - foreach(u, ptr, pfi_ubis) { - int s = seqnum; - if (seqnum > (u->ids_size - 1)) { - s = 0; /* per default use the first */ - } - u->curr_seqnum = s; - - switch (ubi_update_process) { - case UBI_REMOVE: - 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); - 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) - 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, - err_buf, - err_buf_size); - } - else { - rc = write_normal_volume(EXAMPLE_UBI_DEVICE, - u->ids[s], - u->data_size, pfi, - err_buf, - err_buf_size); - } - if (rc != 0) - goto err; - break; - default: - EBUF("Invoked unknown UBI operation."); - rc = -1; - goto err; - - } - if (rc != 0) { - goto err; - } - } - err: - return rc; - -} - -static int -erase_unmapped_ubi_volumes(int devno, list_t pfi_ubis, - char *err_buf, size_t err_buf_size) -{ - int rc = 0; - list_t ptr; - pfi_ubi_t u; - size_t i; - uint8_t ubi_volumes[PFI_UBI_MAX_VOLUMES]; - - 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) { - EBUF("PFI file contains an invalid " - "volume id: %d", 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) - goto err; - } - } - err: - return rc; -} - -static int -mirror_ubi_volumes(uint32_t devno, list_t pfi_ubis, - char *err_buf, size_t err_buf_size) -{ - int rc = 0; - list_t ptr; - uint32_t j; - pfi_ubi_t i; - ubi_lib_t ulib = NULL; - - log_msg("%s(...)", __func__); - - rc = ubi_open(&ulib); - if (rc != 0) - 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; -} - -int -pfiflash(FILE* pfi, int complete, int seqnum, pdd_handling_t pdd_handling, - char *err_buf, size_t err_buf_size) -{ - int rc = 0; - pdd_func_t pdd_f = NULL; - - if (pfi == NULL) - return -EINVAL; - - /** - * 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 */ - - bootenv_t bootenv; - rc = bootenv_create(&bootenv); - if (rc != 0) { - EBUF("Cannot create bootenv variable"); - } - - rc = read_pfi_headers(&pfi_raws, &pfi_ubis, pfi, - err_buf, err_buf_size); - if (rc != 0) { - EBUF("Cannot read PFI headers."); - goto err; - } - - /* @TODO: If you want to implement an IPL update - start here. */ - rc = skip_raw_sections(pfi, pfi_raws, err_buf, err_buf_size); - if (rc != 0) { - goto err; - } - - if (complete) { - rc = erase_unmapped_ubi_volumes(EXAMPLE_UBI_DEVICE, pfi_ubis, - err_buf, err_buf_size); - if (rc != 0) { - EBUF("Cannot delete unmapped UBI volumes."); - goto err; - } - } - - if ((pdd_handling >= 0) && (pdd_handling < PDD_HANDLING_NUM)) { - pdd_f = pdd_funcs[pdd_handling]; - } - else { - EBUF("Used unknown PDD handling algorithm (pdd_handling)"); - } - - rc = process_ubi_volumes(pfi, curr_seqnum, pfi_ubis, bootenv, pdd_f, - UBI_REMOVE, err_buf, err_buf_size); - if (rc != 0) { - goto err; - } - rc = process_ubi_volumes(pfi, curr_seqnum, pfi_ubis, bootenv, pdd_f, - UBI_WRITE, err_buf, err_buf_size); - if (rc != 0) { - 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) - 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; -} diff --git a/ubi-utils/src/libreader/reader.c b/ubi-utils/src/libreader/reader.c deleted file mode 100644 index c8242df..0000000 --- a/ubi-utils/src/libreader/reader.c +++ /dev/null @@ -1,442 +0,0 @@ -/* - * 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 -#include -#include -#include -#include - -#include "bootenv.h" -#include "reader.h" - -/* @FIXME hard coded offsets right now - get them from Artem? */ -#define NAND_DEFAULT_VID_HDR_OFF 1984 -#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) { - res->flash_type = NAND_FLASH; - res->vid_hdr_offset = NAND_DEFAULT_VID_HDR_OFF; - } - 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, 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; - - 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_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, - &(res->starts_size), &(res->starts)); - 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, 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; - - 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_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, &(res->ids_size), - &(res->ids)); - 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, &(res->names_size), - &(tmp_names)); - 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/libubi.c b/ubi-utils/src/libubi.c new file mode 100644 index 0000000..9b9a793 --- /dev/null +++ b/ubi-utils/src/libubi.c @@ -0,0 +1,773 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libubi.h" +#include "libubi_int.h" +#include "libubi_sysfs.h" + +/** + * struct ubi_lib - UBI library descriptor. + * + * @ubi general UBI information + * + * @sysfs_root sysfs root directory + * @ubi_root UBI root directory in sysfs + * + * @nlen_max full path to the "maximum volume name length" sysfs file + * @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 *nlen_max; + 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 n = 1; + char *path; + struct stat stat; + + err = sysfs_read_int(desc->version, (int*) &ubi->version); + if (err) + return -1; + + err = sysfs_read_int(desc->nlen_max, (int*) &ubi->nlen_max); + if (err) + return -1; + + /* Calculate number of UBI devices */ + do { + char dir[20]; + + sprintf(&dir[0], "ubi%d", n); + path = mkpath(desc->sysfs_root, dir); + if (!path) + return ENOMEM; + + err = lstat(path, &stat); + if (err == 0) + n += 1; + free(path); + } while (err == 0); + + if (errno != ENOENT) + return -1; + + if (n == 0) { + ubi_err("no UBI devices found"); + errno = EINVAL; + return -1; + } + + errno = 0; + ubi->dev_count = n; + return 0; +} + +void +ubi_dump_handler(ubi_lib_t desc) +{ + ubi_lib_t d = desc; + printf( "UBI Library Descriptor:\n" + "ubi_root: %s\n" + "nlen_max: %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->nlen_max, 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->nlen_max = mkpath(res->ubi_root, UBI_NLEN_MAX); + if (!res->nlen_max) + goto error; + + res->version = mkpath(res->ubi_root, UBI_VERSION); + 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->udev_path, "%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->nlen_max); + 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[desc->ubi.nlen_max]; + + 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], + desc->ubi.nlen_max, 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; + + 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; + req.name_len = strlen(name); + req.name = name; + + /* 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(%08x) err=%d errno=%d\n", + __func__, 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/libubi/libubi.c b/ubi-utils/src/libubi/libubi.c deleted file mode 100644 index 9b9a793..0000000 --- a/ubi-utils/src/libubi/libubi.c +++ /dev/null @@ -1,773 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "libubi.h" -#include "libubi_int.h" -#include "libubi_sysfs.h" - -/** - * struct ubi_lib - UBI library descriptor. - * - * @ubi general UBI information - * - * @sysfs_root sysfs root directory - * @ubi_root UBI root directory in sysfs - * - * @nlen_max full path to the "maximum volume name length" sysfs file - * @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 *nlen_max; - 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 n = 1; - char *path; - struct stat stat; - - err = sysfs_read_int(desc->version, (int*) &ubi->version); - if (err) - return -1; - - err = sysfs_read_int(desc->nlen_max, (int*) &ubi->nlen_max); - if (err) - return -1; - - /* Calculate number of UBI devices */ - do { - char dir[20]; - - sprintf(&dir[0], "ubi%d", n); - path = mkpath(desc->sysfs_root, dir); - if (!path) - return ENOMEM; - - err = lstat(path, &stat); - if (err == 0) - n += 1; - free(path); - } while (err == 0); - - if (errno != ENOENT) - return -1; - - if (n == 0) { - ubi_err("no UBI devices found"); - errno = EINVAL; - return -1; - } - - errno = 0; - ubi->dev_count = n; - return 0; -} - -void -ubi_dump_handler(ubi_lib_t desc) -{ - ubi_lib_t d = desc; - printf( "UBI Library Descriptor:\n" - "ubi_root: %s\n" - "nlen_max: %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->nlen_max, 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->nlen_max = mkpath(res->ubi_root, UBI_NLEN_MAX); - if (!res->nlen_max) - goto error; - - res->version = mkpath(res->ubi_root, UBI_VERSION); - 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->udev_path, "%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->nlen_max); - 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[desc->ubi.nlen_max]; - - 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], - desc->ubi.nlen_max, 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; - - 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; - req.name_len = strlen(name); - req.name = name; - - /* 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(%08x) err=%d errno=%d\n", - __func__, 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/libubi/libubi_int.h b/ubi-utils/src/libubi/libubi_int.h deleted file mode 100644 index 1640010..0000000 --- a/ubi-utils/src/libubi/libubi_int.h +++ /dev/null @@ -1,119 +0,0 @@ -#ifndef __UBI_INT_H__ -#define __UBI_INT_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. - * - * Author: Artem B. Bityutskiy - */ - -/* - * 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, ...) -#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_VERSION - * @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_VERSION "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/libubi/libubi_sysfs.c b/ubi-utils/src/libubi/libubi_sysfs.c deleted file mode 100644 index f7ecebc..0000000 --- a/ubi-utils/src/libubi/libubi_sysfs.c +++ /dev/null @@ -1,231 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#include -#include -#include -#include - -#include "libubi_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, unsigned long long *num) -{ - 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, unsigned int *num) -{ - 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/libubi/libubi_sysfs.h b/ubi-utils/src/libubi/libubi_sysfs.h deleted file mode 100644 index 2fb6072..0000000 --- a/ubi-utils/src/libubi/libubi_sysfs.h +++ /dev/null @@ -1,109 +0,0 @@ -/* - * 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/libubi_int.h b/ubi-utils/src/libubi_int.h new file mode 100644 index 0000000..ab387f5 --- /dev/null +++ b/ubi-utils/src/libubi_int.h @@ -0,0 +1,119 @@ +#ifndef __UBI_INT_H__ +#define __UBI_INT_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. + * + * Author: Artem B. Bityutskiy + */ + +/* + * 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_VERSION + * @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_VERSION "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/libubi_sysfs.c b/ubi-utils/src/libubi_sysfs.c new file mode 100644 index 0000000..95fd3de --- /dev/null +++ b/ubi-utils/src/libubi_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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "libubi_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/libubi_sysfs.h b/ubi-utils/src/libubi_sysfs.h new file mode 100644 index 0000000..2fb6072 --- /dev/null +++ b/ubi-utils/src/libubi_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/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 +#include +#include +#include +#include +#include + +#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, ""); + return; + } + if (!u->ec) { + fprintf(stderr, ""); + err = 1; + } + if (!u->v) { + fprintf(stderr, ""); + 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/libubigen/ubigen.c b/ubi-utils/src/libubigen/ubigen.c deleted file mode 100644 index 0cfa687..0000000 --- a/ubi-utils/src/libubigen/ubigen.c +++ /dev/null @@ -1,486 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#include - -#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) -{ -#ifdef DEBUG - int err = 0; - if (!u) { - fprintf(stderr, ""); - return; - } - if (!u->ec) { - fprintf(stderr, ""); - err = 1; - } - if (!u->v) { - fprintf(stderr, ""); - 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..e5715fc --- /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 +#include +#include +#include +#include + +#include +#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 (ulib != NULL) + ubi_close(&ulib); + if (fd_in != -1) + ubi_vol_close(fd_in); + if (fd_out != -1) + ubi_vol_close(fd_out); + return rc; +} diff --git a/ubi-utils/src/libubimirror/ubimirror.c b/ubi-utils/src/libubimirror/ubimirror.c deleted file mode 100644 index bf6b37c..0000000 --- a/ubi-utils/src/libubimirror/ubimirror.c +++ /dev/null @@ -1,217 +0,0 @@ -/* - * 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 -#include -#include -#include -#include - -#include -#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, size_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, size_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, size_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 (ulib != NULL) - ubi_close(&ulib); - if (fd_in != -1) - ubi_vol_close(fd_in); - if (fd_out != -1) - ubi_vol_close(fd_out); - return rc; -} 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 +#include +#include + +#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 + +#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..49ce597 --- /dev/null +++ b/ubi-utils/src/mkbootenv.c @@ -0,0 +1,173 @@ +/* + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "bootenv.h" +#include "error.h" + +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" + "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 struct argp_option options[] = { + { .name = "copyright", + .key = 'c', + .arg = NULL, + .flags = 0, + .doc = "Print copyright information.", + .group = 1 }, + { .name = "output", + .key = 'o', + .arg = "", + .flags = 0, + .doc = "Write the the output data to instead of stdout.", + .group = 1 }, + { .name = NULL, + .key = 0, + .arg = NULL, + .flags = 0, + .doc = NULL, + .group = 0 }, +}; + +typedef struct myargs { + FILE* fp_in; + FILE* fp_out; + + 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 'o': + args->fp_out = fopen(arg, "wb"); + if ((args->fp_out) == NULL) { + fprintf(stderr, + "Cannot open file %s for output\n", arg); + exit(1); + } + 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 = "[bootenv-txt-file]", + .doc = doc, + .children = NULL, + .help_filter = NULL, + .argp_domain = NULL, +}; + +int +main(int argc, char **argv) { + int rc = 0; + bootenv_t env; + + myargs args = { + .fp_in = stdin, + .fp_out = stdout, + .arg1 = NULL, + .options = NULL, + }; + + argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &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/mkbootenv/mkbootenv.c b/ubi-utils/src/mkbootenv/mkbootenv.c deleted file mode 100644 index 49ce597..0000000 --- a/ubi-utils/src/mkbootenv/mkbootenv.c +++ /dev/null @@ -1,173 +0,0 @@ -/* - * 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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "config.h" -#include "bootenv.h" -#include "error.h" - -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" - "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 struct argp_option options[] = { - { .name = "copyright", - .key = 'c', - .arg = NULL, - .flags = 0, - .doc = "Print copyright information.", - .group = 1 }, - { .name = "output", - .key = 'o', - .arg = "", - .flags = 0, - .doc = "Write the the output data to instead of stdout.", - .group = 1 }, - { .name = NULL, - .key = 0, - .arg = NULL, - .flags = 0, - .doc = NULL, - .group = 0 }, -}; - -typedef struct myargs { - FILE* fp_in; - FILE* fp_out; - - 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 'o': - args->fp_out = fopen(arg, "wb"); - if ((args->fp_out) == NULL) { - fprintf(stderr, - "Cannot open file %s for output\n", arg); - exit(1); - } - 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 = "[bootenv-txt-file]", - .doc = doc, - .children = NULL, - .help_filter = NULL, - .argp_domain = NULL, -}; - -int -main(int argc, char **argv) { - int rc = 0; - bootenv_t env; - - myargs args = { - .fp_in = stdin, - .fp_out = stdout, - .arg1 = NULL, - .options = NULL, - }; - - argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &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/mkpfi/f128_nand_sample.cfg b/ubi-utils/src/mkpfi/f128_nand_sample.cfg deleted file mode 100644 index e468d9d..0000000 --- a/ubi-utils/src/mkpfi/f128_nand_sample.cfg +++ /dev/null @@ -1,38 +0,0 @@ -[targets] -complete=ipl,spl,bootenv,kernel,rootfs -bootcode=spl,bootenv - -# Build sections -[ipl] -image=ipl.bin -raw_starts=0x00000000 -raw_total_size=128kiB - -[spl] -image=u-boot.bin -ubi_ids=2,3 -ubi_size=2MiB -ubi_type=static -ubi_names=spl_0,spl_1 - -[bootenv] -bootenv_file=bootenv_complete.txt -ubi_ids=4,5 -ubi_size=128kiB -ubi_type=static -ubi_names=bootenv_0,bootenv_1 - -[kernel] -image=vmlinux.bin -ubi_ids=6,7 -ubi_size=6MiB -ubi_type=static -ubi_names=kernel_0,kernel_1 - -[rootfs] -image=rootfs.bin -ubi_ids=8,9 -ubi_alignment=2kiB -ubi_size=16MiB -ubi_type=dynamic -ubi_names=rootfs_0,rootfs_1 diff --git a/ubi-utils/src/mkpfi/f64_nor_sample.cfg b/ubi-utils/src/mkpfi/f64_nor_sample.cfg deleted file mode 100644 index fd44e27..0000000 --- a/ubi-utils/src/mkpfi/f64_nor_sample.cfg +++ /dev/null @@ -1,39 +0,0 @@ -[targets] -complete=ipl,spl,bootenv,kernel,rootfs -bootcode=spl,bootenv -rootfs=rootfs - -# Build sections -[ipl] -image=ipl.bin -raw_starts=0x02FE0000, 0x03FE0000 -raw_total_size=128kiB - -[spl] -image=u-boot.bin -ubi_ids=2,3 -ubi_size=2MiB -ubi_type=static -ubi_names=spl_0,spl_1 - -[bootenv] -bootenv_file=bootenv_complete.txt -ubi_ids=4,5 -ubi_size=128kiB -ubi_type=static -ubi_names=bootenv_0,bootenv_1 - -[kernel] -image=vmlinux.bin -ubi_ids=6,7 -ubi_size=6MiB -ubi_type=static -ubi_names=kernel_0,kernel_1 - -[rootfs] -image=rootfs.bin -ubi_ids=8,9 -ubi_alignment=2kiB -ubi_size=16128kiB -ubi_type=dynamic -ubi_names=rootfs_0,rootfs_1 diff --git a/ubi-utils/src/mkpfi/mkpfi b/ubi-utils/src/mkpfi/mkpfi deleted file mode 100755 index 2cce587..0000000 --- a/ubi-utils/src/mkpfi/mkpfi +++ /dev/null @@ -1,723 +0,0 @@ -#!/usr/bin/perl -# -# Copyright (c) International Business Machines Corp., 2006 -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See -# the GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -# - -# -# mkpfi -# -# This perl program is assembles PFI files from a config file. -# -# Author: Oliver Lohmann (oliloh@de.ibm.com) -# -use warnings; -use strict; -use lib "/usr/lib/perl5"; # Please change this path as you need it, or - # make a proposal how this could be done - # nicer. -use Getopt::Long; -use Pod::Usage; -use Config::IniFiles; -use File::Temp; - -# ---------------------------------------------------------------------------- -# Versions -our $version : unique = "0.1"; -our $pfi_version : unique = "0x1"; - -# ---------------------------------------------------------------------------- -# Globals -my $verbose = 0; -my $cfg; - -my %opts = (); -my %files = (config => ""); -my @tmp_files; - -my %tools = (ubicrc32 => "ubicrc32"); - -# ---------------------------------------------------------------------------- -# Processing the input sections -# -# The idea is to combine each section entry with a function -# in order to allow some kind of preprocessing for the values -# before they are written into the PFI file. -# This is especially useful to be more verbose and -# user-friendly in the layout file. -# -# All key-function hashes are applied after the general -# validation of the configuration file. -# If any mandatory key is missing in a section the user -# will be informed and the PFI creation process is aborted. -# -# Default keys will be checked for their presence inside the config -# file. If they are missing, they will be generated with appr. values. - -# Mandatory keys for UBI volumes. -my %ubi_keys = ("ubi_ids" => \&check_id_list, - "ubi_size" => \&replace_num, - "ubi_type" => \&replace_type, - "ubi_names" => \&remove_spaces, - "ubi_alignment" => \&replace_num); - -# Mandatory keys for RAW sections. -my %raw_keys = ("raw_starts" => \&expand_starts, - "raw_total_size" => \&replace_num); - -# Common default keys for documentation and control purposes. -my %common_keys = ("flags" => \&replace_num, - "label" => \&do_nothing); - -# Define any defaults here. Values which maintained in this default -# region need not to be specified by the user explicitly. -my %def_ubi_keys = ("ubi_alignment" => [\&set_default, "0x1"]); -my %def_raw_keys = (); -my %def_common_keys = ("flags" => [\&set_default, "0x0"], - "label" => [\&generate_label, ""]); - -# ---------------------------------------------------------------------------- -# Input keys, actually the path to the input data. - -my %input_keys = ("image" => \&do_nothing); - -# Placeholder keys allow the replacement via a special -# purpose function. E.g. the bootenv_file key will be used -# to generate bootenv binary data from an text file and -# replace the bootenv_file key with an image key to handle it -# in the same way in the further creation process. -my %input_placeholder_keys = ("bootenv_file" => \&create_bootenv_image); - -# ---------------------------------------------------------------------------- -# Helper - -# @brief Get current time string. -sub get_date { - my $tmp = scalar localtime; - $tmp =~ s/ /_/g; - return $tmp; -} - -# @brief Print an info message to stdout. -sub INFO($) { - my $str = shift; - - if (!$verbose) { - return; - } - - print STDOUT $str; -} - -# @brief Print an error message to stderr. -sub ERR($) { - my $str = shift; - print STDERR $str; -} - -# @brief Print a warning message to stderr. -sub WARN($) { - my $str = shift; - print STDERR $str; -} - -sub parse_command_line($) { - my $opt = shift; - my $result = GetOptions( "help" => \$$opt{'help'}, - "man" => \$$opt{'man'}, - "config=s" => \$$opt{'config'}, - "verbose" => \$$opt{'verbose'}, - ) or pod2usage(2); - pod2usage(1) if defined ($$opt{help}); - pod2usage(-verbose => 2) if defined ($$opt{man}); - - $verbose = $$opt{verbose} if defined $$opt{verbose}; - - if (!defined $$opt{config}) { - ERR("[ ERROR: No config file specified. Aborting...\n"); - exit 1; - } - -} - -# @brief Check if all needed tools are in PATH. -sub check_tools { - my $err = 0; - my $key; - - foreach $key (keys %tools) { - if (`which $tools{$key}` eq "") { - ERR("\n") if ($err == 0); - ERR("! Please add the tool \'$tools{$key}\' " . - "to your path!\n"); - $err = 1; - } - } - die "[ ERROR: Did not find all needed tools!\n" if $err; -} - -sub open_cfg_file($) { - my $fname = shift; - my $res = new Config::IniFiles( -file => $fname ); - - die "[ ERROR: Cannot load your config file!\n" if (!defined $res); - return $res; -} - -sub set_default($$$$) { - my ($cfg, $section, $parameter, $def_value) = @_; - $cfg->newval($section, $parameter, $def_value); - return; -} - -sub generate_label($$$$) { - my ($cfg, $section, $parameter, $def_value) = @_; - my $new_label = $def_value . $section; - $new_label .= "_" . get_date; - $cfg->newval($section, $parameter, $new_label); - return; -} - -# @brief Converts any num to a unified hex string, i.e the resulting value -# always starts with "0x" and is aligned to 8 hexdigits. -# @return Returns 0 on success, otherwise an error occured. -# -sub any_num_to_hex($$) { - my $val = shift; - my $res = shift; - - # M(iB) - if ($val =~ m/([0-9]+)[Mm][i]?[Bb]?/g) { - $$res = sprintf("0x%08x", $1 * 1024 * 1024); - } - # k(iB) - elsif ($val =~ m/([0-9]+)[kK][i]?[Bb]?/g) { - $$res = sprintf("0x%08x", $1 * 1024); - } - # hex - elsif ($val =~ m/0x?([0-9a-fA-F]+)/g) { - $$res = sprintf("0x%08x", hex $1); - } - # decimal - elsif ($val =~ m/^([0-9]+)$/g) { - $$res = sprintf("0x%08x", $1); - } - else { - $$res = ""; - return -1; - } - - return 0; -} - -sub remove_spaces($$$) { - my ($cfg, $section, $parameter) = @_; - my ($start, @starts, @new_starts); - my $val = $cfg->val($section, $parameter); - my $res; - - $val =~ s/ //g; # spaces - $cfg->newval($section, $parameter, $val); -} - -sub expand_starts($$$) { - my ($cfg, $section, $parameter) = @_; - my ($start, @starts, @new_starts); - my $val = $cfg->val($section, $parameter); - my $res; - - $val =~ s/ //g; # spaces - @starts = split(/,/, $val); - - foreach $start (@starts) { - if (any_num_to_hex($start, \$res) != 0) { - ERR("[ ERROR: [$section]\n"); - ERR("[ Expecting a list of numeric " . - "values for parameter: $parameter\n"); - exit 1; - } - push (@new_starts, $res); - } - $res = join(',', @starts); - - $cfg->newval($section, $parameter, $res); -} - -sub check_id_list($$$) { - my ($cfg, $section, $parameter) = @_; - my $val = $cfg->val($section, $parameter); - my $res; - - if (!($val =~ m/^[0-9]+[,0-9]*/)) { - ERR("[ ERROR: Syntax error in 'ubi_ids' in " . - "section '$section': $val\n"); - ERR("[ Aborting... "); - exit 1; - } -} - -sub replace_type($$$) { - my ($cfg, $section, $parameter) = @_; - my $val = $cfg->val($section, $parameter); - my $res; - - $res = lc($val); - grep {$res eq $_} ('static', 'dynamic') - or die "[ ERROR: Unknown UBI Volume Type in " . - "section '$section': $val\n"; - - $cfg->newval($section, $parameter, $res); -} - - -sub replace_num($$$) { - my ($cfg, $section, $parameter) = @_; - my $val = $cfg->val($section, $parameter); - my $res = ""; - - if (any_num_to_hex($val, \$res) != 0) { - ERR("[ ERROR: [$section]\n"); - ERR("[ Expecting a numeric value " . - "for parameter: $parameter\n"); - exit 1; - } - $cfg->newval($section, $parameter, $res); -} - -sub do_nothing($$$) { - my ($cfg, $section, $parameter) = @_; - return; -} - -sub bootenv_sanity_check($) { - my $env = shift; # hash array containing bootenv - my %pdd = (); - - defined($$env{'pdd'}) or return "'pdd' not defined"; - foreach (split /,/, $$env{'pdd'}) { - defined($$env{$_}) or return "undefined '$_' in pdd"; - $pdd{$_} = 1; - } - - defined $$env{'pdd_preserve'} or - return ""; - foreach (split /,/, $$env{'pdd_preserve'}) { - defined($pdd{$_}) - or return "pdd_preserve field '$_' not in pdd"; - } - return ""; -} - -sub create_bootenv_image($$$) { - my ($cfg, $section, $parameter) = @_; - my $txt_fn = $cfg->val($section, "bootenv_file"); - my $in; - - my %value = (); - my @key = (); - - open $in, "<", $txt_fn - or die "[ ERROR: can't open bootenv file '$txt_fn'.\n"; - while (<$in>) { - next if (/^\s*(\#.*)?$/); # Skip comments/whitespace. - - if (/^(\S+?)\+\=(.*)$/) { - defined($value{$1}) or - die "$txt_fn:$.: error: appending to" . - " non-existent '$1'\n"; - $value{$1} .= $2; - } elsif (/^(\S+?)\=(.*)$/) { - not defined($value{$1}) or - die "$txt_fn:$.: error: trying to" . - " redefine '$1'\n"; - push @key, $1; - $value{$1} = $2; - } else { - die "$txt_fn:$.: error: unrecognized syntax\n"; - } - } - close $in; - - $_ = &bootenv_sanity_check(\%value) - and die "$txt_fn: error: $_\n"; - - my $tmp_file = new File::Temp(); - push (@tmp_files, $tmp_file); - - foreach (@key) { - print $tmp_file "$_=", $value{$_}, "\0"; - } - close $tmp_file; - - $cfg->newval($section, "image", $tmp_file-> filename); -} - -sub process_keys($$$) { - my ($cfg, $section, $keys) = @_; - my @parameters = $cfg->Parameters($section); - my $i; - - for ($i = 0 ; $i < scalar(@parameters) ; $i++ ) { - if (defined($$keys{$parameters[$i]})) { - $$keys{$parameters[$i]}->($cfg, $section, - $parameters[$i]); - } - } - -} - -sub is_in_keylist($$) { - my ($key, $keys) = @_; - my $i; - - for ($i = 0; $i < scalar(@$keys); $i++) { - if ($$keys[$i] eq $key) { - return 1; - } - } - - return 0; -} - -sub check_default_keys($$$) { - my ($cfg, $section, $keys) = @_; - my @parameters = $cfg->Parameters($section); - my $key; - - foreach $key (keys %$keys) { - if (!is_in_keylist($key, \@parameters)) { - $$keys{$key}[0]-> - ($cfg, $section, $key, $$keys{$key}[1]); - } - } - -} - - - -sub check_keys($$$) { - my ($cfg, $section, $keys) = @_; - my @parameters = $cfg->Parameters($section); - my ($i, $key, $err); - - $err = 0; - for ($i = 0 ; $i < scalar(@$keys) ; $i++ ) { - if (!is_in_keylist($$keys[$i], \@parameters)) { - ERR("[ ERROR: [$section]\n") if $err == 0; - $err = 1; - ERR("[ Missing key '$$keys[$i]'\n"); - } - } - - if ($err) { - ERR("[ Aborting...\n"); - exit 1; - } -} - -sub push_pfi_data($$$$$) { - my ($cfg, $section, $pfi_infos, $keys, $mode) = @_; - my ($tmp, $i, $hdr); - - my %pfi_info = (); - $pfi_info{'mode'} = $mode; - $pfi_info{'image'} = $cfg->val($section, "image"); - - # Build the PFI header - $hdr = sprintf("PFI!\n"); - $hdr .= sprintf("version=0x%08x\n", hex $pfi_version); - $hdr .= sprintf("mode=$mode\n"); - - # calculate the size of the binary data part - $tmp = -s $cfg->val($section, "image"); - if (!defined $tmp) { - ERR("[ ERROR: [$section]\n"); - ERR("[ Missing input image: " - . $cfg->val($section, "image") . "\n"); - exit 1; - } - # Check for the image to fit into the given space - my $quota; - if ($mode eq 'raw') { - $quota = oct $cfg->val($section, "raw_total_size"); - } elsif ($mode eq 'ubi') { - $quota = oct $cfg->val($section, "ubi_size"); - } - $tmp <= $quota - or die "[ERROR: image file too big: " . - $cfg->val($section, "image") . "\n"; - $pfi_info{'size'} = $tmp; - - $hdr .= sprintf("size=0x%08x\n", $tmp); - - my $img_file = $cfg->val($section, "image"); - my $crc32 = `$tools{'ubicrc32'} $img_file 2>&1`; - if (any_num_to_hex($crc32, \$tmp) != 0) { - die "[ ERROR: $tools{'ubicrc32'} returned with errors"; - } - $hdr .= sprintf("crc=$tmp\n"); - - - # Process all remaining keys - for ($i = 0; $i < scalar (@$keys); $i++) { - if ($$keys[$i] eq "image") { # special case image input file - if (! -e ($tmp = $cfg->val($section, "image"))) { - ERR("[ ERROR: [$section]\n"); - ERR("[ Cannot find input file $tmp\n"); - exit 1; - } - next; - } - $hdr .= sprintf("%s=%s\n", $$keys[$i], - $cfg->val($section, $$keys[$i])); - } - - $hdr .= sprintf("\n"); # end marker for PFI-header - - $pfi_info{'header'} = $hdr; - - # store in the header list - push @$pfi_infos, \%pfi_info; -} - -sub process_section($$$$$$) { - my ($cfg, $section, $pfi_infos, $custom_keys, - $def_custom_keys, $mode) = @_; - my @keys = (keys %common_keys, keys %$custom_keys); - my @complete_keys = (@keys, keys %input_keys); - - # set defaults if necessary - check_default_keys($cfg, $section, $def_custom_keys); - check_default_keys($cfg, $section, \%def_common_keys); - - # check for placeholders... - process_keys($cfg, $section, \%input_placeholder_keys); - - # VALIDATE layout.cfg entries - check_keys($cfg, $section, \@complete_keys); - - # execute linked functions (if any) - process_keys($cfg, $section, \%common_keys); - process_keys($cfg, $section, $custom_keys); - - push_pfi_data($cfg, $section, $pfi_infos, \@keys, $mode); -} - -sub get_section_info($$) { - my ($cfg, $section) = @_; - my @parameters = $cfg->Parameters($section); - my ($ubi, $raw, $i, @res); - - $ubi = $raw = 0; - for ($i = 0 ; $i < scalar(@parameters) ; $i++ ) { - if ($parameters[$i] =~ m/ubi_/gi) { - $ubi = 1; - @res = (\%ubi_keys, \%def_ubi_keys, "ubi"); - } - if ($parameters[$i] =~ m/raw_/gi) { - $raw = 1; - @res = (\%raw_keys, \%def_raw_keys, "raw"); - } - } - - if (($ubi + $raw) != 1) { # double definition in section - ERR("[ ERROR: Layout error in section '$section'\n"); - exit 1; - } - - return @res; -} - -sub mk_target_list($$) { - my $val = shift; - my $tmp = shift; - my $complete = 0; - - if ($val =~ m/\((.*)\)/g) { - $val = $1; - $complete = 1; - } - $val =~ s/ //g; # spaces - - @$tmp = split(/,/, $val); - - return $complete; -} - -sub copy_bytes($$$) { - my ($in, $out, $to_copy) = @_; - - while ($to_copy) { - my $buf; - my $bufsize = 1024*1024; - - $bufsize < $to_copy or $bufsize = $to_copy; - read($in, $buf, $bufsize) == $bufsize - or die "[ ERROR: Image file shrunk during operation\n"; - print $out $buf; - $to_copy -= $bufsize; - } -} - -sub write_target($$) { - my ($pfi_infos, $target) = @_; - my ($pfi_info); - - INFO("[ Writting target pfi file: '$target.pfi'...\n"); - if (-e "$target.pfi") { - WARN("! Replaced old pfi...\n"); - `rm -f $target.pfi`; - } - open(FILE, ">", "$target.pfi") - or die "[ ERROR: Cannot create output file: $target.pfi\n"; - binmode(FILE); - - # @FIXME sort by mode (first raw, then ubi) - # Currently this ordering is based on a string comparism. :-) - @$pfi_infos = sort {(lc $$a{'mode'}) cmp (lc $$b{'mode'})} @$pfi_infos; - - # Print all headers first - foreach $pfi_info (@$pfi_infos) { - print FILE $$pfi_info{'header'}; - - } - # Print the linked data sections - print FILE "DATA\n"; - foreach $pfi_info (@$pfi_infos) { - open(IMAGE, "<", $$pfi_info{'image'}) - or die "[ ERROR: Cannot open input image: " . - "$$pfi_info{'image'}" . "\n"; - binmode(IMAGE); - ©_bytes(\*IMAGE, \*FILE, $$pfi_info{'size'}); - close(IMAGE) or die "[ ERROR: Cannot close input image: " . - "$$pfi_info{'image'}" . "\n"; - } - close(FILE) or die "[ ERROR: Cannot close output file: $target.pfi\n"; -} - -sub process_config($) { - my $cfg = shift; - my @sections = $cfg->Sections; - my ($i, $j, $keylist, $def_keylist, $mode, $tmp, - @tlist, $complete,@pfi_infos); - - my @parameters = $cfg->Parameters("targets") or - die "[ ERROR: Config file has no 'targets' section!\n"; - - for ($i = 0 ; $i < scalar(@parameters) ; $i++ ) { - INFO("[ Processing target '$parameters[$i]'...\n"); - @pfi_infos = (); - - # get a list of subtargets - $complete = mk_target_list($cfg->val("targets", - $parameters[$i]), \@tlist); - # build all subtargets - for ($j = 0 ; $j < scalar(@tlist) ; $j++ ) { - ($keylist, $def_keylist, $mode) - = get_section_info($cfg, $tlist[$j]); - process_section($cfg, $tlist[$j], - \@pfi_infos, - $keylist, $def_keylist, $mode); - } - - write_target(\@pfi_infos, $parameters[$i]); - } - - INFO("[ Success.\n"); - - -} - -sub clear_files() { - # @FIXME: - # Works implicitly and Fedora seems to have removed - # the cleanup call. Thus for now, inactive. - # File::Temp::cleanup(); -} - -require 5.008_000; # Tested with version 5.8.0. -select STDOUT; $| = 1; # make STDOUT output unbuffered -select STDERR; $| = 1; # make STDERR output unbuffered - -parse_command_line(\%opts); -check_tools; -$cfg = open_cfg_file($opts{config}); -process_config($cfg); -clear_files; - -__END__ - - -=head1 NAME - -mkpfi - Using GetOpt::Long, Pod::Usage, Config::IniFiles - - -=head1 SYNOPSIS - -mkpfi [OPTIONS ...] - - - OPTION - - [--config] [--help] [--man] - - -=head1 ABSTRACT - -Perl script for generating pdd pfi files from given config files. - -=head1 OPTIONS - -=over - -=item B<--help> - -Print out brief help message. - -=item B<--usage> - -Print usage. - -=item B<--config> - -Config input file. - -=item B<--man> - -Print manual page, same as 'perldoc mkpfi'. - -=item B<--verbose> - -Be verbose! - -=back - -=head1 BUGS - -Report via MTD mailing list - - -=head1 SEE ALSO - -http://www.linux-mtd.infradead.org/ - - -=head1 AUTHOR - -Oliver Lohmann (oliloh@de.ibm.com) - -=cut diff --git a/ubi-utils/src/nand2bin.c b/ubi-utils/src/nand2bin.c new file mode 100644 index 0000000..a5e8bca --- /dev/null +++ b/ubi-utils/src/nand2bin.c @@ -0,0 +1,328 @@ +/* + * 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 ... + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "nandecc.h" + +#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 error_t parse_opt (int key, char *arg, struct argp_state *state); + +const char *argp_program_bug_address = PACKAGE_BUGREPORT; + +static char doc[] = "\nVersion: " PACKAGE_VERSION "\n\t" + BUILD_OS" "BUILD_CPU" at "__DATE__" "__TIME__"\n" + "\nSplit data and OOB.\n"; + +static struct argp_option options[] = { + { .name = "pagesize", + .key = 'p', + .arg = "", + .flags = 0, + .doc = "NAND pagesize", + .group = OPTION_ARG_OPTIONAL }, + + { .name = "output", + .key = 'o', + .arg = "", + .flags = 0, + .doc = "Data output file", + .group = OPTION_ARG_OPTIONAL }, + + { .name = "oob", + .key = 'O', + .arg = "", + .flags = 0, + .doc = "OOB output file", + .group = OPTION_ARG_OPTIONAL }, + + { .name = NULL, .key = 0, .arg = NULL, .flags = 0, + .doc = NULL, .group = 0 }, +}; + +static struct argp argp = { + .options = options, + .parser = parse_opt, + .args_doc = "input.mif", + .doc = doc, + .children = NULL, + .help_filter = NULL, + .argp_domain = NULL, +}; + +/* + * 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 key The parameter. + * @param arg Argument passed to parameter. + * @param state Location to put information on parameters. + * + * @return error + * + * Get the `input' argument from `argp_parse', which we know is a + * pointer to our arguments structure. + */ +static error_t +parse_opt(int key, char *arg, struct argp_state *state) +{ + struct args *args = state->input; + + switch (key) { + case 'p': /* --pagesize */ + args->pagesize = str_to_num(arg); break; + + case 'o': /* --output= */ + args->output_file = arg; + break; + + case 'O': /* --oob= */ + args->output_file = arg; + break; + + case ARGP_KEY_NO_ARGS: + /* argp_usage(state); */ + break; + + case ARGP_KEY_ARG: + args->arg1 = arg; + /* Now we consume all the rest of the arguments. + `state->next' is the index in `state->argv' of the + next argument to be parsed, which is the first STRING + we're interested in, so we can just use + `&state->argv[state->next]' as the value for + arguments->strings. + + _In addition_, by setting `state->next' to the end + of the arguments, we can force argp to stop parsing here and + return. */ + + args->options = &state->argv[state->next]; + state->next = state->argc; + break; + + case ARGP_KEY_END: + /* argp_usage(state); */ + break; + + default: + return(ARGP_ERR_UNKNOWN); + } + + 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: %d\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; + + argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &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/nand2bin/nand2bin.c b/ubi-utils/src/nand2bin/nand2bin.c deleted file mode 100644 index a728fb5..0000000 --- a/ubi-utils/src/nand2bin/nand2bin.c +++ /dev/null @@ -1,327 +0,0 @@ -/* - * 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 ... - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "nandecc.h" - -#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 error_t parse_opt (int key, char *arg, struct argp_state *state); - -const char *argp_program_bug_address = ""; - -static char doc[] = "\nVersion: " VERSION "\n\t" - HOST_OS" "HOST_CPU" at "__DATE__" "__TIME__"\n" - "\nSplit data and OOB.\n"; - -static struct argp_option options[] = { - { .name = "pagesize", - .key = 'p', - .arg = "", - .flags = 0, - .doc = "NAND pagesize", - .group = OPTION_ARG_OPTIONAL }, - - { .name = "output", - .key = 'o', - .arg = "", - .flags = 0, - .doc = "Data output file", - .group = OPTION_ARG_OPTIONAL }, - - { .name = "oob", - .key = 'O', - .arg = "", - .flags = 0, - .doc = "OOB output file", - .group = OPTION_ARG_OPTIONAL }, - - { .name = NULL, .key = 0, .arg = NULL, .flags = 0, - .doc = NULL, .group = 0 }, -}; - -static struct argp argp = { - .options = options, - .parser = parse_opt, - .args_doc = "input.mif", - .doc = doc, - .children = NULL, - .help_filter = NULL, - .argp_domain = NULL, -}; - -/* - * 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 key The parameter. - * @param arg Argument passed to parameter. - * @param state Location to put information on parameters. - * - * @return error - * - * Get the `input' argument from `argp_parse', which we know is a - * pointer to our arguments structure. - */ -static error_t -parse_opt(int key, char *arg, struct argp_state *state) -{ - struct args *args = state->input; - - switch (key) { - case 'p': /* --pagesize */ - args->pagesize = str_to_num(arg); break; - - case 'o': /* --output= */ - args->output_file = arg; - break; - - case 'O': /* --oob= */ - args->output_file = arg; - break; - - case ARGP_KEY_NO_ARGS: - /* argp_usage(state); */ - break; - - case ARGP_KEY_ARG: - args->arg1 = arg; - /* Now we consume all the rest of the arguments. - `state->next' is the index in `state->argv' of the - next argument to be parsed, which is the first STRING - we're interested in, so we can just use - `&state->argv[state->next]' as the value for - arguments->strings. - - _In addition_, by setting `state->next' to the end - of the arguments, we can force argp to stop parsing here and - return. */ - - args->options = &state->argv[state->next]; - state->next = state->argc; - break; - - case ARGP_KEY_END: - /* argp_usage(state); */ - break; - - default: - return(ARGP_ERR_UNKNOWN); - } - - 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, size_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: %d\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; - - argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &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/nand2bin/nandcorr.c b/ubi-utils/src/nand2bin/nandcorr.c deleted file mode 100644 index 7f1a547..0000000 --- a/ubi-utils/src/nand2bin/nandcorr.c +++ /dev/null @@ -1,85 +0,0 @@ -/* - * 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/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 + +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..f71d916 --- /dev/null +++ b/ubi-utils/src/pddcustomize.c @@ -0,0 +1,496 @@ +/* + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "bootenv.h" +#include "error.h" +#include "example_ubi.h" +#include "libubi.h" +#include "ubimirror.h" + +typedef enum action_t { + ACT_NORMAL = 0, + ACT_LIST, + ACT_ARGP_ABORT, + ACT_ARGP_ERR, +} action_t; + +#define ABORT_ARGP do { \ + state->next = state->argc; \ + args->action = ACT_ARGP_ABORT; \ +} while (0) + +#define ERR_ARGP do { \ + state->next = state->argc; \ + args->action = ACT_ARGP_ERR; \ +} while (0) + +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" + "pddcustomize - customize bootenv and pdd values.\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: "input", key: 'i', arg: "", flags: 0, + doc: "Binary input file. For debug purposes.", + group: 1 }, + + { name: "output", key: 'o', arg: "", flags: 0, + doc: "Binary output file. For debug purposes.", + group: 1 }, + + { name: "list", key: 'l', arg: NULL, flags: 0, + doc: "List card bootenv/pdd values.", + group: 1 }, + + { name: "both", key: 'b', arg: NULL, flags: 0, + doc: "Mirror updated PDD to redundand copy.", + group: 1 }, + + { name: "side", key: 's', arg: "", flags: 0, + doc: "The side/seqnum to update.", + group: 1 }, + + { name: "host", key: 'x', arg: NULL, flags: 0, + doc: "use x86 platform for debugging.", + group: 1 }, + + { name: NULL, key: 0, arg: NULL, flags: 0, doc: NULL, group: 0 }, +}; + +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 error_t +parse_opt(int key, char *arg, struct argp_state *state) +{ + int rc = 0; + int err = 0; + + myargs *args = state->input; + + 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(arg); + if (args->side < 0) { + err_msg("Unsupported seqnum: %d.\n" + "Supported seqnums are '0' and '1'\n", + args->side, arg); + ERR_ARGP; + } + break; + case 'i': + args->file_in = arg; + break; + case 'o': + args->file_out = arg; + break; + case ARGP_KEY_ARG: + rc = extract_pair(args->env_in, arg); + if (rc != 0) + ERR_ARGP; + break; + case ARGP_KEY_END: + if (err) { + err_msg("\n"); + argp_usage(state); + ERR_ARGP; + } + break; + default: + return(ARGP_ERR_UNKNOWN); + } + + return 0; +} + +static struct argp argp = { + options: options, + parser: parse_opt, + args_doc: "[key=value] [...]", + doc: doc, + children: NULL, + help_filter: NULL, + argp_domain: NULL, +}; + + +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; + } + + argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &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/pddcustomize/pddcustomize.c b/ubi-utils/src/pddcustomize/pddcustomize.c deleted file mode 100644 index f71d916..0000000 --- a/ubi-utils/src/pddcustomize/pddcustomize.c +++ /dev/null @@ -1,496 +0,0 @@ -/* - * 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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "config.h" -#include "bootenv.h" -#include "error.h" -#include "example_ubi.h" -#include "libubi.h" -#include "ubimirror.h" - -typedef enum action_t { - ACT_NORMAL = 0, - ACT_LIST, - ACT_ARGP_ABORT, - ACT_ARGP_ERR, -} action_t; - -#define ABORT_ARGP do { \ - state->next = state->argc; \ - args->action = ACT_ARGP_ABORT; \ -} while (0) - -#define ERR_ARGP do { \ - state->next = state->argc; \ - args->action = ACT_ARGP_ERR; \ -} while (0) - -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" - "pddcustomize - customize bootenv and pdd values.\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: "input", key: 'i', arg: "", flags: 0, - doc: "Binary input file. For debug purposes.", - group: 1 }, - - { name: "output", key: 'o', arg: "", flags: 0, - doc: "Binary output file. For debug purposes.", - group: 1 }, - - { name: "list", key: 'l', arg: NULL, flags: 0, - doc: "List card bootenv/pdd values.", - group: 1 }, - - { name: "both", key: 'b', arg: NULL, flags: 0, - doc: "Mirror updated PDD to redundand copy.", - group: 1 }, - - { name: "side", key: 's', arg: "", flags: 0, - doc: "The side/seqnum to update.", - group: 1 }, - - { name: "host", key: 'x', arg: NULL, flags: 0, - doc: "use x86 platform for debugging.", - group: 1 }, - - { name: NULL, key: 0, arg: NULL, flags: 0, doc: NULL, group: 0 }, -}; - -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 error_t -parse_opt(int key, char *arg, struct argp_state *state) -{ - int rc = 0; - int err = 0; - - myargs *args = state->input; - - 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(arg); - if (args->side < 0) { - err_msg("Unsupported seqnum: %d.\n" - "Supported seqnums are '0' and '1'\n", - args->side, arg); - ERR_ARGP; - } - break; - case 'i': - args->file_in = arg; - break; - case 'o': - args->file_out = arg; - break; - case ARGP_KEY_ARG: - rc = extract_pair(args->env_in, arg); - if (rc != 0) - ERR_ARGP; - break; - case ARGP_KEY_END: - if (err) { - err_msg("\n"); - argp_usage(state); - ERR_ARGP; - } - break; - default: - return(ARGP_ERR_UNKNOWN); - } - - return 0; -} - -static struct argp argp = { - options: options, - parser: parse_opt, - args_doc: "[key=value] [...]", - doc: doc, - children: NULL, - help_filter: NULL, - argp_domain: NULL, -}; - - -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; - } - - argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &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 +#include +#include +#include +#include +#include + +#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 +#include + +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..c8d5ee4 --- /dev/null +++ b/ubi-utils/src/pfi.c @@ -0,0 +1,461 @@ +/* + * 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. + * + * Wed Feb 8 11:38:22 CET 2006: Initial creation. + */ + +#include +#include +#include +#include +#include +#include +#include + +#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 */ +}; + +static const char* modes[] = {"raw", "ubi", NULL}; /* 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) +{ + const char* ptr = modes[0]; + int i = 0; + while (ptr != NULL) { + if(strcmp(ptr, mode) == 0) { + return i; + } + ptr++; + 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 ((uint32_t)value == (uint32_t)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 + * Andreas Arnez + * Joern Engel + * Frank Haverkamp + * + * @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 /* 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 . + */ +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 . + * + * 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..6536c19 --- /dev/null +++ b/ubi-utils/src/pfi2bin.c @@ -0,0 +1,678 @@ +/* + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "config.h" +#include "list.h" +#include "error.h" +#include "reader.h" +#include "peb.h" +#include "crc32.h" + +#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)) = + "Licensed Materials - Property of IBM\n" + "IBM Flexible Support Processor Licensed Material\n" + "(c) Copyright IBM Corp 2006 All Rights Reserved.\n" + "US Government Users Restricted Rights - Use, duplication\n" + "or disclosure restricted by GSA ADP Schedule Contract\n" + "with IBM Corp."; + +static error_t parse_opt (int key, char *arg, struct argp_state *state); + +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" + "pfi2bin - a tool to convert PFI files into binary images.\n"; + +static struct argp_option options[] = { + /* COMMON */ + { name: NULL, key: 0, arg: NULL, flags: 0, + doc: "Common settings:", + group: OPTION_ARG_OPTIONAL}, + + { name: "verbose", key: 'v', arg: NULL, flags: 0, + doc: "Print more information.", + group: OPTION_ARG_OPTIONAL }, + + { name: "copyright", key: 'c', arg: NULL, flags: 0, + group: OPTION_ARG_OPTIONAL }, + + + /* INPUT */ + { name: NULL, key: 0, arg: NULL, flags: 0, + doc: "Input:", + group: 4}, + + { name: "platform", key: 'j', arg: "pdd-file", flags: 0, + doc: "PDD information which contains the card settings.", + group: 4 }, + + /* OUTPUT */ + { name: NULL, key: 0, arg: NULL, flags: 0, + doc: "Output:", + group: 5}, + + { name: "output", key: 'o', arg: "filename", flags: 0, + doc: "Outputfile, default: stdout.", + group: 5 }, + + { name: NULL, key: 0, arg: NULL, flags: 0, doc: NULL, group: 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 struct argp argp = { + options: options, + parser: parse_opt, + args_doc: "pfifile", + doc: doc, + children: NULL, + help_filter: NULL, + argp_domain: NULL, +}; + +static error_t +parse_opt(int key, char *arg, struct argp_state *state) +{ + myargs *args = state->input; + + switch (key) { + /* common settings */ + case 'v': /* --verbose= */ + args->verbose = 1; + break; + + case 'c': /* --copyright */ + fprintf(stderr, "%s\n", copyright); + exit(0); + break; + + case 'j': /* --platform */ + args->f_in_pdd = arg; + break; + + case 'o': /* --output */ + args->f_out = arg; + break; + + case ARGP_KEY_ARG: + args->f_in_pfi = arg; + /* args->arg1 = arg; */ + args->options = &state->argv[state->next]; + state->next = state->argc; + break; + + case ARGP_KEY_END: + if (args->action == ACT_NOTHING) { + argp_usage(state); + exit(1); + } + break; + + default: + return(ARGP_ERR_UNKNOWN); + } + + 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; + + 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); + + 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); + 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 */ + argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &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/pfi2bin/pfi2bin.c b/ubi-utils/src/pfi2bin/pfi2bin.c deleted file mode 100644 index 6536c19..0000000 --- a/ubi-utils/src/pfi2bin/pfi2bin.c +++ /dev/null @@ -1,678 +0,0 @@ -/* - * 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. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "config.h" -#include "list.h" -#include "error.h" -#include "reader.h" -#include "peb.h" -#include "crc32.h" - -#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)) = - "Licensed Materials - Property of IBM\n" - "IBM Flexible Support Processor Licensed Material\n" - "(c) Copyright IBM Corp 2006 All Rights Reserved.\n" - "US Government Users Restricted Rights - Use, duplication\n" - "or disclosure restricted by GSA ADP Schedule Contract\n" - "with IBM Corp."; - -static error_t parse_opt (int key, char *arg, struct argp_state *state); - -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" - "pfi2bin - a tool to convert PFI files into binary images.\n"; - -static struct argp_option options[] = { - /* COMMON */ - { name: NULL, key: 0, arg: NULL, flags: 0, - doc: "Common settings:", - group: OPTION_ARG_OPTIONAL}, - - { name: "verbose", key: 'v', arg: NULL, flags: 0, - doc: "Print more information.", - group: OPTION_ARG_OPTIONAL }, - - { name: "copyright", key: 'c', arg: NULL, flags: 0, - group: OPTION_ARG_OPTIONAL }, - - - /* INPUT */ - { name: NULL, key: 0, arg: NULL, flags: 0, - doc: "Input:", - group: 4}, - - { name: "platform", key: 'j', arg: "pdd-file", flags: 0, - doc: "PDD information which contains the card settings.", - group: 4 }, - - /* OUTPUT */ - { name: NULL, key: 0, arg: NULL, flags: 0, - doc: "Output:", - group: 5}, - - { name: "output", key: 'o', arg: "filename", flags: 0, - doc: "Outputfile, default: stdout.", - group: 5 }, - - { name: NULL, key: 0, arg: NULL, flags: 0, doc: NULL, group: 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 struct argp argp = { - options: options, - parser: parse_opt, - args_doc: "pfifile", - doc: doc, - children: NULL, - help_filter: NULL, - argp_domain: NULL, -}; - -static error_t -parse_opt(int key, char *arg, struct argp_state *state) -{ - myargs *args = state->input; - - switch (key) { - /* common settings */ - case 'v': /* --verbose= */ - args->verbose = 1; - break; - - case 'c': /* --copyright */ - fprintf(stderr, "%s\n", copyright); - exit(0); - break; - - case 'j': /* --platform */ - args->f_in_pdd = arg; - break; - - case 'o': /* --output */ - args->f_out = arg; - break; - - case ARGP_KEY_ARG: - args->f_in_pfi = arg; - /* args->arg1 = arg; */ - args->options = &state->argv[state->next]; - state->next = state->argc; - break; - - case ARGP_KEY_END: - if (args->action == ACT_NOTHING) { - argp_usage(state); - exit(1); - } - break; - - default: - return(ARGP_ERR_UNKNOWN); - } - - 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; - - 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); - - 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); - 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 */ - argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &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..04f62df --- /dev/null +++ b/ubi-utils/src/pfiflash.c @@ -0,0 +1,243 @@ +/* + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "error.h" +#include "config.h" + +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" + "pfiflash - a tool for updating a controller with PFI files.\n"; + +static const char copyright [] __attribute__((unused)) = + "FIXME: insert license type."; /* FIXME */ + +static struct argp_option options[] = { + /* Output options */ + { name: NULL, key: 0, arg: NULL, flags: 0, + doc: "Standard options:", + group: 1 }, + + { name: "copyright", key: 'c', arg: NULL, flags: 0, + doc: "Print copyright information.", + group: 1 }, + + { name: "verbose", key: 'v', arg: NULL, flags: 0, + doc: "Be verbose during program execution.", + group: 1 }, + + { name: "logfile", key: 'l', arg: "", flags: 0, + doc: "Write a logfile to .", + group: 1 }, + + /* Output options */ + { name: NULL, key: 0, arg: NULL, flags: 0, + doc: "Process options:", + group: 2 }, + + { name: "complete", key: 'C', arg: NULL, flags: 0, + doc: "Execute a complete system update. Updates both sides.", + group: 2 }, + + { name: "side", key: 's', arg: "", flags: 0, + doc: "Select the side which shall be updated.", + group: 2 }, + + { name: "pdd-update", key: 'p', arg: "", flags: 0, + doc: "Specify the pdd-update algorithm. is either " + "'keep', 'merge' or 'overwrite'.", + group: 2 }, + + { name: NULL, key: 0, arg: NULL, flags: 0, doc: NULL, group: 0 }, +}; + +typedef struct myargs { + int verbose; + const char *logfile; + + 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 error_t +parse_opt(int key, char *arg, struct argp_state *state) +{ + int err = 0; + + myargs *args = state->input; + + switch (key) { + /* standard options */ + case 'c': + err_msg("%s\n", copyright); + exit(0); + break; + case 'v': + args->verbose = 1; + break; + case 'l': + args->logfile = arg; + break; + /* process options */ + case 'C': + args->complete = 1; + break; + case 'p': + args->pdd_handling = get_pdd_handling(arg); + 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(arg); + if (args->seqnum < 0) { + err_quit("Unsupported side: %s.\n" + "Supported sides are '0' and '1'\n", arg); + } + break; + + case ARGP_KEY_ARG: /* input file */ + args->fp_in = fopen(arg, "r"); + if ((args->fp_in) == NULL) { + err_sys("Cannot open PFI file %s for input", arg); + } + args->arg1 = arg; + args->options = &state->argv[state->next]; + state->next = state->argc; + break; + case ARGP_KEY_END: + if (err) { + err_msg("\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: "[pfifile]", + doc: doc, + children: NULL, + help_filter: NULL, + argp_domain: NULL, +}; + +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 = "/tmp/pfiflash.log", + .pdd_handling = PDD_KEEP, + .fp_in = stdin, + }; + + argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &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; + } + + rc = pfiflash(args.fp_in, args.complete, args.seqnum, + args.pdd_handling, 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("Error: %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..fc2eede --- /dev/null +++ b/ubi-utils/src/pfiflash.h @@ -0,0 +1,62 @@ +#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 + * + * @brief The pfiflash library offers an interface for using the + * pfiflash * utility. + */ + +#include /* 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 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/pfiflash.c b/ubi-utils/src/pfiflash/pfiflash.c deleted file mode 100644 index 18b3aa2..0000000 --- a/ubi-utils/src/pfiflash/pfiflash.c +++ /dev/null @@ -1,243 +0,0 @@ -/* - * 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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include "error.h" -#include "config.h" - -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" - "pfiflash - a tool for updating a controller with PFI files.\n"; - -static const char copyright [] __attribute__((unused)) = - "FIXME: insert license type."; /* FIXME */ - -static struct argp_option options[] = { - /* Output options */ - { name: NULL, key: 0, arg: NULL, flags: 0, - doc: "Standard options:", - group: 1 }, - - { name: "copyright", key: 'c', arg: NULL, flags: 0, - doc: "Print copyright information.", - group: 1 }, - - { name: "verbose", key: 'v', arg: NULL, flags: 0, - doc: "Be verbose during program execution.", - group: 1 }, - - { name: "logfile", key: 'l', arg: "", flags: 0, - doc: "Write a logfile to .", - group: 1 }, - - /* Output options */ - { name: NULL, key: 0, arg: NULL, flags: 0, - doc: "Process options:", - group: 2 }, - - { name: "complete", key: 'C', arg: NULL, flags: 0, - doc: "Execute a complete system update. Updates both sides.", - group: 2 }, - - { name: "side", key: 's', arg: "", flags: 0, - doc: "Select the side which shall be updated.", - group: 2 }, - - { name: "pdd-update", key: 'p', arg: "", flags: 0, - doc: "Specify the pdd-update algorithm. is either " - "'keep', 'merge' or 'overwrite'.", - group: 2 }, - - { name: NULL, key: 0, arg: NULL, flags: 0, doc: NULL, group: 0 }, -}; - -typedef struct myargs { - int verbose; - const char *logfile; - - 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 error_t -parse_opt(int key, char *arg, struct argp_state *state) -{ - int err = 0; - - myargs *args = state->input; - - switch (key) { - /* standard options */ - case 'c': - err_msg("%s\n", copyright); - exit(0); - break; - case 'v': - args->verbose = 1; - break; - case 'l': - args->logfile = arg; - break; - /* process options */ - case 'C': - args->complete = 1; - break; - case 'p': - args->pdd_handling = get_pdd_handling(arg); - if (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(arg); - if (args->seqnum < 0) { - err_quit("Unsupported side: %s.\n" - "Supported sides are '0' and '1'\n", arg); - } - break; - - case ARGP_KEY_ARG: /* input file */ - args->fp_in = fopen(arg, "r"); - if ((args->fp_in) == NULL) { - err_sys("Cannot open PFI file %s for input", arg); - } - args->arg1 = arg; - args->options = &state->argv[state->next]; - state->next = state->argc; - break; - case ARGP_KEY_END: - if (err) { - err_msg("\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: "[pfifile]", - doc: doc, - children: NULL, - help_filter: NULL, - argp_domain: NULL, -}; - -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 = "/tmp/pfiflash.log", - .pdd_handling = PDD_KEEP, - .fp_in = stdin, - }; - - argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &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; - } - - rc = pfiflash(args.fp_in, args.complete, args.seqnum, - args.pdd_handling, 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("Error: %s\nrc: %d\n", err_buf, rc); - return rc; -} diff --git a/ubi-utils/src/reader.c b/ubi-utils/src/reader.c new file mode 100644 index 0000000..e4a8ceb --- /dev/null +++ b/ubi-utils/src/reader.c @@ -0,0 +1,452 @@ +/* + * 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 +#include +#include +#include +#include + +#include "config.h" +#include "bootenv.h" +#include "reader.h" + +/* @FIXME hard coded offsets right now - get them from Artem? */ +#define NAND_DEFAULT_VID_HDR_OFF 1984 +#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) { + res->flash_type = NAND_FLASH; + res->vid_hdr_offset = NAND_DEFAULT_VID_HDR_OFF; + } + 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; +} + +/** + * FIXME enhance flasing raw PFI content e.g. IPLs for NAND and NOR. + * Here is one of the only places where the flash type and its special + * handling is exposed to the users. + */ +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; + + 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_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, + &(res->starts_size), &(res->starts)); + 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; +} + +/** + * FIXME Enhance reading raw PFI sections, e.g. IPL. See comment at + * write_pfi_ubi. + */ +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; + + 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_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, &(res->ids_size), + &(res->ids)); + 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, &(res->names_size), + &(tmp_names)); + 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..93c15e3 --- /dev/null +++ b/ubi-utils/src/reader.h @@ -0,0 +1,84 @@ +#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 +#include + +#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 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; +}; + +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) */ +}; + +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 +#include +#include +#include +#include +#include +#include +#include + +#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/ubicrc32/ubicrc32.c b/ubi-utils/src/ubicrc32/ubicrc32.c deleted file mode 100644 index fb4ef49..0000000 --- a/ubi-utils/src/ubicrc32/ubicrc32.c +++ /dev/null @@ -1,143 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#include -#include -#include - -#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/ubicrc32/ubicrc32.pl b/ubi-utils/src/ubicrc32/ubicrc32.pl deleted file mode 100755 index add5f9d..0000000 --- a/ubi-utils/src/ubicrc32/ubicrc32.pl +++ /dev/null @@ -1,74 +0,0 @@ -#!/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/src/ubigen.c b/ubi-utils/src/ubigen.c new file mode 100644 index 0000000..8a464dd --- /dev/null +++ b/ubi-utils/src/ubigen.c @@ -0,0 +1,369 @@ +/* + * 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 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ubigen.h" +#include "config.h" + +typedef enum action_t { + ACT_NORMAL = 0x00000001, + ACT_BROKEN_UPDATE = 0x00000002, +} action_t; + + +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" + "ubigen - a tool for adding UBI information to a binary input file.\n"; + +static const char copyright [] __attribute__((unused)) = + "FIXME: insert license type"; /* FIXME */ + +#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) + +static struct argp_option options[] = { + /* COMMON */ + { name: NULL, key: 0, arg: NULL, flags: 0, + doc: "Common settings:", + group: 1}, + + { name: "copyright", key: 'c', arg: NULL, flags: 0, + doc: "Print copyright information.", + group: 1 }, + + { name: "verbose", key: 'v', arg: NULL, flags: 0, + doc: "Print more progress information.", + group: 1 }, + + { name: "debug", key: 'd', arg: NULL, flags: 0, + group: 1 }, + + + /* INPUT */ + { name: NULL, key: 0, arg: NULL, flags: 0, + doc: "UBI Settings:", + group: 4}, + + { name: "alignment", key: 'A', arg: "", flags: 0, + doc: "Set the alignment size to (default 1).\n" + "Values can be specified as bytes, 'ki' or 'Mi'.", + group: 4 }, + + { name: "blocksize", key: 'B', arg: "", flags: 0, + doc: "Set the eraseblock size to (default 128 KiB).\n" + "Values can be specified as bytes, 'ki' or 'Mi'.", + group: 4 }, + + { name: "erasecount", key: 'E', arg: "", flags: 0, + doc: "Set the erase count to (default 0)", + group: 4 }, + + { name: "setver", key: 'X', arg: "", flags: 0, + doc: "Set UBI version number to (default 1)", + group: 4 }, + + { name: "id", key: 'I', arg: "", flags: 0, + doc: "The UBI volume id.", + group: 4 }, + + + { name: "offset", key: 'O', arg: "", flags: 0, + doc: "Offset from start of an erase block to the UBI volume header.", + group: 4 }, + + { name: "type", key: 'T', arg: "", flags: 0, + doc: "The UBI volume type:\n1 = dynamic, 2 = static", + group: 4 }, + + /* INPUT/OUTPUT */ + { name: NULL, key: 0, arg: NULL, flags: 0, + doc: "Input/Output:", + group: 5 }, + + { name: "infile", key: 'i', arg: "", flags: 0, + doc: "Read input from file.", + group: 5 }, + + { name: "outfile", key: 'o', arg: "", flags: 0, + doc: "Write output to file (default is stdout).", + group: 5 }, + + /* Special options */ + { name: NULL, key: 0, arg: NULL, flags: 0, + doc: "Special options:", + group: 6 }, + + { name: "broken-update", key: 'U', arg: "", flags: 0, + doc: "Create an ubi image which simulates a broken update.\n" + " specifies the logical eraseblock number to update.\n", + group: 6 }, + + { name: NULL, key: 0, arg: NULL, flags: 0, doc: NULL, group: 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 error_t +parse_opt(int key, char *arg, struct argp_state *state) +{ + int err = 0; + char* endp; + + myargs *args = state->input; + + switch (key) { + case 'c': + fprintf(stderr, "%s\n", copyright); + exit(0); + break; + case 'o': /* output */ + args->fp_out = fopen(arg, "wb"); + if ((args->fp_out) == NULL) { + fprintf(stderr, "Cannot open file %s for output\n", + arg); + exit(1); + } + break; + case 'i': /* input */ + args->fp_in = fopen(arg, "rb"); + if ((args->fp_in) == NULL) { + fprintf(stderr, "Cannot open file %s for input\n", + arg); + exit(1); + } + break; + case 'v': /* verbose */ + args->verbose = 1; + break; + + case 'B': /* eb_size */ + args->eb_size = (uint32_t) ustrtoul(arg, &endp, 0); + CHECK_ENDP("B", endp); + break; + case 'E': /* erasecount */ + args->ec = (uint64_t) strtoul(arg, &endp, 0); + CHECK_ENDP("E", endp); + break; + case 'I': /* id */ + args->id = (uint16_t) strtoul(arg, &endp, 0); + CHECK_ENDP("I", endp); + break; + case 'T': /* type */ + args->type = (uint16_t) strtoul(arg, &endp, 0); + CHECK_ENDP("T", endp); + break; + case 'X': /* versionnr */ + args->version = (uint8_t) strtoul(arg, &endp, 0); + CHECK_ENDP("X", endp); + break; + case 'O': /* offset for volume hdr */ + args->hdr_offset = + (uint32_t) strtoul(arg, &endp, 0); + CHECK_ENDP("O", endp); + break; + + case 'U': /* broken update */ + args->action = ACT_BROKEN_UPDATE; + args->update_block = + (uint32_t) strtoul(arg, &endp, 0); + CHECK_ENDP("U", endp); + break; + + case ARGP_KEY_ARG: + if (!args->fp_in) { + 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 (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, "\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: 0, + doc: doc, + children: NULL, + help_filter: NULL, + argp_domain: NULL, +}; + + +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 */ + argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &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 /* FILE */ +#include +#include + +#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/ubigen/ubigen_main.c b/ubi-utils/src/ubigen/ubigen_main.c deleted file mode 100644 index 8a464dd..0000000 --- a/ubi-utils/src/ubigen/ubigen_main.c +++ /dev/null @@ -1,369 +0,0 @@ -/* - * 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 - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ubigen.h" -#include "config.h" - -typedef enum action_t { - ACT_NORMAL = 0x00000001, - ACT_BROKEN_UPDATE = 0x00000002, -} action_t; - - -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" - "ubigen - a tool for adding UBI information to a binary input file.\n"; - -static const char copyright [] __attribute__((unused)) = - "FIXME: insert license type"; /* FIXME */ - -#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) - -static struct argp_option options[] = { - /* COMMON */ - { name: NULL, key: 0, arg: NULL, flags: 0, - doc: "Common settings:", - group: 1}, - - { name: "copyright", key: 'c', arg: NULL, flags: 0, - doc: "Print copyright information.", - group: 1 }, - - { name: "verbose", key: 'v', arg: NULL, flags: 0, - doc: "Print more progress information.", - group: 1 }, - - { name: "debug", key: 'd', arg: NULL, flags: 0, - group: 1 }, - - - /* INPUT */ - { name: NULL, key: 0, arg: NULL, flags: 0, - doc: "UBI Settings:", - group: 4}, - - { name: "alignment", key: 'A', arg: "", flags: 0, - doc: "Set the alignment size to (default 1).\n" - "Values can be specified as bytes, 'ki' or 'Mi'.", - group: 4 }, - - { name: "blocksize", key: 'B', arg: "", flags: 0, - doc: "Set the eraseblock size to (default 128 KiB).\n" - "Values can be specified as bytes, 'ki' or 'Mi'.", - group: 4 }, - - { name: "erasecount", key: 'E', arg: "", flags: 0, - doc: "Set the erase count to (default 0)", - group: 4 }, - - { name: "setver", key: 'X', arg: "", flags: 0, - doc: "Set UBI version number to (default 1)", - group: 4 }, - - { name: "id", key: 'I', arg: "", flags: 0, - doc: "The UBI volume id.", - group: 4 }, - - - { name: "offset", key: 'O', arg: "", flags: 0, - doc: "Offset from start of an erase block to the UBI volume header.", - group: 4 }, - - { name: "type", key: 'T', arg: "", flags: 0, - doc: "The UBI volume type:\n1 = dynamic, 2 = static", - group: 4 }, - - /* INPUT/OUTPUT */ - { name: NULL, key: 0, arg: NULL, flags: 0, - doc: "Input/Output:", - group: 5 }, - - { name: "infile", key: 'i', arg: "", flags: 0, - doc: "Read input from file.", - group: 5 }, - - { name: "outfile", key: 'o', arg: "", flags: 0, - doc: "Write output to file (default is stdout).", - group: 5 }, - - /* Special options */ - { name: NULL, key: 0, arg: NULL, flags: 0, - doc: "Special options:", - group: 6 }, - - { name: "broken-update", key: 'U', arg: "", flags: 0, - doc: "Create an ubi image which simulates a broken update.\n" - " specifies the logical eraseblock number to update.\n", - group: 6 }, - - { name: NULL, key: 0, arg: NULL, flags: 0, doc: NULL, group: 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 error_t -parse_opt(int key, char *arg, struct argp_state *state) -{ - int err = 0; - char* endp; - - myargs *args = state->input; - - switch (key) { - case 'c': - fprintf(stderr, "%s\n", copyright); - exit(0); - break; - case 'o': /* output */ - args->fp_out = fopen(arg, "wb"); - if ((args->fp_out) == NULL) { - fprintf(stderr, "Cannot open file %s for output\n", - arg); - exit(1); - } - break; - case 'i': /* input */ - args->fp_in = fopen(arg, "rb"); - if ((args->fp_in) == NULL) { - fprintf(stderr, "Cannot open file %s for input\n", - arg); - exit(1); - } - break; - case 'v': /* verbose */ - args->verbose = 1; - break; - - case 'B': /* eb_size */ - args->eb_size = (uint32_t) ustrtoul(arg, &endp, 0); - CHECK_ENDP("B", endp); - break; - case 'E': /* erasecount */ - args->ec = (uint64_t) strtoul(arg, &endp, 0); - CHECK_ENDP("E", endp); - break; - case 'I': /* id */ - args->id = (uint16_t) strtoul(arg, &endp, 0); - CHECK_ENDP("I", endp); - break; - case 'T': /* type */ - args->type = (uint16_t) strtoul(arg, &endp, 0); - CHECK_ENDP("T", endp); - break; - case 'X': /* versionnr */ - args->version = (uint8_t) strtoul(arg, &endp, 0); - CHECK_ENDP("X", endp); - break; - case 'O': /* offset for volume hdr */ - args->hdr_offset = - (uint32_t) strtoul(arg, &endp, 0); - CHECK_ENDP("O", endp); - break; - - case 'U': /* broken update */ - args->action = ACT_BROKEN_UPDATE; - args->update_block = - (uint32_t) strtoul(arg, &endp, 0); - CHECK_ENDP("U", endp); - break; - - case ARGP_KEY_ARG: - if (!args->fp_in) { - 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 (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, "\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: 0, - doc: doc, - children: NULL, - help_filter: NULL, - argp_domain: NULL, -}; - - -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 */ - argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &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/ubiinfo/ubiflash.h b/ubi-utils/src/ubiinfo/ubiflash.h deleted file mode 100644 index 6883879..0000000 --- a/ubi-utils/src/ubiinfo/ubiflash.h +++ /dev/null @@ -1,185 +0,0 @@ -#ifndef _UBI_FLASH_H -#define _UBI_FLASH_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. - */ - -/* - * FLASH related data structures and constants for UBI. - * UBI scan analysis. - * - * IPL Initial Program Loader - * SPL Secondary Program Loader - */ - -#include -#include -#include - -#define UBI_BLOCK_IDENT_MAX 16 - -/* Block status information constants */ -enum blockstat { - /* IO Error */ - STAT_IO_FAILED = 1, /* 0xffffffff */ - /* Block is bad */ - STAT_BLOCK_BAD = 2, /* 0xfffffffe */ - /* ECC unrecoverable error */ - STAT_ECC_ERROR = 3, /* 0xfffffffd */ - /* CRC checksum failed */ - STAT_CRC_ERROR = 4, /* 0xfffffffc */ - /* Magic number not available */ - STAT_NO_MAGIC = 5, /* 0xfffffffb */ - /* No image available */ - STAT_NO_IMAGE = 6, - /* Image is invalid */ - STAT_INVALID_IMAGE = 7, - /* Image is defect */ - STAT_DEFECT_IMAGE = 8, -}; - -/* - * Flash types - */ -enum flashtypes { - FLASH_TYPE_NAND = 1, - FLASH_TYPE_NOR, -}; - -/* Nand read buffer size: 2KiB + 64byte spare */ -#define NAND_READ_BUF_SIZE (2048 + 64) - -/* Size of the CRC table */ -#define CRC32_TABLE_SIZE 256 - -/* Image is not available marker for image offs */ -#define UBI_IMAGE_NOT_AVAILABLE 0xFFFFFFFF - -/* Increment this number, whenever you change the structure */ -#define UBI_SCAN_INFO_VERSION 2 - -/* Time measurement as far as the code size allows us to do this */ -#define UBI_TIMESTAMPS 16 - -/** - * struct ubi_scan_info - RAM table filled by IPL scan - * - * @version: Version of the structure - * @bootstatus: Boot status of the current boot - * @flashtype: Flash type (NAND/NOR) - * @flashid: ID of the flash chip - * @flashmfr: Manufacturer ID of the flash chip - * @flashsize: Size of the FLASH - * @blocksize: Eraseblock size - * @blockshift: Shift count to calc block number from offset - * @nrblocks: Number of erase blocks on flash - * @pagesize: Pagesize (NAND) - * @blockinfo: Pointer to an array of block status information - * filled by FLASH scan - * @images: Pointer to FLASH block translation table sorted - * by image type and load order - * @imageblocks: Number of blocks found per image - * @imageoffs: Offset per imagetype to the first - * block in the translation table - * @imagedups duplicate blocks (max. one per volume) - * @imagelen: Length of the loaded image - * @crc32_table: CRC32 table buffer - * @page_buf: Page buffer for NAND FLASH - */ -struct ubi_scan_info { - int version; - unsigned int bootstatus; - unsigned int flashtype; - unsigned int flashid; - unsigned int flashmfr; - unsigned int flashsize; - unsigned int blocksize; - unsigned int blockshift; - unsigned int nrblocks; - unsigned int pagesize; - - struct ubi_vid_hdr *blockinfo; - struct ubi_vid_hdr **images; - unsigned int imageblocks[UBI_BLOCK_IDENT_MAX]; - unsigned int imageoffs[UBI_BLOCK_IDENT_MAX]; - struct ubi_vid_hdr *imagedups[UBI_BLOCK_IDENT_MAX]; - unsigned int imagelen; - uint32_t crc32_table[CRC32_TABLE_SIZE]; - uint8_t page_buf[NAND_READ_BUF_SIZE]; - unsigned int times[UBI_TIMESTAMPS]; -}; - -/* External function definition */ -extern int flash_read(void *buf, unsigned int offs, int len); -extern int flash_read_slice(struct ubi_scan_info *fi, void *buf, - unsigned int offs, int len); -extern void ipl_main(struct ubi_scan_info *fi); - -#ifndef CFG_EXAMPLE_IPL -extern int ipl_scan(struct ubi_scan_info *fi); -extern int ipl_load(struct ubi_scan_info *fi, int nr, uint8_t *loadaddr); - -#define IPL_STATIC - -#else -#define IPL_STATIC static -#endif - -/** - * get_boot_status - get the boot status register - * - * Shift the lower 16 bit into the upper 16 bit and return - * the result. - */ -uint32_t get_boot_status(void); - -/** - * set_boot_status - Set the boot status register - * - * @status: The status value to set - * - */ -void set_boot_status(uint32_t status); - -static inline unsigned int ubi_vid_offset(struct ubi_scan_info *fi) -{ - if (fi->flashtype == FLASH_TYPE_NOR) - return UBI_EC_HDR_SIZE; - else - return fi->pagesize - UBI_VID_HDR_SIZE; -} - -static inline unsigned int ubi_data_offset(struct ubi_scan_info *fi) -{ - if (fi->flashtype == FLASH_TYPE_NOR) - return UBI_EC_HDR_SIZE + UBI_VID_HDR_SIZE; - else - return fi->pagesize; -} - -/** - * IPL checkpoints - */ -#define CHKP_HWINIT 0x3030 -#define CHKP_IPLSCAN_FAILED 0x3034 -#define CHKP_SPL_START 0x3037 -#define CHKP_SPLLOAD_STATUS 0x3130 - -extern void checkpoint(uint32_t cpoint); -extern void switch_watchdog(void); - -#endif diff --git a/ubi-utils/src/ubiinfo/ubiinfo.c b/ubi-utils/src/ubiinfo/ubiinfo.c deleted file mode 100644 index 6f7443b..0000000 --- a/ubi-utils/src/ubiinfo/ubiinfo.c +++ /dev/null @@ -1,406 +0,0 @@ -/* - * 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. - */ - -/* - * Print out information about the UBI table this IPL is using. This - * can be used afterwards to analyze misbehavior of the IPL code. The - * input this program requires is the last 1 MiB DDRAM of our system - * where the scanning table is placed into. - * - * Author: Frank Haverkamp - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define __unused __attribute__((unused)) - -/* This should hopefully be constant and the same in all - * configurations. - */ -#define CFG_IPLSIZE 512 -#define CFG_SPLCODE 512 -#define MEMTOP 0x06600000 /* Sunray 102 MiB */ -#define MEMSIZE 0x00100000 /* 1 MiB */ -#define CODE_SIZE (64 * 1024) - -/* FIXME Except of the memory size this should be defined via - * parameters - * - * CFG_MEMTOP_BAMBOO 0x02000000 - * CFG_MEMTOP_SUNRAY 0x06600000 - */ - -#include "ubiipl.h" -#include "ubiflash.h" - -#define MIN(x,y) ((x)<(y)?(x):(y)) - -#define ERR_RET(rc) { \ - fprintf(stderr, "%s:%d failed rc=%d\n", __func__, \ - __LINE__, (rc)); \ - return (rc); \ - } - -#define VERSION "1.3" - -static error_t parse_opt (int key, char *arg, struct argp_state *state); -const char *argp_program_version = VERSION; -const char *argp_program_bug_address = ""; - -static char doc[] = "\nVersion: " VERSION "\n\t" - " at "__DATE__" "__TIME__"\n" - "\n" - "Test program\n"; - -static struct argp_option options[] = { - /* common settings */ - { .name = "verbose", - .key = 'v', - .arg = "", - .flags = 0, - .doc = "Set verbosity level to ", - .group = OPTION_ARG_OPTIONAL }, - - { .name = "memtop", - .key = 'm', - .arg = "", - .flags = 0, - .doc = "Set top of memory, 102 MiB for Sunray and 16 MiB for Bamboo", - .group = OPTION_ARG_OPTIONAL }, - - { .name = NULL, - .key = 0, - .arg = NULL, - .flags = 0, - .doc = NULL, - .group = 0 }, -}; - -typedef struct test_args { - int verbose; - unsigned long memtop; - char *arg1; - char **options; -} test_args; - -static struct test_args g_args = { - .memtop = MEMTOP, - .verbose = 0, - .arg1 = NULL, - .options = NULL, -}; - -static struct argp argp = { - options: options, - parser: parse_opt, - args_doc: "[last_1MiB_memory.bin]", - doc: doc, - children: NULL, - help_filter: NULL, - argp_domain: NULL, -}; - -static int verbose = 0; - -/** - * @brief Parse the arguments passed into the test case. - * - * @param key The parameter. - * @param arg Argument passed to parameter. - * @param state Location to put information on parameters. - * - * @return error_t - */ -static error_t -parse_opt(int key, char *arg, struct argp_state *state) -{ - /* Get the `input' argument from `argp_parse', which we - know is a pointer to our arguments structure. */ - test_args *args = state->input; - - switch (key) { - /* common settings */ - case 'v': /* --verbose= */ - verbose = args->verbose = strtoul(arg, (char **)NULL, 0); - break; - - case 'm': /* --memtop */ - args->memtop = strtoul(arg, (char **)NULL, 0); - break; - - case ARGP_KEY_NO_ARGS: - /* argp_usage(state); */ - break; - - case ARGP_KEY_ARG: - args->arg1 = arg; - /* Now we consume all the rest of the arguments. - `state->next' is the index in `state->argv' of the - next argument to be parsed, which is the first STRING - we're interested in, so we can just use - `&state->argv[state->next]' as the value for - arguments->strings. - - _In addition_, by setting `state->next' to the end - of the arguments, we can force argp to stop parsing - here and return. */ - - args->options = &state->argv[state->next]; - state->next = state->argc; - break; - - case ARGP_KEY_END: - /* print out message if no arguments are given but PFI - write should be done */ - break; - - default: - return(ARGP_ERR_UNKNOWN); - } - return 0; -} - -static void -hexdump(const char *buf, int len) -{ - char line[16]; - char str[256]; - char dummy[256]; - int j = 0; - - while (len > 0) { - int i; - - strcpy(str, " "); - - for (j = 0; j < MIN(16, len); j++) - line[j] = *buf++; - - for (i = 0; i < j; i++) { - if (!(i & 3)) { - sprintf(dummy, " %.2x", line[i] & 0xff); - strcat(str, dummy); - } else { - sprintf(dummy, "%.2x", line[i] & 0xff); - strcat(str, dummy); - } - } - - /* Print empty space */ - for (; i < 16; i++) - if (!(i & 1)) - strcat(str, " "); - else - strcat(str, " "); - - strcat(str, " "); - for (i = 0; i < j; i++) { - if (isprint(line[i])) { - sprintf(dummy, "%c", line[i]); - strcat(str, dummy); - } else { - strcat(str, "."); - } - } - printf("%s\n", str); - len -= 16; - } -} - -static void -print_status_help(void) -{ - printf("Error Codes from IPL\n"); - printf(" IO Error %d\n", STAT_IO_FAILED); - printf(" Block is bad %d\n", STAT_BLOCK_BAD); - printf(" ECC unrec error %d\n", STAT_ECC_ERROR); - printf(" CRC csum failed %d\n", STAT_CRC_ERROR); - printf(" Magic not avail %d\n", STAT_NO_MAGIC); - printf(" No image avail %d\n", STAT_NO_IMAGE); - printf(" Image is invalid %d\n", STAT_INVALID_IMAGE); - printf(" Image is defect %d\n\n", STAT_DEFECT_IMAGE); - -} - -static void -print_ubi_scan_info(struct ubi_scan_info *fi) -{ - int i; - - printf("ubi_scan_info\n"); - printf(" version %08x\n", ntohl(fi->version)); - printf(" bootstatus %08x\n", ntohl(fi->bootstatus)); - printf(" flashtype %08x\n", ntohl(fi->flashtype)); - printf(" flashid %08x\n", ntohl(fi->flashid)); - printf(" flashmfgr %08x\n", ntohl(fi->flashmfr)); - printf(" flashsize %d bytes (%dM)\n", - ntohl(fi->flashsize), ntohl(fi->flashsize) / (1024 * 1024)); - printf(" blocksize %d bytes\n", ntohl(fi->blocksize)); - printf(" blockshift %d\n", ntohl(fi->blockshift)); - printf(" nrblocks %d\n", ntohl(fi->nrblocks)); - printf(" pagesize %d\n", ntohl(fi->pagesize)); - printf(" imagelen %d\n", ntohl(fi->imagelen)); - printf(" blockinfo %08x\n", ntohl((int)fi->blockinfo)); - - printf(" nr imageblocks imageoffs\n"); - for (i = 0; i < UBI_BLOCK_IDENT_MAX; i++) - printf(" [%2d] %08x %08x\n", i, - ntohl(fi->imageblocks[i]), - ntohl(fi->imageoffs[i])); - - for (i = 0; i < UBI_TIMESTAMPS; i++) { - if (!ntohl(fi->times[i])) - continue; - printf("time[%3d] = %08x %.3f sec\n", i, ntohl(fi->times[i]), - (double)ntohl(fi->times[i]) / 500000000.0); - } - - printf("crc32_table\n"); - hexdump((char *)&fi->crc32_table, sizeof(fi->crc32_table)); - printf("\npage_buf\n"); - hexdump((char *)&fi->page_buf, sizeof(fi->page_buf)); - - printf("\n"); - -} - -static void -print_ubi_block_info(struct ubi_scan_info *fi, - struct ubi_vid_hdr *bi, int nr) -{ - int i; - int unknown = 0; - - printf("\nBINFO\n"); - - for (i = 0; i < nr; i++) { - if ((int)ubi32_to_cpu(bi[i].magic) != UBI_VID_HDR_MAGIC) { - printf("block=%d %08x\n", - i, i * ntohl(fi->blocksize)); -#if 0 - printf("."); - if ((unknown & 0x3f) == 0x3f) - printf("\n"); - unknown++; -#else - hexdump((char *)&bi[i], - sizeof(struct ubi_vid_hdr)); -#endif - } else { - if (unknown) - printf("\n"); - printf("block=%d %08x\n" - " leb_ver=0x%x data_size=%d " - "lnum=%d used_ebs=0x%x\n" - " data_crc=%08x hdr_crc=%08x\n", - i, i * ntohl(fi->blocksize), - ubi32_to_cpu(bi[i].leb_ver), - ubi32_to_cpu(bi[i].data_size), - ubi32_to_cpu(bi[i].lnum), - ubi32_to_cpu(bi[i].used_ebs), - ubi32_to_cpu(bi[i].data_crc), - ubi32_to_cpu(bi[i].hdr_crc)); - hexdump((char *)&bi[i], - sizeof(struct ubi_vid_hdr)); - unknown = 0; - } - } -} - -static int do_read(unsigned int memtop, char *buf, int buf_len __unused) -{ - unsigned long finfo_addr; - unsigned long binfo_addr; - unsigned long images_addr; - unsigned long nrblocks; - unsigned long bi_size; - unsigned long images_size; - struct ubi_scan_info *fi; - struct ubi_vid_hdr *bi; - char *images; - unsigned long memaddr = memtop - MEMSIZE; - - print_status_help(); - - /* Read and print FINFO */ - finfo_addr = MEMSIZE - CFG_IPLSIZE * 1024; - - printf("read info at addr %08lx\n", finfo_addr); - fi = (struct ubi_scan_info *)(buf + finfo_addr); - - binfo_addr = ntohl((unsigned long)fi->blockinfo) - memaddr; - images_addr = ntohl((unsigned long)fi->images) - memaddr; - nrblocks = ntohl(fi->nrblocks); - - printf("BINFO %08lx\n", binfo_addr); - - bi_size = nrblocks * sizeof(struct ubi_vid_hdr); - images_size = nrblocks * sizeof(unsigned int); - - printf("FINFO\n"); - print_ubi_scan_info(fi); - /* hexdump((char *)fi, sizeof(*fi)); */ - - /* Read and print BINFO */ - bi = (struct ubi_vid_hdr *)(buf + binfo_addr); - print_ubi_block_info(fi, bi, nrblocks); - - /* Read and print IMAGES */ - images = buf + images_addr; - printf("\nIMAGES\n"); - hexdump(images, images_size); - - return 0; -} - -int main(int argc, char *argv[]) -{ - char buf[MEMSIZE]; - FILE *fp; - int rc; - - argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &g_args); - - if (!g_args.arg1) { - fprintf(stderr, "Please specify a file " - "name for memory dump!\n"); - exit(EXIT_FAILURE); - } - - memset(buf, 0xAB, sizeof(buf)); - - fp = fopen(g_args.arg1, "r"); - if (!fp) - exit(EXIT_FAILURE); - rc = fread(buf, 1, sizeof(buf), fp); - if (rc != sizeof(buf)) - exit(EXIT_FAILURE); - fclose(fp); - do_read(g_args.memtop, buf, sizeof(buf)); - - exit(EXIT_SUCCESS); -} diff --git a/ubi-utils/src/ubiinfo/ubiipl.h b/ubi-utils/src/ubiinfo/ubiipl.h deleted file mode 100644 index 3a8b900..0000000 --- a/ubi-utils/src/ubiinfo/ubiipl.h +++ /dev/null @@ -1,87 +0,0 @@ -#ifndef _UBI_IPL_H -#define _UBI_IPL_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. - */ - -/* - * Constants calculated from the CFG_XXX defines - * - * Declaration of the loader function which is invoked by the - * assembler part of the IPL - */ - -/* Size of IPL - is 4K for NAND and can also be 4K for NOR */ -#define IPL_SIZE 4096 - -/* Needed in asm code to upload the data, needed in C-code for CRC32 */ -#define IPL_RAMADDR (CFG_MEMTOP - IPL_SIZE) - -#if !defined(__ASSEMBLY__) - -#include -#include - -/* Address of the flash info structure */ -#define FINFO_ADDR (struct ubi_scan_info *) (CFG_MEMTOP - CFG_IPLSIZE * 1024) - -/* Size of the flash info structure */ -#define FINFO_SIZE sizeof(struct ubi_scan_info) - -/* Blockinfo array address */ -#define BINFO_ADDR (struct ubi_vid_hdr *) ((void *)FINFO_ADDR + FINFO_SIZE) - -/* Number of erase blocks */ -#define NR_ERASE_BLOCKS ((CFG_FLASHSIZE * 1024) / CFG_BLOCKSIZE) - -/* Blockinfo size */ -#define BINFO_SIZE (NR_ERASE_BLOCKS * sizeof(struct ubi_vid_hdr)) - -/* Images array address */ -#define IMAGES_ADDR (struct ubi_vid_hdr **) ((void *)BINFO_ADDR + BINFO_SIZE) - -/* Images array size */ -#define IMAGES_SIZE (NR_ERASE_BLOCKS * sizeof(unsigned int)) - -/* Total size of flash info + blockinfo + images */ -#define INFO_SIZE ((FINFO_SIZE + BINFO_SIZE + IMAGES_SIZE) / sizeof(uint32_t)) - -/* Load address of the SPL */ -#define SPL_ADDR (void *) ((void *)FINFO_ADDR - CFG_SPLCODE * 1024) - -#define IPL_SIZE_CRC32 (IPL_SIZE - sizeof(uint32_t)) -#define IPL_RAMADDR_CRC32 ((void *)(IPL_RAMADDR + sizeof(uint32_t))) - -/* - * Linker script magic to ensure that load_spl() is linked to the - * right place - */ -#define __crc32 __attribute__((__section__(".crc32"))) -#define __entry __attribute__((__section__(".entry.text"))) -#define __unused __attribute__((unused)) - -#define MIN(x,y) ((x)<(y)?(x):(y)) - -#define stop_on_error(x) \ - { while (1); } - -void __entry load_spl(void); -void hardware_init(void); - -#endif /* __ASSEMBLY__ */ - -#endif diff --git a/ubi-utils/src/ubimirror.c b/ubi-utils/src/ubimirror.c new file mode 100644 index 0000000..e43ba10 --- /dev/null +++ b/ubi-utils/src/ubimirror.c @@ -0,0 +1,206 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "error.h" +#include "example_ubi.h" +#include "ubimirror.h" + +typedef enum action_t { + ACT_NORMAL = 0, + ACT_ARGP_ABORT, + ACT_ARGP_ERR, +} action_t; + +#define ABORT_ARGP do { \ + state->next = state->argc; \ + args->action = ACT_ARGP_ABORT; \ +} while (0) + +#define ERR_ARGP do { \ + state->next = state->argc; \ + args->action = ACT_ARGP_ERR; \ +} while (0) + +#define VOL_ARGS_MAX 2 + + +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" + "ubimirror - mirrors ubi volumes.\n"; + +static const char copyright [] __attribute__((unused)) = + "(C) IBM Coorporation 2007"; + + +static struct argp_option options[] = { + { name: "copyright", key: 'c', arg: NULL, flags: 0, + doc: "Print copyright information.", + group: 1 }, + + { name: "side", key: 's', arg: "", flags: 0, + doc: "Use the side as source.", + group: 1 }, + + { name: NULL, key: 0, arg: NULL, flags: 0, doc: NULL, group: 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 error_t +parse_opt(int key, char *arg, struct argp_state *state) +{ + int err = 0; + + myargs *args = state->input; + + switch (key) { + case 'c': + err_msg("%s\n", copyright); + ABORT_ARGP; + break; + case 's': + args->side = get_update_side(arg); + if (args->side < 0) { + err_msg("Unsupported seqnum: %s.\n" + "Supported seqnums are '0' and '1'\n", arg); + ERR_ARGP; + } + break; + case ARGP_KEY_ARG: + /* only two entries allowed */ + if (args->vol_no >= VOL_ARGS_MAX) { + err_msg("\n"); + argp_usage(state); + ERR_ARGP; + } + args->vol[(args->vol_no)++] = arg; + break; + case ARGP_KEY_END: + if (err) { + err_msg("\n"); + argp_usage(state); + ERR_ARGP; + } + break; + default: + return(ARGP_ERR_UNKNOWN); + } + + return 0; +} + +static struct argp argp = { + options: options, + parser: parse_opt, + args_doc: " ", + doc: doc, + children: NULL, + help_filter: NULL, + argp_domain: NULL, +}; + + +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, + }; + + argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &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 + +/** + * @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/ubimirror/ubimirror.c b/ubi-utils/src/ubimirror/ubimirror.c deleted file mode 100644 index e43ba10..0000000 --- a/ubi-utils/src/ubimirror/ubimirror.c +++ /dev/null @@ -1,206 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#include -#include -#include -#include - -#include "config.h" -#include "error.h" -#include "example_ubi.h" -#include "ubimirror.h" - -typedef enum action_t { - ACT_NORMAL = 0, - ACT_ARGP_ABORT, - ACT_ARGP_ERR, -} action_t; - -#define ABORT_ARGP do { \ - state->next = state->argc; \ - args->action = ACT_ARGP_ABORT; \ -} while (0) - -#define ERR_ARGP do { \ - state->next = state->argc; \ - args->action = ACT_ARGP_ERR; \ -} while (0) - -#define VOL_ARGS_MAX 2 - - -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" - "ubimirror - mirrors ubi volumes.\n"; - -static const char copyright [] __attribute__((unused)) = - "(C) IBM Coorporation 2007"; - - -static struct argp_option options[] = { - { name: "copyright", key: 'c', arg: NULL, flags: 0, - doc: "Print copyright information.", - group: 1 }, - - { name: "side", key: 's', arg: "", flags: 0, - doc: "Use the side as source.", - group: 1 }, - - { name: NULL, key: 0, arg: NULL, flags: 0, doc: NULL, group: 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 error_t -parse_opt(int key, char *arg, struct argp_state *state) -{ - int err = 0; - - myargs *args = state->input; - - switch (key) { - case 'c': - err_msg("%s\n", copyright); - ABORT_ARGP; - break; - case 's': - args->side = get_update_side(arg); - if (args->side < 0) { - err_msg("Unsupported seqnum: %s.\n" - "Supported seqnums are '0' and '1'\n", arg); - ERR_ARGP; - } - break; - case ARGP_KEY_ARG: - /* only two entries allowed */ - if (args->vol_no >= VOL_ARGS_MAX) { - err_msg("\n"); - argp_usage(state); - ERR_ARGP; - } - args->vol[(args->vol_no)++] = arg; - break; - case ARGP_KEY_END: - if (err) { - err_msg("\n"); - argp_usage(state); - ERR_ARGP; - } - break; - default: - return(ARGP_ERR_UNKNOWN); - } - - return 0; -} - -static struct argp argp = { - options: options, - parser: parse_opt, - args_doc: " ", - doc: doc, - children: NULL, - help_filter: NULL, - argp_domain: NULL, -}; - - -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, - }; - - argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &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/ubimkvol.c b/ubi-utils/src/ubimkvol.c new file mode 100644 index 0000000..30c569c --- /dev/null +++ b/ubi-utils/src/ubimkvol.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. + */ + +/* + * An utility to create UBI volumes. + * + * Author: Artem B. Bityutskiy + * + * 1.0 Initial release + * 1.1 Does not support erase blocks anymore. This is replaced by + * the number of bytes. + */ + +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include + +static void usage(void); +static int param_sanity_check(ubi_lib_t lib); +static int parse_options(int argc, char * const argv[]); + +/* + * The variables below are set by command line arguments. + */ +static int vol_type = UBI_DYNAMIC_VOLUME; +static int devn = -1; +static long long bytes = 0; +static int alignment = 1; +static int vol_id = UBI_VOL_NUM_AUTO; +static char *name = NULL; +static int nlen = 0; + +int main(int argc, char * const argv[]) +{ + int err; + ubi_lib_t lib; + + err = parse_options(argc, argv); + if (err) { + fprintf(stderr, "Wrong options ...\n"); + return err == 1 ? 0 : -1; + } + + if (devn == -1) { + fprintf(stderr, "Device number was not specified\n"); + fprintf(stderr, "Use -h option for help\n"); + return -1; + } + + err = ubi_open(&lib); + if (err) { + perror("Cannot open libubi"); + return -1; + } + + err = param_sanity_check(lib); + if (err) { + perror("Input parameters check"); + fprintf(stderr, "Use -h option for help\n"); + goto out_libubi; + } + + err = ubi_mkvol(lib, devn, vol_id, vol_type, bytes, alignment, name); + 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); */ + + vol_id = err; + ubi_close(&lib); + return 0; + +out_libubi: + ubi_close(&lib); + return -1; +} + +/* 'getopt()' option string */ +static const char *optstring = "ht:s:n:N:d:a:"; + +static int parse_options(int argc, char * const argv[]) +{ + int opt = 0; + + while (opt != -1) { + char *endp; + + opt = getopt(argc, argv, optstring); + + switch (opt) { + case 'h': + usage(); + return 1; + case 't': + if (!strcmp(optarg, "dynamic")) + vol_type = UBI_DYNAMIC_VOLUME; + else if (!strcmp(optarg, "static")) + vol_type = UBI_STATIC_VOLUME; + else { + fprintf(stderr, "Bad volume type: \"%s\"\n", + optarg); + goto out; + } + break; + case 's': + bytes = strtoull(optarg, &endp, 0); + if (endp == optarg || bytes < 0) { + fprintf(stderr, "Bad volume size: \"%s\"\n", + optarg); + goto out; + } + if (endp != '\0') { + if (strcmp(endp, "KiB") == 0) + bytes *= 1024; + else if (strcmp(endp, "MiB") == 0) + bytes *= 1024*1024; + } + break; + case 'a': + alignment = strtoul(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || + alignment <= 0) { + fprintf(stderr, "Bad volume alignment: " + "\"%s\"\n", optarg); + goto out; + } + break; + case 'd': + devn = strtoul(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || devn < 0) { + fprintf(stderr, "Bad UBI device number: " + "\"%s\"\n", optarg); + goto out; + } + break; + case 'n': + vol_id = strtoul(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || + (vol_id < 0 && vol_id != UBI_DYNAMIC_VOLUME)) { + fprintf(stderr, "Bad volume ID: " + "\"%s\"\n", optarg); + goto out; + } + break; + case 'N': + name = optarg; + nlen = strlen(name); + break; + + case ':': + fprintf(stderr, "Parameter is missing\n"); + goto out; + case '?': + fprintf(stderr, "Unknown parameter\n"); + goto out; + case -1: + break; + default: + fprintf(stderr, "Internal error\n"); + goto out; + } + } + + return 0; + + out: + errno = EINVAL; + return -1; +} + +static int param_sanity_check(ubi_lib_t lib) +{ + int err, len; + struct ubi_info ubi; + + if (bytes == 0) { + fprintf(stderr, "Volume size was not specified\n"); + goto out; + } + + if (name == NULL) { + fprintf(stderr, "Volume name was not specified\n"); + goto out; + } + + err = ubi_get_info(lib, &ubi); + if (err) + return -1; + + if (devn >= (int)ubi.dev_count) { + fprintf(stderr, "Device %d does not exist\n", devn); + goto out; + } + + len = strlen(name); + if (len > (int)ubi.nlen_max) { + fprintf(stderr, "Too long name (%d symbols), max is %d\n", + len, ubi.nlen_max); + goto out; + } + + return 0; + +out: + errno = EINVAL; + return -1; +} + +static void usage(void) +{ + printf("Usage: ubi_mkvol OPTIONS\n" + "Version: " PACKAGE_VERSION "\n" + "The command line options:\n" + "\t-h - this help message\n" + "\t-d - UBI device number\n" + "\t-t TYPE - volume type (dynamic, static) " + "(default is dynamic)\n" + "\t-n VOLID - volume ID to assign to the new volume. If not" + "specified, \n" + "\t the volume ID will be assigned automatically\n" + "\t-s BYTES - volume size in bytes, " + "kilobytes (KiB) or megabytes (MiB)\n" + "\t-N NAME - volume name\n" + "\t-a ALIGNMENT - volume alignment (default is 1)\n"); +} diff --git a/ubi-utils/src/ubimkvol/ubimkvol.c b/ubi-utils/src/ubimkvol/ubimkvol.c deleted file mode 100644 index f929252..0000000 --- a/ubi-utils/src/ubimkvol/ubimkvol.c +++ /dev/null @@ -1,252 +0,0 @@ -/* - * 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 - * - * 1.0 Initial release - * 1.1 Does not support erase blocks anymore. This is replaced by - * the number of bytes. - */ - -#include -#include -#include -#include -#include -#include - -#include - -#define VERSION "1.1" - -static void usage(void); -static int param_sanity_check(ubi_lib_t lib); -static int parse_options(int argc, char * const argv[]); - -/* - * The variables below are set by command line arguments. - */ -static int vol_type = UBI_DYNAMIC_VOLUME; -static int devn = -1; -static long long bytes = 0; -static int alignment = 1; -static int vol_id = UBI_VOL_NUM_AUTO; -static char *name = NULL; -static int nlen = 0; - -int main(int argc, char * const argv[]) -{ - int err; - ubi_lib_t lib; - - err = parse_options(argc, argv); - if (err) { - fprintf(stderr, "Wrong options ...\n"); - return err == 1 ? 0 : -1; - } - - if (devn == -1) { - fprintf(stderr, "Device number was not specified\n"); - fprintf(stderr, "Use -h option for help\n"); - return -1; - } - - err = ubi_open(&lib); - if (err) { - perror("Cannot open libubi"); - return -1; - } - - err = param_sanity_check(lib); - if (err) { - perror("Input parameters check"); - fprintf(stderr, "Use -h option for help\n"); - goto out_libubi; - } - - err = ubi_mkvol(lib, devn, vol_id, vol_type, bytes, alignment, name); - 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); */ - - vol_id = err; - ubi_close(&lib); - return 0; - -out_libubi: - ubi_close(&lib); - return -1; -} - -/* 'getopt()' option string */ -static const char *optstring = "ht:s:n:N:d:a:"; - -static int parse_options(int argc, char * const argv[]) -{ - int opt = 0; - - while (opt != -1) { - char *endp; - - opt = getopt(argc, argv, optstring); - - switch (opt) { - case 'h': - usage(); - return 1; - case 't': - if (!strcmp(optarg, "dynamic")) - vol_type = UBI_DYNAMIC_VOLUME; - else if (!strcmp(optarg, "static")) - vol_type = UBI_STATIC_VOLUME; - else { - fprintf(stderr, "Bad volume type: \"%s\"\n", - optarg); - goto out; - } - break; - case 's': - bytes = strtoull(optarg, &endp, 0); - if (endp == optarg || bytes < 0) { - fprintf(stderr, "Bad volume size: \"%s\"\n", - optarg); - goto out; - } - if (endp != '\0') { - if (strcmp(endp, "KiB") == 0) - bytes *= 1024; - else if (strcmp(endp, "MiB") == 0) - bytes *= 1024*1024; - } - break; - case 'a': - alignment = strtoul(optarg, &endp, 0); - if (*endp != '\0' || endp == optarg || - alignment <= 0) { - fprintf(stderr, "Bad volume alignment: " - "\"%s\"\n", optarg); - goto out; - } - break; - case 'd': - devn = strtoul(optarg, &endp, 0); - if (*endp != '\0' || endp == optarg || devn < 0) { - fprintf(stderr, "Bad UBI device number: " - "\"%s\"\n", optarg); - goto out; - } - break; - case 'n': - vol_id = strtoul(optarg, &endp, 0); - if (*endp != '\0' || endp == optarg || - (vol_id < 0 && vol_id != UBI_DYNAMIC_VOLUME)) { - fprintf(stderr, "Bad volume ID: " - "\"%s\"\n", optarg); - goto out; - } - break; - case 'N': - name = optarg; - nlen = strlen(name); - break; - - case ':': - fprintf(stderr, "Parameter is missing\n"); - goto out; - case '?': - fprintf(stderr, "Unknown parameter\n"); - goto out; - case -1: - break; - default: - fprintf(stderr, "Internal error\n"); - goto out; - } - } - - return 0; - - out: - errno = EINVAL; - return -1; -} - -static int param_sanity_check(ubi_lib_t lib) -{ - int err, len; - struct ubi_info ubi; - - if (bytes == 0) { - fprintf(stderr, "Volume size was not specified\n"); - goto out; - } - - if (name == NULL) { - fprintf(stderr, "Volume name was not specified\n"); - goto out; - } - - err = ubi_get_info(lib, &ubi); - if (err) - return -1; - - if (devn >= ubi.dev_count) { - fprintf(stderr, "Device %d does not exist\n", devn); - goto out; - } - - len = strlen(name); - if (len > ubi.nlen_max) { - fprintf(stderr, "Too long name (%d symbols), max is %d\n", - len, ubi.nlen_max); - goto out; - } - - return 0; - -out: - errno = EINVAL; - return -1; -} - -static void usage(void) -{ - printf("Usage: ubi_mkvol OPTIONS\n" - "Version: " VERSION "\n" - "The command line options:\n" - "\t-h - this help message\n" - "\t-d - UBI device number\n" - "\t-t TYPE - volume type (dynamic, static) " - "(default is dynamic)\n" - "\t-n VOLID - volume ID to assign to the new volume. If not" - "specified, \n" - "\t the volume ID will be assigned automatically\n" - "\t-s BYTES - volume size in bytes, " - "kilobytes (KiB) or megabytes (MiB)\n" - "\t-N NAME - volume name\n" - "\t-a ALIGNMENT - volume alignment (default is 1)\n"); -} diff --git a/ubi-utils/src/ubirmvol.c b/ubi-utils/src/ubirmvol.c new file mode 100644 index 0000000..cb47b6c --- /dev/null +++ b/ubi-utils/src/ubirmvol.c @@ -0,0 +1,174 @@ +/* + * 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. + * + * Autor: Artem B. Bityutskiy + */ + +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include + +static void usage(void); +static int param_sanity_check(ubi_lib_t lib); +static int parse_options(int argc, char * const argv[]); + +/* + * The below variables are set by command line options. + */ +static int vol_id = -1; +static int devn = -1; + +int main(int argc, char * const argv[]) +{ + int err, old_errno; + ubi_lib_t lib; + + err = parse_options(argc, argv); + if (err) + return err == 1 ? 0 : -1; + + if (devn == -1) { + fprintf(stderr, "Device number was not specified\n"); + fprintf(stderr, "Use -h option for help\n"); + return -1; + } + + err = ubi_open(&lib); + if (err) { + perror("Cannot open libubi"); + return -1; + } + + err = param_sanity_check(lib); + if (err) { + perror("Input parameters check"); + fprintf(stderr, "Use -h option for help\n"); + goto out_libubi; + } + + err = ubi_rmvol(lib, devn, 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; + } + + return 0; + +out_libubi: + ubi_close(&lib); + return -1; +} + +/* 'getopt()' option string */ +static const char *optstring = "hd:n:"; + +static int parse_options(int argc, char * const argv[]) +{ + int opt = 0; + + while (opt != -1) { + char *endp; + + opt = getopt(argc, argv, optstring); + + switch (opt) { + case 'h': + usage(); + return 1; + case 'n': + vol_id = strtoul(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || vol_id < 0) { + fprintf(stderr, "Bad volume " + "number: \"%s\"\n", optarg); + goto out; + } + break; + case 'd': + devn = strtoul(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || devn < 0) { + fprintf(stderr, "Bad UBI device " + "number: \"%s\"\n", optarg); + goto out; + } + break; + case ':': + fprintf(stderr, "Parameter is missing\n"); + goto out; + case '?': + fprintf(stderr, "Unknown parameter\n"); + goto out; + case -1: + break; + default: + fprintf(stderr, "Internal error\n"); + goto out; + } + } + + return 0; + +out: + errno = EINVAL; + return -1; +} + +static int param_sanity_check(ubi_lib_t lib) +{ + int err; + struct ubi_info ubi; + + if (vol_id == -1) { + fprintf(stderr, "Volume ID was not specified\n"); + goto out; + } + + err = ubi_get_info(lib, &ubi); + if (err) + return -1; + + if (devn >= (int)ubi.dev_count) { + fprintf(stderr, "Device %d does not exist\n", devn); + goto out; + } + + return 0; + +out: + errno = EINVAL; + return -1; +} + +static void usage(void) +{ + printf("Usage: ubi_rmvol OPTIONS\n" + "Command line options:\n" + "\t-h - this help message\n" + "\t-d - UBI device number\n" + "\t-n VOLNUM - volume number to remove\n"); +} diff --git a/ubi-utils/src/ubirmvol/ubirmvol.c b/ubi-utils/src/ubirmvol/ubirmvol.c deleted file mode 100644 index fc5ada5..0000000 --- a/ubi-utils/src/ubirmvol/ubirmvol.c +++ /dev/null @@ -1,172 +0,0 @@ -/* - * 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. - * - * Autor: Artem B. Bityutskiy - */ - -#include -#include -#include -#include -#include -#include -#include - -static void usage(void); -static int param_sanity_check(ubi_lib_t lib); -static int parse_options(int argc, char * const argv[]); - -/* - * The below variables are set by command line options. - */ -static int vol_id = -1; -static int devn = -1; - -int main(int argc, char * const argv[]) -{ - int err, old_errno; - ubi_lib_t lib; - - err = parse_options(argc, argv); - if (err) - return err == 1 ? 0 : -1; - - if (devn == -1) { - fprintf(stderr, "Device number was not specified\n"); - fprintf(stderr, "Use -h option for help\n"); - return -1; - } - - err = ubi_open(&lib); - if (err) { - perror("Cannot open libubi"); - return -1; - } - - err = param_sanity_check(lib); - if (err) { - perror("Input parameters check"); - fprintf(stderr, "Use -h option for help\n"); - goto out_libubi; - } - - err = ubi_rmvol(lib, devn, 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; - } - - return 0; - -out_libubi: - ubi_close(&lib); - return -1; -} - -/* 'getopt()' option string */ -static const char *optstring = "hd:n:"; - -static int parse_options(int argc, char * const argv[]) -{ - int opt = 0; - - while (opt != -1) { - char *endp; - - opt = getopt(argc, argv, optstring); - - switch (opt) { - case 'h': - usage(); - return 1; - case 'n': - vol_id = strtoul(optarg, &endp, 0); - if (*endp != '\0' || endp == optarg || vol_id < 0) { - fprintf(stderr, "Bad volume " - "number: \"%s\"\n", optarg); - goto out; - } - break; - case 'd': - devn = strtoul(optarg, &endp, 0); - if (*endp != '\0' || endp == optarg || devn < 0) { - fprintf(stderr, "Bad UBI device " - "number: \"%s\"\n", optarg); - goto out; - } - break; - case ':': - fprintf(stderr, "Parameter is missing\n"); - goto out; - case '?': - fprintf(stderr, "Unknown parameter\n"); - goto out; - case -1: - break; - default: - fprintf(stderr, "Internal error\n"); - goto out; - } - } - - return 0; - -out: - errno = EINVAL; - return -1; -} - -static int param_sanity_check(ubi_lib_t lib) -{ - int err; - struct ubi_info ubi; - - if (vol_id == -1) { - fprintf(stderr, "Volume ID was not specified\n"); - goto out; - } - - err = ubi_get_info(lib, &ubi); - if (err) - return -1; - - if (devn >= ubi.dev_count) { - fprintf(stderr, "Device %d does not exist\n", devn); - goto out; - } - - return 0; - -out: - errno = EINVAL; - return -1; -} - -static void usage(void) -{ - printf("Usage: ubi_rmvol OPTIONS\n" - "Command line options:\n" - "\t-h - this help message\n" - "\t-d - UBI device number\n" - "\t-n VOLNUM - volume number to remove\n"); -} diff --git a/ubi-utils/src/ubiupdatevol.c b/ubi-utils/src/ubiupdatevol.c new file mode 100644 index 0000000..28e1e8f --- /dev/null +++ b/ubi-utils/src/ubiupdatevol.c @@ -0,0 +1,352 @@ +/* + * 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define MAXPATH 1024 +#define BUFSIZE 128 * 1024 +#define MIN(x,y) ((x)<(y)?(x):(y)) + +struct args { + int device; + int volume; + int truncate; + int broken_update; + int bufsize; + + /* special stuff needed to get additional arguments */ + char *arg1; + char **options; /* [STRING...] */ +}; + +static struct args myargs = { + .device = -1, + .volume = -1, + .truncate = 0, + .broken_update = 0, + .bufsize = BUFSIZE, + .arg1 = NULL, + .options = NULL, +}; + +static int verbose = 0; + +static error_t parse_opt (int key, char *arg, struct argp_state *state); + +const char *argp_program_bug_address = PACKAGE_BUGREPORT; + +static char doc[] = "\nVersion: " PACKAGE_VERSION "\n\t" + BUILD_OS" "BUILD_CPU" at "__DATE__" "__TIME__"\n" + "\nWrite to UBI Volume.\n"; + +static struct argp_option options[] = { + { .name = "device", + .key = 'd', + .arg = "", + .flags = 0, + .doc = "UBI device", + .group = OPTION_ARG_OPTIONAL }, + + { .name = "volume", + .key = 'n', + .arg = "", + .flags = 0, + .doc = "UBI volume id", + .group = OPTION_ARG_OPTIONAL }, + + { .name = "truncate", + .key = 't', + .arg = NULL, + .flags = 0, + .doc = "truncate volume", + .group = OPTION_ARG_OPTIONAL }, + + { .name = "broken-update", + .key = 'B', + .arg = NULL, + .flags = 0, + .doc = "broken update, this is for testing", + .group = OPTION_ARG_OPTIONAL }, + + { .name = NULL, .key = 0, .arg = NULL, .flags = 0, + .doc = NULL, .group = 0 }, +}; + +static struct argp argp = { + .options = options, + .parser = parse_opt, + .args_doc = 0, + .doc = doc, + .children = NULL, + .help_filter = NULL, + .argp_domain = NULL, +}; + +/* + * @brief Parse the arguments passed into the test case. + * + * @param key The parameter. + * @param arg Argument passed to parameter. + * @param state Location to put information on parameters. + * + * @return error + * + * Get the `input' argument from `argp_parse', which we know is a + * pointer to our arguments structure. + */ +static error_t +parse_opt(int key, char *arg, struct argp_state *state) +{ + struct args *args = state->input; + + switch (key) { + case 'v': /* --verbose= */ + verbose = strtoul(arg, (char **)NULL, 0); + break; + + case 'd': /* --device= */ + args->device = strtol(arg, (char **)NULL, 0); + break; + + case 'b': /* --bufsize= */ + args->bufsize = strtol(arg, (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 'n': /* --volume= */ + args->volume = strtol(arg, (char **)NULL, 0); + break; + + case ARGP_KEY_NO_ARGS: + /* argp_usage(state); */ + break; + + case ARGP_KEY_ARG: + args->arg1 = arg; + /* Now we consume all the rest of the arguments. + `state->next' is the index in `state->argv' of the + next argument to be parsed, which is the first STRING + we're interested in, so we can just use + `&state->argv[state->next]' as the value for + arguments->strings. + + _In addition_, by setting `state->next' to the end + of the arguments, we can force argp to stop parsing here and + return. */ + + args->options = &state->argv[state->next]; + state->next = state->argc; + break; + + case ARGP_KEY_END: + /* argp_usage(state); */ + break; + + default: + return(ARGP_ERR_UNKNOWN); + } + + 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) +{ + int rc, ofd; + char path[MAXPATH]; + int old_errno; + + snprintf(path, MAXPATH-1, "/dev/ubi%d_%d", args->device, args->volume); + path[MAXPATH-1] = '\0'; + + ofd = open(path, O_RDWR); + if (ofd < 0) { + fprintf(stderr, "Cannot open volume %s\n", path); + exit(EXIT_FAILURE); + } + rc = ioctl(ofd, UBI_IOCVOLUP, &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) +{ + 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 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->device, args->volume); + 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 = ioctl(ofd, UBI_IOCVOLUP, &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; + + argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &myargs); + + if (myargs.truncate) { + rc = ubi_truncate_volume(&myargs, 0LL); + if (rc < 0) + exit(EXIT_FAILURE); + exit(EXIT_SUCCESS); + } + if (myargs.broken_update) { + rc = ubi_truncate_volume(&myargs, 1LL); + if (rc < 0) + exit(EXIT_FAILURE); + exit(EXIT_SUCCESS); + } + rc = ubi_update_volume(&myargs); + if (rc < 0) + exit(EXIT_FAILURE); + + exit(EXIT_SUCCESS); +} diff --git a/ubi-utils/src/ubiwritevol/ubiwritevol.c b/ubi-utils/src/ubiwritevol/ubiwritevol.c deleted file mode 100644 index 8fdbe37..0000000 --- a/ubi-utils/src/ubiwritevol/ubiwritevol.c +++ /dev/null @@ -1,352 +0,0 @@ -/* - * 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 - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#define MAXPATH 1024 -#define BUFSIZE 128 * 1024 -#define MIN(x,y) ((x)<(y)?(x):(y)) - -struct args { - int device; - int volume; - int truncate; - int broken_update; - int bufsize; - - /* special stuff needed to get additional arguments */ - char *arg1; - char **options; /* [STRING...] */ -}; - -static struct args myargs = { - .device = -1, - .volume = -1, - .truncate = 0, - .broken_update = 0, - .bufsize = BUFSIZE, - .arg1 = NULL, - .options = NULL, -}; - -static int verbose = 0; - -static error_t parse_opt (int key, char *arg, struct argp_state *state); - -const char *argp_program_bug_address = ""; - -static char doc[] = "\nVersion: " VERSION "\n\t" - HOST_OS" "HOST_CPU" at "__DATE__" "__TIME__"\n" - "\nWrite to UBI Volume.\n"; - -static struct argp_option options[] = { - { .name = "device", - .key = 'd', - .arg = "", - .flags = 0, - .doc = "UBI device", - .group = OPTION_ARG_OPTIONAL }, - - { .name = "volume", - .key = 'n', - .arg = "", - .flags = 0, - .doc = "UBI volume id", - .group = OPTION_ARG_OPTIONAL }, - - { .name = "truncate", - .key = 't', - .arg = NULL, - .flags = 0, - .doc = "truncate volume", - .group = OPTION_ARG_OPTIONAL }, - - { .name = "broken-update", - .key = 'B', - .arg = NULL, - .flags = 0, - .doc = "broken update, this is for testing", - .group = OPTION_ARG_OPTIONAL }, - - { .name = NULL, .key = 0, .arg = NULL, .flags = 0, - .doc = NULL, .group = 0 }, -}; - -static struct argp argp = { - .options = options, - .parser = parse_opt, - .args_doc = 0, - .doc = doc, - .children = NULL, - .help_filter = NULL, - .argp_domain = NULL, -}; - -/* - * @brief Parse the arguments passed into the test case. - * - * @param key The parameter. - * @param arg Argument passed to parameter. - * @param state Location to put information on parameters. - * - * @return error - * - * Get the `input' argument from `argp_parse', which we know is a - * pointer to our arguments structure. - */ -static error_t -parse_opt(int key, char *arg, struct argp_state *state) -{ - struct args *args = state->input; - - switch (key) { - case 'v': /* --verbose= */ - verbose = strtoul(arg, (char **)NULL, 0); - break; - - case 'd': /* --device= */ - args->device = strtol(arg, (char **)NULL, 0); - break; - - case 'b': /* --bufsize= */ - args->bufsize = strtol(arg, (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 'n': /* --volume= */ - args->volume = strtol(arg, (char **)NULL, 0); - break; - - case ARGP_KEY_NO_ARGS: - /* argp_usage(state); */ - break; - - case ARGP_KEY_ARG: - args->arg1 = arg; - /* Now we consume all the rest of the arguments. - `state->next' is the index in `state->argv' of the - next argument to be parsed, which is the first STRING - we're interested in, so we can just use - `&state->argv[state->next]' as the value for - arguments->strings. - - _In addition_, by setting `state->next' to the end - of the arguments, we can force argp to stop parsing here and - return. */ - - args->options = &state->argv[state->next]; - state->next = state->argc; - break; - - case ARGP_KEY_END: - /* argp_usage(state); */ - break; - - default: - return(ARGP_ERR_UNKNOWN); - } - - 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) -{ - int rc, ofd; - char path[MAXPATH]; - int old_errno; - - snprintf(path, MAXPATH-1, "/dev/ubi%d_%d", args->device, args->volume); - path[MAXPATH-1] = '\0'; - - ofd = open(path, O_RDWR); - if (ofd < 0) { - fprintf(stderr, "Cannot open volume %s\n", path); - exit(EXIT_FAILURE); - } - rc = ioctl(ofd, UBI_IOCVOLUP, &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) -{ - 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 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->device, args->volume); - 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 = ioctl(ofd, UBI_IOCVOLUP, &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; - - argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &myargs); - - if (myargs.truncate) { - rc = ubi_truncate_volume(&myargs, 0LL); - if (rc < 0) - exit(EXIT_FAILURE); - exit(EXIT_SUCCESS); - } - if (myargs.broken_update) { - rc = ubi_truncate_volume(&myargs, 1LL); - if (rc < 0) - exit(EXIT_FAILURE); - exit(EXIT_SUCCESS); - } - rc = ubi_update_volume(&myargs); - if (rc < 0) - exit(EXIT_FAILURE); - - exit(EXIT_SUCCESS); -} diff --git a/ubi-utils/src/unubi.c b/ubi-utils/src/unubi.c new file mode 100644 index 0000000..6d877e7 --- /dev/null +++ b/ubi-utils/src/unubi.c @@ -0,0 +1,392 @@ +/* + * 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 UBI images. Not yet finished ... + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "crc32.h" +#include + +#define MAXPATH 1024 +#define MIN(x,y) ((x)<(y)?(x):(y)) + +static uint32_t crc32_table[256]; + +struct args { + const char *output_dir; + uint32_t hdr_offs; + uint32_t data_offs; + uint32_t blocksize; + + /* special stuff needed to get additional arguments */ + char *arg1; + char **options; /* [STRING...] */ +}; + +static struct args myargs = { + .output_dir = "unubi", + .hdr_offs = 64, + .data_offs = 128, + .blocksize = 128 * 1024, + .arg1 = NULL, + .options = NULL, +}; + +static error_t parse_opt (int key, char *arg, struct argp_state *state); + +const char *argp_program_bug_address = + "... uuuh, lets wait until it looks nicer"; + +static char doc[] = "\nVersion: " PACKAGE_VERSION "\n\t" + BUILD_OS" "BUILD_CPU" at "__DATE__" "__TIME__"\n" + "\nWrite to UBI Volume.\n"; + +static struct argp_option options[] = { + { .name = "dir", + .key = 'd', + .arg = "", + .flags = 0, + .doc = "output directory", + .group = OPTION_ARG_OPTIONAL }, + + { .name = "blocksize", + .key = 'b', + .arg = "", + .flags = 0, + .doc = "blocksize", + .group = OPTION_ARG_OPTIONAL }, + + { .name = "data-offs", + .key = 'x', + .arg = "", + .flags = 0, + .doc = "data offset", + .group = OPTION_ARG_OPTIONAL }, + + { .name = "hdr-offs", + .key = 'x', + .arg = "", + .flags = 0, + .doc = "hdr offset", + .group = OPTION_ARG_OPTIONAL }, + + { .name = NULL, .key = 0, .arg = NULL, .flags = 0, + .doc = NULL, .group = 0 }, +}; + +static struct argp argp = { + .options = options, + .parser = parse_opt, + .args_doc = "image-file", + .doc = doc, + .children = NULL, + .help_filter = NULL, + .argp_domain = NULL, +}; + +/* + * 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 key The parameter. + * @param arg Argument passed to parameter. + * @param state Location to put information on parameters. + * + * @return error + * + * Get the `input' argument from `argp_parse', which we know is a + * pointer to our arguments structure. + */ +static error_t +parse_opt(int key, char *arg, struct argp_state *state) +{ + struct args *args = state->input; + + switch (key) { + case 'b': /* --blocksize */ + args->blocksize = str_to_num(arg); + break; + + case 'x': /* --data-offs= */ + args->data_offs = str_to_num(arg); + break; + + case 'y': /* --hdr-offs= */ + args->hdr_offs = str_to_num(arg); + break; + + case 'd': /* --dir= */ + args->output_dir = arg; + break; + + case ARGP_KEY_NO_ARGS: + /* argp_usage(state); */ + break; + + case ARGP_KEY_ARG: + args->arg1 = arg; + /* Now we consume all the rest of the arguments. + `state->next' is the index in `state->argv' of the + next argument to be parsed, which is the first STRING + we're interested in, so we can just use + `&state->argv[state->next]' as the value for + arguments->strings. + + _In addition_, by setting `state->next' to the end + of the arguments, we can force argp to stop parsing + here and return. */ + + args->options = &state->argv[state->next]; + state->next = state->argc; + break; + + case ARGP_KEY_END: + /* argp_usage(state); */ + break; + + default: + return(ARGP_ERR_UNKNOWN); + } + + return 0; +} + +static inline void +hexdump(FILE *fp, const void *p, ssize_t size) +{ + int k; + const uint8_t *buf = p; + + for (k = 0; k < size; k++) { + fprintf(fp, "%02x ", buf[k]); + if ((k & 15) == 15) + fprintf(fp, "\n"); + } +} + +/* + * This was put together in 1.5 hours and this is exactly how it looks + * like! FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME + * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME + * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME + * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME + * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME + * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME + * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME + * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME + * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME + * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME + * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME + * FIXME FIXME FIXME FIXME FIXME FIXME! + */ +static int extract_volume(struct args *args, const uint8_t *buf, + int len, int volume, FILE *fp) +{ + int i, rc; + int nrblocks = len / args->blocksize; + int max_lnum = -1, lnum = 0; + const uint8_t *ptr; + uint8_t **vol_tab; + int *vol_len; + + vol_tab = calloc(nrblocks, sizeof(uint8_t *)); + vol_len = calloc(nrblocks, sizeof(int)); + + if (!buf || !vol_tab || !vol_len) + exit(EXIT_FAILURE); + + for (i = 0, ptr = buf; i < nrblocks; i++, ptr += args->blocksize) { + uint32_t crc; + struct ubi_ec_hdr *ec_hdr = (struct ubi_ec_hdr *)ptr; + struct ubi_vid_hdr *vid_hdr = NULL; + uint8_t *data; + + /* default */ + vol_len[lnum] = args->blocksize - (2 * 1024); + + /* Check UBI EC header */ + crc = clc_crc32(crc32_table, UBI_CRC32_INIT, ec_hdr, + UBI_EC_HDR_SIZE_CRC); + if (crc != ubi32_to_cpu(ec_hdr->hdr_crc)) + continue; + + vid_hdr = (struct ubi_vid_hdr *) + (ptr + ubi32_to_cpu(ec_hdr->vid_hdr_offset)); + data = (uint8_t *)(ptr + ubi32_to_cpu(ec_hdr->data_offset)); + + crc = clc_crc32(crc32_table, UBI_CRC32_INIT, vid_hdr, + UBI_VID_HDR_SIZE_CRC); + if (crc != ubi32_to_cpu(vid_hdr->hdr_crc)) + continue; + + if (volume == (int)ubi32_to_cpu(vid_hdr->vol_id)) { + + printf("****** block %4d volume %2d **********\n", + i, volume); + + hexdump(stdout, ptr, 64); + + printf("--- vid_hdr\n"); + hexdump(stdout, vid_hdr, 64); + + printf("--- data\n"); + hexdump(stdout, data, 64); + + lnum = ubi32_to_cpu(vid_hdr->lnum); + vol_tab[lnum] = data; + if (max_lnum < lnum) + max_lnum = lnum; + if (vid_hdr->vol_type == UBI_VID_STATIC) + vol_len[lnum] = + ubi32_to_cpu(vid_hdr->data_size); + + } + } + + for (lnum = 0; lnum <= max_lnum; lnum++) { + if (vol_tab[lnum]) { + rc = fwrite(vol_tab[lnum], 1, vol_len[lnum], fp); + if (ferror(fp) || (vol_len[lnum] != rc)) { + perror("could not write file"); + exit(EXIT_FAILURE); + } + } else { + /* Fill up empty areas by 0xff, for static + * volumes this means they are broken! + */ + for (i = 0; i < vol_len[lnum]; i++) { + if (fputc(0xff, fp) == EOF) { + perror("could not write char"); + exit(EXIT_FAILURE); + } + } + } + } + + free(vol_tab); + free(vol_len); + return 0; +} + +int +main(int argc, char *argv[]) +{ + int len, rc; + FILE *fp; + struct stat file_info; + uint8_t *buf; + int i; + + init_crc32_table(crc32_table); + + argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &myargs); + + if (!myargs.arg1) { + fprintf(stderr, "Please specify input file!\n"); + exit(EXIT_FAILURE); + } + + fp = fopen(myargs.arg1, "r"); + if (!fp) { + perror("Cannot open file"); + exit(EXIT_FAILURE); + } + if (fstat(fileno(fp), &file_info) != 0) { + fprintf(stderr, "Cannot fetch file size " + "from input file.\n"); + } + len = file_info.st_size; + buf = malloc(len); + if (!buf) { + perror("out of memory!"); + exit(EXIT_FAILURE); + } + rc = fread(buf, 1, len, fp); + if (ferror(fp) || (len != rc)) { + perror("could not read file"); + exit(EXIT_FAILURE); + } + if (!myargs.output_dir) { + fprintf(stderr, "No output directory specified!\n"); + exit(EXIT_FAILURE); + } + + rc = mkdir(myargs.output_dir, 0777); + if (rc && errno != EEXIST) { + perror("Cannot create output directory"); + exit(EXIT_FAILURE); + } + for (i = 0; i < 32; i++) { + char fname[1024]; + FILE *fpout; + + printf("######### VOLUME %d ############################\n", + i); + + sprintf(fname, "%s/ubivol_%d.bin", myargs.output_dir, i); + fpout = fopen(fname, "w+"); + if (!fpout) { + perror("Cannot open file"); + exit(EXIT_FAILURE); + } + extract_volume(&myargs, buf, len, i, fpout); + fclose(fpout); + } + + fclose(fp); + free(buf); + exit(EXIT_SUCCESS); +} diff --git a/ubi-utils/src/unubi/unubi.c b/ubi-utils/src/unubi/unubi.c deleted file mode 100644 index 9cb1354..0000000 --- a/ubi-utils/src/unubi/unubi.c +++ /dev/null @@ -1,391 +0,0 @@ -/* - * 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 UBI images. Not yet finished ... - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "crc32.h" -#include - -#define MAXPATH 1024 -#define MIN(x,y) ((x)<(y)?(x):(y)) - -static uint32_t crc32_table[256]; - -struct args { - const char *output_dir; - uint32_t hdr_offs; - uint32_t data_offs; - uint32_t blocksize; - - /* special stuff needed to get additional arguments */ - char *arg1; - char **options; /* [STRING...] */ -}; - -static struct args myargs = { - .output_dir = "unubi", - .hdr_offs = 64, - .data_offs = 128, - .blocksize = 128 * 1024, - .arg1 = NULL, - .options = NULL, -}; - -static error_t parse_opt (int key, char *arg, struct argp_state *state); - -const char *argp_program_bug_address = ""; - -static char doc[] = "\nVersion: " VERSION "\n\t" - HOST_OS" "HOST_CPU" at "__DATE__" "__TIME__"\n" - "\nWrite to UBI Volume.\n"; - -static struct argp_option options[] = { - { .name = "dir", - .key = 'd', - .arg = "", - .flags = 0, - .doc = "output directory", - .group = OPTION_ARG_OPTIONAL }, - - { .name = "blocksize", - .key = 'b', - .arg = "", - .flags = 0, - .doc = "blocksize", - .group = OPTION_ARG_OPTIONAL }, - - { .name = "data-offs", - .key = 'x', - .arg = "", - .flags = 0, - .doc = "data offset", - .group = OPTION_ARG_OPTIONAL }, - - { .name = "hdr-offs", - .key = 'x', - .arg = "", - .flags = 0, - .doc = "hdr offset", - .group = OPTION_ARG_OPTIONAL }, - - { .name = NULL, .key = 0, .arg = NULL, .flags = 0, - .doc = NULL, .group = 0 }, -}; - -static struct argp argp = { - .options = options, - .parser = parse_opt, - .args_doc = "image-file", - .doc = doc, - .children = NULL, - .help_filter = NULL, - .argp_domain = NULL, -}; - -/* - * 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 key The parameter. - * @param arg Argument passed to parameter. - * @param state Location to put information on parameters. - * - * @return error - * - * Get the `input' argument from `argp_parse', which we know is a - * pointer to our arguments structure. - */ -static error_t -parse_opt(int key, char *arg, struct argp_state *state) -{ - struct args *args = state->input; - - switch (key) { - case 'b': /* --blocksize */ - args->blocksize = str_to_num(arg); - break; - - case 'x': /* --data-offs= */ - args->data_offs = str_to_num(arg); - break; - - case 'y': /* --hdr-offs= */ - args->hdr_offs = str_to_num(arg); - break; - - case 'd': /* --dir= */ - args->output_dir = arg; - break; - - case ARGP_KEY_NO_ARGS: - /* argp_usage(state); */ - break; - - case ARGP_KEY_ARG: - args->arg1 = arg; - /* Now we consume all the rest of the arguments. - `state->next' is the index in `state->argv' of the - next argument to be parsed, which is the first STRING - we're interested in, so we can just use - `&state->argv[state->next]' as the value for - arguments->strings. - - _In addition_, by setting `state->next' to the end - of the arguments, we can force argp to stop parsing - here and return. */ - - args->options = &state->argv[state->next]; - state->next = state->argc; - break; - - case ARGP_KEY_END: - /* argp_usage(state); */ - break; - - default: - return(ARGP_ERR_UNKNOWN); - } - - return 0; -} - -static inline void -hexdump(FILE *fp, const void *p, size_t size) -{ - int k; - const uint8_t *buf = p; - - for (k = 0; k < size; k++) { - fprintf(fp, "%02x ", buf[k]); - if ((k & 15) == 15) - fprintf(fp, "\n"); - } -} - -/* - * This was put together in 1.5 hours and this is exactly how it looks - * like! FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME - * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME - * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME - * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME - * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME - * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME - * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME - * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME - * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME - * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME - * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME - * FIXME FIXME FIXME FIXME FIXME FIXME! - */ -static int extract_volume(struct args *args, const uint8_t *buf, - int len, int volume, FILE *fp) -{ - int i, rc; - int nrblocks = len / args->blocksize; - int max_lnum = -1, lnum = 0; - const uint8_t *ptr; - uint8_t **vol_tab; - int *vol_len; - - vol_tab = calloc(nrblocks, sizeof(uint8_t *)); - vol_len = calloc(nrblocks, sizeof(int)); - - if (!buf || !vol_tab || !vol_len) - exit(EXIT_FAILURE); - - for (i = 0, ptr = buf; i < nrblocks; i++, ptr += args->blocksize) { - uint32_t crc; - struct ubi_ec_hdr *ec_hdr = (struct ubi_ec_hdr *)ptr; - struct ubi_vid_hdr *vid_hdr = NULL; - uint8_t *data; - - /* default */ - vol_len[lnum] = args->blocksize - (2 * 1024); - - /* Check UBI EC header */ - crc = clc_crc32(crc32_table, UBI_CRC32_INIT, ec_hdr, - UBI_EC_HDR_SIZE_CRC); - if (crc != ubi32_to_cpu(ec_hdr->hdr_crc)) - continue; - - vid_hdr = (struct ubi_vid_hdr *) - (ptr + ubi32_to_cpu(ec_hdr->vid_hdr_offset)); - data = (uint8_t *)(ptr + ubi32_to_cpu(ec_hdr->data_offset)); - - crc = clc_crc32(crc32_table, UBI_CRC32_INIT, vid_hdr, - UBI_VID_HDR_SIZE_CRC); - if (crc != ubi32_to_cpu(vid_hdr->hdr_crc)) - continue; - - if (volume == ubi32_to_cpu(vid_hdr->vol_id)) { - - printf("****** block %4d volume %2d **********\n", - i, volume); - - hexdump(stdout, ptr, 64); - - printf("--- vid_hdr\n"); - hexdump(stdout, vid_hdr, 64); - - printf("--- data\n"); - hexdump(stdout, data, 64); - - lnum = ubi32_to_cpu(vid_hdr->lnum); - vol_tab[lnum] = data; - if (max_lnum < lnum) - max_lnum = lnum; - if (vid_hdr->vol_type == UBI_VID_STATIC) - vol_len[lnum] = - ubi32_to_cpu(vid_hdr->data_size); - - } - } - - for (lnum = 0; lnum <= max_lnum; lnum++) { - if (vol_tab[lnum]) { - rc = fwrite(vol_tab[lnum], 1, vol_len[lnum], fp); - if (ferror(fp) || (vol_len[lnum] != rc)) { - perror("could not write file"); - exit(EXIT_FAILURE); - } - } else { - /* Fill up empty areas by 0xff, for static - * volumes this means they are broken! - */ - for (i = 0; i < vol_len[lnum]; i++) { - if (fputc(0xff, fp) == EOF) { - perror("could not write char"); - exit(EXIT_FAILURE); - } - } - } - } - - free(vol_tab); - free(vol_len); - return 0; -} - -int -main(int argc, char *argv[]) -{ - int len, rc; - FILE *fp; - struct stat file_info; - uint8_t *buf; - int i; - - init_crc32_table(crc32_table); - - argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &myargs); - - if (!myargs.arg1) { - fprintf(stderr, "Please specify input file!\n"); - exit(EXIT_FAILURE); - } - - fp = fopen(myargs.arg1, "r"); - if (!fp) { - perror("Cannot open file"); - exit(EXIT_FAILURE); - } - if (fstat(fileno(fp), &file_info) != 0) { - fprintf(stderr, "Cannot fetch file size " - "from input file.\n"); - } - len = file_info.st_size; - buf = malloc(len); - if (!buf) { - perror("out of memory!"); - exit(EXIT_FAILURE); - } - rc = fread(buf, 1, len, fp); - if (ferror(fp) || (len != rc)) { - perror("could not read file"); - exit(EXIT_FAILURE); - } - if (!myargs.output_dir) { - fprintf(stderr, "No output directory specified!\n"); - exit(EXIT_FAILURE); - } - - rc = mkdir(myargs.output_dir, 0777); - if (rc && errno != EEXIST) { - perror("Cannot create output directory"); - exit(EXIT_FAILURE); - } - for (i = 0; i < 32; i++) { - char fname[1024]; - FILE *fpout; - - printf("######### VOLUME %d ############################\n", - i); - - sprintf(fname, "%s/ubivol_%d.bin", myargs.output_dir, i); - fpout = fopen(fname, "w+"); - if (!fpout) { - perror("Cannot open file"); - exit(EXIT_FAILURE); - } - extract_volume(&myargs, buf, len, i, fpout); - fclose(fpout); - } - - fclose(fp); - free(buf); - exit(EXIT_SUCCESS); -} -- cgit v1.2.3 From be43152d316c90a0c04aae1ed033ceaa69edbc72 Mon Sep 17 00:00:00 2001 From: Frank Haverkamp Date: Wed, 21 Jun 2006 15:07:46 +0200 Subject: [MTD] UBI: fixed bug in bin2nand introduced by the previous change. Signed-off-by: Frank Haverkamp --- ubi-utils/Makefile | 6 +++--- ubi-utils/src/bin2nand.c | 10 ++++++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/ubi-utils/Makefile b/ubi-utils/Makefile index 307da31..6161f3d 100644 --- a/ubi-utils/Makefile +++ b/ubi-utils/Makefile @@ -7,9 +7,9 @@ HOST_VERSION_NAME := $(shell uname -r) BUILD_CPU := $(shell uname -m) BUILD_OS := $(shell uname -o) -KERNELHDR := ../include # mtd-utils private headers -DESTDIR := /usr/local # default installation -SBINDIR := bin # default directory for binaries +KERNELHDR := ../include +DESTDIR := /usr/local +SBINDIR := bin CC := $(CROSS)gcc CFLAGS := -I./inc -I./src -I$(KERNELHDR) -O2 -g -Wall -Werror \ diff --git a/ubi-utils/src/bin2nand.c b/ubi-utils/src/bin2nand.c index 168f7dd..26d78de 100644 --- a/ubi-utils/src/bin2nand.c +++ b/ubi-utils/src/bin2nand.c @@ -286,7 +286,7 @@ int main (int argc, char** argv) goto err; } - if (strcmp(args.file_out_data, "") != 0) { + 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", @@ -295,7 +295,7 @@ int main (int argc, char** argv) } } - if (strcmp(args.file_out_oob, "") != 0) { + 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", @@ -349,8 +349,10 @@ err: if (rc != 0) { err_msg("Error during conversion. rc: %d\n", rc); - remove(args.file_out_data); - remove(args.file_out_oob); + if (args.file_out_data) + remove(args.file_out_data); + if (args.file_out_oob) + remove(args.file_out_oob); } return rc; } -- cgit v1.2.3 From b366fb2eb4fa3b72766e25550b5643928e581c91 Mon Sep 17 00:00:00 2001 From: Frank Haverkamp Date: Wed, 21 Jun 2006 15:28:37 +0200 Subject: [MTD] UBI: Enhanced example for testing. Signed-off-by: Frank Haverkamp --- ubi-utils/scripts/Makefile | 26 ++++++++++++++++++++++---- ubi-utils/scripts/pdd.txt | 16 ++++++++++++++++ 2 files changed, 38 insertions(+), 4 deletions(-) create mode 100644 ubi-utils/scripts/pdd.txt diff --git a/ubi-utils/scripts/Makefile b/ubi-utils/scripts/Makefile index 6d014ea..e8850fd 100644 --- a/ubi-utils/scripts/Makefile +++ b/ubi-utils/scripts/Makefile @@ -21,21 +21,39 @@ dd = dd sed = sed bin2nand = bin2nand ubigen = ubigen -mkpfi = mkpfi -pfi2bin = pfi2bin +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) +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 # @@ -54,4 +72,4 @@ clean: $(RM) *.pfi *~ distclean: clean - $(RM) *.bin + $(RM) *.bin *.mif *.oob *.img 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 -- cgit v1.2.3 From d8f5f9f8cfc5a780f7e95b178e919ac2b20fda93 Mon Sep 17 00:00:00 2001 From: Josh Boyer Date: Wed, 21 Jun 2006 08:37:06 -0500 Subject: Fix ubi-header.h to use userspace swab functions Signed-off-by: Josh Boyer --- include/mtd/ubi-header.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/include/mtd/ubi-header.h b/include/mtd/ubi-header.h index 1b325a1..12ce1c9 100644 --- a/include/mtd/ubi-header.h +++ b/include/mtd/ubi-header.h @@ -30,7 +30,7 @@ #ifndef __UBI_HEADER_H__ #define __UBI_HEADER_H__ -#include +#include /* The version of this UBI implementation */ #define UBI_VERSION 1 @@ -104,14 +104,14 @@ typedef struct { * In this implementation UBI uses the big-endian format for on-flash integers. * The below are the corresponding endianess conversion macros. */ -#define cpu_to_ubi16(x) ((ubi16_t){__cpu_to_be16(x)}) -#define ubi16_to_cpu(x) ((uint16_t)__be16_to_cpu((x).int16)) +#define cpu_to_ubi16(x) ((ubi16_t){cpu_to_be16(x)}) +#define ubi16_to_cpu(x) ((uint16_t)be16_to_cpu((x).int16)) -#define cpu_to_ubi32(x) ((ubi32_t){__cpu_to_be32(x)}) -#define ubi32_to_cpu(x) ((uint32_t)__be32_to_cpu((x).int32)) +#define cpu_to_ubi32(x) ((ubi32_t){cpu_to_be32(x)}) +#define ubi32_to_cpu(x) ((uint32_t)be32_to_cpu((x).int32)) -#define cpu_to_ubi64(x) ((ubi64_t){__cpu_to_be64(x)}) -#define ubi64_to_cpu(x) ((uint64_t)__be64_to_cpu((x).int64)) +#define cpu_to_ubi64(x) ((ubi64_t){cpu_to_be64(x)}) +#define ubi64_to_cpu(x) ((uint64_t)be64_to_cpu((x).int64)) /* * Sizes of UBI headers. -- cgit v1.2.3 From faa7699bf15b9a08b1b2658745314ae8f63878a2 Mon Sep 17 00:00:00 2001 From: "dedekind@linutronix.de" Date: Fri, 30 Jun 2006 14:05:25 +0200 Subject: [MTD] UBI: Adaptations to new driver, reworked frontend --- include/mtd/ubi-header.h | 21 +-- include/mtd/ubi-user.h | 7 +- ubi-utils/src/config.h | 4 +- ubi-utils/src/libubi.c | 31 ++-- ubi-utils/src/libubi_int.h | 8 +- ubi-utils/src/ubimkvol.c | 405 ++++++++++++++++++++++++++----------------- ubi-utils/src/ubirmvol.c | 267 +++++++++++++++++----------- ubi-utils/src/ubiupdatevol.c | 50 +++--- 8 files changed, 470 insertions(+), 323 deletions(-) diff --git a/include/mtd/ubi-header.h b/include/mtd/ubi-header.h index 12ce1c9..ca96fc9 100644 --- a/include/mtd/ubi-header.h +++ b/include/mtd/ubi-header.h @@ -41,7 +41,7 @@ /* The initial CRC32 value used when calculating CRC checksums */ #define UBI_CRC32_INIT 0xFFFFFFFFU -/** +/* * Magic numbers of the UBI headers. * * @UBI_EC_HDR_MAGIC: erase counter header magic number (ASCII "UBI#") @@ -52,7 +52,7 @@ enum { UBI_VID_HDR_MAGIC = 0x55424921 }; -/** +/* * Molume type constants used in volume identifier headers. * * @UBI_VID_DYNAMIC: dynamic volume @@ -63,23 +63,19 @@ enum { UBI_VID_STATIC = 2 }; -/** +/* * Compatibility constants used by internal volumes. * * @UBI_COMPAT_DELETE: delete this internal volume before anything is written * to the flash * @UBI_COMPAT_RO: attach this device in read-only mode - * @UBI_COMPAT_IGNORE: ignore this internal volume, but the UBI wear-leveling - * unit may still move these logical eraseblocks to ensure wear-leveling * @UBI_COMPAT_PRESERVE: preserve this internal volume - do not touch its - * physical eraseblocks, don't even allow the wear-leveling unit to move - * them + * physical eraseblocks, don't allow the wear-leveling unit to move them * @UBI_COMPAT_REJECT: reject this UBI image */ enum { UBI_COMPAT_DELETE = 1, UBI_COMPAT_RO = 2, - UBI_COMPAT_IGNORE = 3, UBI_COMPAT_PRESERVE = 4, UBI_COMPAT_REJECT = 5 }; @@ -281,7 +277,7 @@ struct ubi_vid_hdr { */ struct ubi_vid_hdr_upd_vol { ubi32_t vol_id; - uint8_t padding[UBI_VID_HDR_IVOL_DATA_SIZE - 4]; + uint8_t padding[UBI_VID_HDR_IVOL_DATA_SIZE-4]; } __attribute__ ((packed)); /* @@ -295,13 +291,13 @@ struct ubi_vid_hdr_upd_vol { */ #define UBI_INTERNAL_VOL_START (0x7FFFFFFF - 4096) -/** +/* * enum ubi_internal_volume_numbers - volume IDs of internal UBI volumes. * * %UBI_LAYOUT_VOL_ID: volume ID of the layout volume * %UBI_UPDATE_VOL_ID: volume ID of the update volume */ -enum ubi_internal_volume_ids { +enum { UBI_LAYOUT_VOL_ID = UBI_INTERNAL_VOL_START, UBI_UPDATE_VOL_ID = UBI_INTERNAL_VOL_START + 1 }; @@ -351,6 +347,7 @@ enum ubi_internal_volume_ids { * @alignment: volume alignment * @data_pad: how many bytes are not used at the end of the eraseblocks to * satisfy the requested alignment + * @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) * @padding1: reserved, zeroes * @name_len: the volume name length * @name: the volume name @@ -382,7 +379,7 @@ struct ubi_vol_tbl_record { uint8_t vol_type; uint8_t padding1; ubi16_t name_len; - uint8_t name[UBI_VOL_NAME_MAX + 1]; + uint8_t name[UBI_VOL_NAME_MAX+1]; uint8_t padding2[24]; ubi32_t crc; } __attribute__ ((packed)); diff --git a/include/mtd/ubi-user.h b/include/mtd/ubi-user.h index 161f674..0eb1470 100644 --- a/include/mtd/ubi-user.h +++ b/include/mtd/ubi-user.h @@ -32,6 +32,9 @@ */ #define UBI_VOL_NUM_AUTO (-1) +/* Maximum volume name length */ +#define UBI_MAX_VOLUME_NAME 127 + /* * IOCTL commands of UBI character devices */ @@ -56,7 +59,7 @@ /* An eraseblock erasure command, used for debugging, disabled by dafault */ #define UBI_IOCEBER _IOW(UBI_VOL_IOC_MAGIC, 0, int32_t) -/** +/* * UBI volume type constants. * * @UBI_DYNAMIC_VOLUME: dynamic volume @@ -113,7 +116,7 @@ struct ubi_mkvol_req { * * @vol_id: ID of the volume to re-size * @bytes: new size of the volume in bytes - * + * * Re-sizing is possible for both dynamic and static volumes. But while dynamic * volumes may be re-sized arbitrarily, static volumes cannot be made to be * smaller then the number of bytes they bear. To arbitrarily shrink a static diff --git a/ubi-utils/src/config.h b/ubi-utils/src/config.h index 746fa3c..b5bbd5b 100644 --- a/ubi-utils/src/config.h +++ b/ubi-utils/src/config.h @@ -20,8 +20,8 @@ * Author: Frank Haverkamp */ -#define PACKAGE_VERSION "1.0" -#define PACKAGE_BUGREPORT "dedekind@oktetlabs.ru, haver@vnet.ibm.com, or tglx@linutronix.de" +#define PACKAGE_VERSION "1.1" +#define PACKAGE_BUGREPORT "haver@vnet.ibm.com, dedekind@linutronix.de, or tglx@linutronix.de" #define __unused __attribute__((unused)) diff --git a/ubi-utils/src/libubi.c b/ubi-utils/src/libubi.c index 9b9a793..979f157 100644 --- a/ubi-utils/src/libubi.c +++ b/ubi-utils/src/libubi.c @@ -34,6 +34,7 @@ #include #include #include +#include #include "libubi.h" #include "libubi_int.h" @@ -47,7 +48,6 @@ * @sysfs_root sysfs root directory * @ubi_root UBI root directory in sysfs * - * @nlen_max full path to the "maximum volume name length" sysfs file * @version full path to the "UBI version" sysfs file * * @cdev_path path pattern to UBI character devices @@ -85,7 +85,6 @@ struct ubi_lib char *sysfs_root; char *ubi_root; - char *nlen_max; char *version; char *cdev_path; int cdev_path_len; @@ -147,10 +146,6 @@ get_ubi_info(ubi_lib_t desc, struct ubi_info *ubi) if (err) return -1; - err = sysfs_read_int(desc->nlen_max, (int*) &ubi->nlen_max); - if (err) - return -1; - /* Calculate number of UBI devices */ do { char dir[20]; @@ -186,7 +181,6 @@ ubi_dump_handler(ubi_lib_t desc) ubi_lib_t d = desc; printf( "UBI Library Descriptor:\n" "ubi_root: %s\n" - "nlen_max: %s\n" "version: %s\n" "cdev_path: %s\n" "udev_path: %s\n" @@ -204,12 +198,12 @@ ubi_dump_handler(ubi_lib_t desc) "vol_type_path: %s\n" "vol_name_path: %s\n" "cdev_path_len: %d\n\n", - d->ubi_root, d->nlen_max, 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); + 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 @@ -281,11 +275,7 @@ ubi_open(ubi_lib_t *desc) if (!res->ubi_root) goto error; - res->nlen_max = mkpath(res->ubi_root, UBI_NLEN_MAX); - if (!res->nlen_max) - goto error; - - res->version = mkpath(res->ubi_root, UBI_VERSION); + res->version = mkpath(res->ubi_root, UBI_VER); if (!res->version) goto error; @@ -394,7 +384,6 @@ ubi_close(ubi_lib_t *desc) free(tmp->udev_path); free(tmp->cdev_path); free(tmp->version); - free(tmp->nlen_max); free(tmp->ubi_root); free(tmp->sysfs_root); free(tmp); @@ -486,7 +475,7 @@ ubi_get_vol_info(ubi_lib_t desc, unsigned int devn, unsigned int vol_id, int err; int len; char buf1[10]; - char buf2[desc->ubi.nlen_max]; + char buf2[UBI_MAX_VOLUME_NAME]; err = sysfs_read_dev_subst(desc->vol_nums_path, &req->major, &req->minor, 2, devn, vol_id); @@ -523,7 +512,7 @@ ubi_get_vol_info(ubi_lib_t desc, unsigned int devn, unsigned int vol_id, } len = sysfs_read_data_subst(desc->vol_name_path, &buf2[0], - desc->ubi.nlen_max, 2, devn, vol_id); + UBI_MAX_VOLUME_NAME, 2, devn, vol_id); if (len == -1) return -1; diff --git a/ubi-utils/src/libubi_int.h b/ubi-utils/src/libubi_int.h index ab387f5..830a682 100644 --- a/ubi-utils/src/libubi_int.h +++ b/ubi-utils/src/libubi_int.h @@ -1,5 +1,3 @@ -#ifndef __UBI_INT_H__ -#define __UBI_INT_H__ /* * Copyright (c) International Business Machines Corp., 2006 * @@ -24,6 +22,8 @@ * Author: Artem B. Bityutskiy */ +#ifndef __UBI_INT_H__ +#define __UBI_INT_H__ /* * Enable/disable UBI library debugging messages. */ @@ -58,7 +58,7 @@ * @def UBI_NLEN_MAX * @brief Name of syfs file containing the maximum UBI volume name length. * - * @def UBI_VERSION + * @def UBI_VER * @brief Name of sysfs file containing UBI version. * * @def UBI_WEAR @@ -98,7 +98,7 @@ **/ #define UBI_ROOT "ubi" #define UBI_NLEN_MAX "volume_name_max" -#define UBI_VERSION "version" +#define UBI_VER "version" #define UBI_WEAR "wear" #define UBI_VOL_COUNT "volumes_count" #define UBI_TOT_EBS "total_eraseblocks" diff --git a/ubi-utils/src/ubimkvol.c b/ubi-utils/src/ubimkvol.c index 30c569c..d35d2f3 100644 --- a/ubi-utils/src/ubimkvol.c +++ b/ubi-utils/src/ubimkvol.c @@ -19,13 +19,16 @@ /* * An utility to create UBI volumes. * - * Author: Artem B. Bityutskiy + * Author: Artem B. Bityutskiy + * Frank Haverkamp * * 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. */ +#include #include #include #include @@ -33,178 +36,229 @@ #include #include -#include "config.h" +#include #include -static void usage(void); -static int param_sanity_check(ubi_lib_t lib); -static int parse_options(int argc, char * const argv[]); +#define VERSION "1.2" /* - * The variables below are set by command line arguments. + * The variables below are set by command line arguments. */ -static int vol_type = UBI_DYNAMIC_VOLUME; -static int devn = -1; -static long long bytes = 0; -static int alignment = 1; -static int vol_id = UBI_VOL_NUM_AUTO; -static char *name = NULL; -static int nlen = 0; +struct args { + int devn; + int vol_id; + int vol_type; + long long bytes; + int alignment; + char *name; + int nlen; -int main(int argc, char * const argv[]) -{ - int err; - ubi_lib_t lib; + /* special stuff needed to get additional arguments */ + char *arg1; + char **options; /* [STRING...] */ +}; - err = parse_options(argc, argv); - if (err) { - fprintf(stderr, "Wrong options ...\n"); - return err == 1 ? 0 : -1; - } +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, +}; - if (devn == -1) { - fprintf(stderr, "Device number was not specified\n"); - fprintf(stderr, "Use -h option for help\n"); - return -1; - } +static int param_sanity_check(struct args *args, ubi_lib_t lib); +static error_t parse_opt(int key, char *optarg, struct argp_state *state); +const char *argp_program_bug_address = PACKAGE_BUGREPORT; - err = ubi_open(&lib); - if (err) { - perror("Cannot open libubi"); - return -1; - } +static char doc[] = "\nVersion: " VERSION "\n\t" + BUILD_OS" "BUILD_CPU" at "__DATE__" "__TIME__"\n" + "\nMake UBI Volume.\n"; - err = param_sanity_check(lib); - if (err) { - perror("Input parameters check"); - fprintf(stderr, "Use -h option for help\n"); - goto out_libubi; - } +static struct argp_option options[] = { + { .name = "devn", + .key = 'd', + .arg = "", + .flags = 0, + .doc = "UBI device", + .group = OPTION_ARG_OPTIONAL }, - err = ubi_mkvol(lib, devn, vol_id, vol_type, bytes, alignment, name); - if (err < 0) { - perror("Cannot create volume"); - fprintf(stderr, " err=%d\n", err); - goto out_libubi; - } + { .name = "vol_id", + .key = 'n', + .arg = "", + .flags = 0, + .doc = "UBI volume id, if not specified, the volume ID will be " + "assigned automatically", + .group = OPTION_ARG_OPTIONAL }, - /* printf("Created volume %d, %lld bytes, type %s, name %s\n", - vol_id, bytes, vol_type == UBI_DYNAMIC_VOLUME ? - "dynamic" : "static", name); */ + { .name = "type", + .key = 't', + .arg = "", + .flags = 0, + .doc = "volume type (dynamic, static), default is dynamic", + .group = OPTION_ARG_OPTIONAL }, - vol_id = err; - ubi_close(&lib); - return 0; + { .name = "size", + .key = 's', + .arg = "", + .flags = 0, + .doc = "volume size volume size in bytes, " + "kilobytes (KiB) or megabytes (MiB)", + .group = OPTION_ARG_OPTIONAL }, -out_libubi: - ubi_close(&lib); - return -1; -} + { .name = "name", + .key = 'N', + .arg = "", + .flags = 0, + .doc = "volume name", + .group = OPTION_ARG_OPTIONAL }, + + { .name = "alignment", + .key = 'a', + .arg = "", + .flags = 0, + .doc = "volume alignment (default is 1)", + .group = OPTION_ARG_OPTIONAL }, -/* 'getopt()' option string */ -static const char *optstring = "ht:s:n:N:d:a:"; + { .name = NULL, .key = 0, .arg = NULL, .flags = 0, + .doc = NULL, .group = 0 }, +}; -static int parse_options(int argc, char * const argv[]) +static struct argp argp = { + .options = options, + .parser = parse_opt, + .args_doc = 0, + .doc = doc, + .children = NULL, + .help_filter = NULL, + .argp_domain = NULL, +}; + +/* + * @brief Parse the arguments passed into the test case. + * + * @param key The parameter. + * @param arg Argument passed to parameter. + * @param state Location to put information on parameters. + * + * @return error + * + * Get the `input' argument from `argp_parse', which we know is a + * pointer to our arguments structure. + */ +static error_t +parse_opt(int key, char *optarg, struct argp_state *state) { - int opt = 0; - - while (opt != -1) { - char *endp; - - opt = getopt(argc, argv, optstring); - - switch (opt) { - case 'h': - usage(); - return 1; - case 't': - if (!strcmp(optarg, "dynamic")) - vol_type = UBI_DYNAMIC_VOLUME; - else if (!strcmp(optarg, "static")) - vol_type = UBI_STATIC_VOLUME; - else { - fprintf(stderr, "Bad volume type: \"%s\"\n", - optarg); - goto out; - } - break; - case 's': - bytes = strtoull(optarg, &endp, 0); - if (endp == optarg || bytes < 0) { - fprintf(stderr, "Bad volume size: \"%s\"\n", - optarg); - goto out; - } - if (endp != '\0') { - if (strcmp(endp, "KiB") == 0) - bytes *= 1024; - else if (strcmp(endp, "MiB") == 0) - bytes *= 1024*1024; - } - break; - case 'a': - alignment = strtoul(optarg, &endp, 0); - if (*endp != '\0' || endp == optarg || - alignment <= 0) { - fprintf(stderr, "Bad volume alignment: " - "\"%s\"\n", optarg); - goto out; - } - break; - case 'd': - devn = strtoul(optarg, &endp, 0); - if (*endp != '\0' || endp == optarg || devn < 0) { - fprintf(stderr, "Bad UBI device number: " - "\"%s\"\n", optarg); - goto out; - } - break; - case 'n': - vol_id = strtoul(optarg, &endp, 0); - if (*endp != '\0' || endp == optarg || - (vol_id < 0 && vol_id != UBI_DYNAMIC_VOLUME)) { - fprintf(stderr, "Bad volume ID: " - "\"%s\"\n", optarg); - goto out; - } - break; - case 'N': - name = optarg; - nlen = strlen(name); - break; - - case ':': - fprintf(stderr, "Parameter is missing\n"); + char *endp; + struct args *args = state->input; + + 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; - case '?': - fprintf(stderr, "Unknown parameter\n"); + } + 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; - case -1: - break; - default: - fprintf(stderr, "Internal error\n"); + } + 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= */ + 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; + } + break; + case 'n': /* --volid= */ + 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 ARGP_KEY_NO_ARGS: + /* argp_usage(state); */ + break; + + case ARGP_KEY_ARG: + args->arg1 = optarg; + /* Now we consume all the rest of the arguments. + `state->next' is the index in `state->argv' of the + next argument to be parsed, which is the first STRING + we're interested in, so we can just use + `&state->argv[state->next]' as the value for + arguments->strings. + + _In addition_, by setting `state->next' to the end + of the arguments, we can force argp to stop parsing + here and return. */ + + args->options = &state->argv[state->next]; + state->next = state->argc; + break; + + case ARGP_KEY_END: + /* argp_usage(state); */ + break; + + default: + return(ARGP_ERR_UNKNOWN); } return 0; - out: - errno = EINVAL; - return -1; + return(ARGP_ERR_UNKNOWN); } -static int param_sanity_check(ubi_lib_t lib) +static int param_sanity_check(struct args *args, ubi_lib_t lib) { int err, len; struct ubi_info ubi; - if (bytes == 0) { + if (args->bytes == 0) { fprintf(stderr, "Volume size was not specified\n"); goto out; } - if (name == NULL) { + if (args->name == NULL) { fprintf(stderr, "Volume name was not specified\n"); goto out; } @@ -213,39 +267,72 @@ static int param_sanity_check(ubi_lib_t lib) if (err) return -1; - if (devn >= (int)ubi.dev_count) { - fprintf(stderr, "Device %d does not exist\n", devn); + if (args->devn >= (int)ubi.dev_count) { + fprintf(stderr, "Device %d does not exist\n", args->devn); goto out; } - len = strlen(name); - if (len > (int)ubi.nlen_max) { + len = strlen(args->name); + if (len > UBI_MAX_VOLUME_NAME) { fprintf(stderr, "Too long name (%d symbols), max is %d\n", - len, ubi.nlen_max); + len, UBI_MAX_VOLUME_NAME); goto out; } return 0; - out: errno = EINVAL; return -1; } -static void usage(void) +int main(int argc, char * const argv[]) { - printf("Usage: ubi_mkvol OPTIONS\n" - "Version: " PACKAGE_VERSION "\n" - "The command line options:\n" - "\t-h - this help message\n" - "\t-d - UBI device number\n" - "\t-t TYPE - volume type (dynamic, static) " - "(default is dynamic)\n" - "\t-n VOLID - volume ID to assign to the new volume. If not" - "specified, \n" - "\t the volume ID will be assigned automatically\n" - "\t-s BYTES - volume size in bytes, " - "kilobytes (KiB) or megabytes (MiB)\n" - "\t-N NAME - volume name\n" - "\t-a ALIGNMENT - volume alignment (default is 1)\n"); + int err; + ubi_lib_t lib; + + err = argp_parse(&argp, argc, (char **)argv, ARGP_IN_ORDER, 0, + &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; + } + + err = ubi_open(&lib); + if (err) { + perror("Cannot open libubi"); + return -1; + } + + err = param_sanity_check(&myargs, lib); + if (err) { + perror("Input parameters check"); + fprintf(stderr, "Use -h option for help\n"); + goto out_libubi; + } + + err = ubi_mkvol(lib, myargs.devn, myargs.vol_id, myargs.vol_type, + myargs.bytes, myargs.alignment, myargs.name); + 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; + ubi_close(&lib); + return 0; + +out_libubi: + ubi_close(&lib); + return -1; } diff --git a/ubi-utils/src/ubirmvol.c b/ubi-utils/src/ubirmvol.c index cb47b6c..f810263 100644 --- a/ubi-utils/src/ubirmvol.c +++ b/ubi-utils/src/ubirmvol.c @@ -17,11 +17,15 @@ */ /* - * An utility to create UBI volumes. + * An utility to remove UBI volumes. * - * Autor: Artem B. Bityutskiy + * Author: Artem B. Bityutskiy + * Frank Haverkamp + * + * 1.1 Reworked the userinterface to use argp. */ +#include #include #include #include @@ -29,29 +33,180 @@ #include #include -#include "config.h" +#include #include -static void usage(void); -static int param_sanity_check(ubi_lib_t lib); -static int parse_options(int argc, char * const argv[]); +#define VERSION "1.1" /* * The below variables are set by command line options. */ -static int vol_id = -1; -static int devn = -1; +struct args { + int devn; + int vol_id; + + /* 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, ubi_lib_t lib); +static error_t parse_opt(int key, char *optarg, struct argp_state *state); +const char *argp_program_bug_address = PACKAGE_BUGREPORT; + +static char doc[] = "\nVersion: " VERSION "\n\t" + BUILD_OS" "BUILD_CPU" at "__DATE__" "__TIME__"\n" + "\nMake UBI Volume.\n"; + +static struct argp_option options[] = { + { .name = "devn", + .key = 'd', + .arg = "", + .flags = 0, + .doc = "UBI device", + .group = OPTION_ARG_OPTIONAL }, + + { .name = "vol_id", + .key = 'n', + .arg = "", + .flags = 0, + .doc = "UBI volume id, if not specified, the volume ID will be " + "assigned automatically", + .group = OPTION_ARG_OPTIONAL }, + + { .name = NULL, .key = 0, .arg = NULL, .flags = 0, + .doc = NULL, .group = 0 }, +}; + +static struct argp argp = { + .options = options, + .parser = parse_opt, + .args_doc = 0, + .doc = doc, + .children = NULL, + .help_filter = NULL, + .argp_domain = NULL, +}; + +/* + * @brief Parse the arguments passed into the test case. + * + * @param key The parameter. + * @param arg Argument passed to parameter. + * @param state Location to put information on parameters. + * + * @return error + * + * Get the `input' argument from `argp_parse', which we know is a + * pointer to our arguments structure. + */ +static error_t +parse_opt(int key, char *optarg, struct argp_state *state) +{ + char *endp; + struct args *args = state->input; + + switch (key) { + case 'd': /* --devn= */ + 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; + } + break; + case 'n': /* --volid= */ + 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 ARGP_KEY_NO_ARGS: + /* argp_usage(state); */ + break; + + case ARGP_KEY_ARG: + args->arg1 = optarg; + /* Now we consume all the rest of the arguments. + `state->next' is the index in `state->argv' of the + next argument to be parsed, which is the first STRING + we're interested in, so we can just use + `&state->argv[state->next]' as the value for + arguments->strings. + + _In addition_, by setting `state->next' to the end + of the arguments, we can force argp to stop parsing + here and return. */ + + args->options = &state->argv[state->next]; + state->next = state->argc; + break; + + case ARGP_KEY_END: + /* argp_usage(state); */ + break; + + default: + return(ARGP_ERR_UNKNOWN); + } + + return 0; + out: + return(ARGP_ERR_UNKNOWN); +} + +static int param_sanity_check(struct args *args, ubi_lib_t lib) +{ + 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(lib, &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; ubi_lib_t lib; - err = parse_options(argc, argv); + err = argp_parse(&argp, argc, (char **)argv, ARGP_IN_ORDER, 0, + &myargs); if (err) return err == 1 ? 0 : -1; - if (devn == -1) { + if (myargs.devn == -1) { fprintf(stderr, "Device number was not specified\n"); fprintf(stderr, "Use -h option for help\n"); return -1; @@ -63,14 +218,14 @@ int main(int argc, char * const argv[]) return -1; } - err = param_sanity_check(lib); + err = param_sanity_check(&myargs, lib); if (err) { perror("Input parameters check"); fprintf(stderr, "Use -h option for help\n"); goto out_libubi; } - err = ubi_rmvol(lib, devn, vol_id); + err = ubi_rmvol(lib, myargs.devn, myargs.vol_id); old_errno = errno; if (err < 0) { perror("Cannot remove volume"); @@ -84,91 +239,3 @@ out_libubi: ubi_close(&lib); return -1; } - -/* 'getopt()' option string */ -static const char *optstring = "hd:n:"; - -static int parse_options(int argc, char * const argv[]) -{ - int opt = 0; - - while (opt != -1) { - char *endp; - - opt = getopt(argc, argv, optstring); - - switch (opt) { - case 'h': - usage(); - return 1; - case 'n': - vol_id = strtoul(optarg, &endp, 0); - if (*endp != '\0' || endp == optarg || vol_id < 0) { - fprintf(stderr, "Bad volume " - "number: \"%s\"\n", optarg); - goto out; - } - break; - case 'd': - devn = strtoul(optarg, &endp, 0); - if (*endp != '\0' || endp == optarg || devn < 0) { - fprintf(stderr, "Bad UBI device " - "number: \"%s\"\n", optarg); - goto out; - } - break; - case ':': - fprintf(stderr, "Parameter is missing\n"); - goto out; - case '?': - fprintf(stderr, "Unknown parameter\n"); - goto out; - case -1: - break; - default: - fprintf(stderr, "Internal error\n"); - goto out; - } - } - - return 0; - -out: - errno = EINVAL; - return -1; -} - -static int param_sanity_check(ubi_lib_t lib) -{ - int err; - struct ubi_info ubi; - - if (vol_id == -1) { - fprintf(stderr, "Volume ID was not specified\n"); - goto out; - } - - err = ubi_get_info(lib, &ubi); - if (err) - return -1; - - if (devn >= (int)ubi.dev_count) { - fprintf(stderr, "Device %d does not exist\n", devn); - goto out; - } - - return 0; - -out: - errno = EINVAL; - return -1; -} - -static void usage(void) -{ - printf("Usage: ubi_rmvol OPTIONS\n" - "Command line options:\n" - "\t-h - this help message\n" - "\t-d - UBI device number\n" - "\t-n VOLNUM - volume number to remove\n"); -} diff --git a/ubi-utils/src/ubiupdatevol.c b/ubi-utils/src/ubiupdatevol.c index 28e1e8f..fd68dd8 100644 --- a/ubi-utils/src/ubiupdatevol.c +++ b/ubi-utils/src/ubiupdatevol.c @@ -14,14 +14,16 @@ * 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 * - * An utility to update UBI volumes. + * 1.0 Reworked the userinterface to use argp. */ -#include - #include #include #include @@ -36,15 +38,18 @@ #include #include +#include #include +#define VERSION "1.0" + #define MAXPATH 1024 #define BUFSIZE 128 * 1024 #define MIN(x,y) ((x)<(y)?(x):(y)) struct args { - int device; - int volume; + int devn; + int vol_id; int truncate; int broken_update; int bufsize; @@ -55,8 +60,8 @@ struct args { }; static struct args myargs = { - .device = -1, - .volume = -1, + .devn = -1, + .vol_id = -1, .truncate = 0, .broken_update = 0, .bufsize = BUFSIZE, @@ -64,25 +69,24 @@ static struct args myargs = { .options = NULL, }; -static int verbose = 0; - static error_t parse_opt (int key, char *arg, struct argp_state *state); +static int verbose = 0; const char *argp_program_bug_address = PACKAGE_BUGREPORT; -static char doc[] = "\nVersion: " PACKAGE_VERSION "\n\t" +static char doc[] = "\nVersion: " VERSION "\n\t" BUILD_OS" "BUILD_CPU" at "__DATE__" "__TIME__"\n" "\nWrite to UBI Volume.\n"; static struct argp_option options[] = { - { .name = "device", + { .name = "devn", .key = 'd', - .arg = "", + .arg = "", .flags = 0, .doc = "UBI device", .group = OPTION_ARG_OPTIONAL }, - { .name = "volume", + { .name = "vol_id", .key = 'n', .arg = "", .flags = 0, @@ -139,8 +143,12 @@ parse_opt(int key, char *arg, struct argp_state *state) verbose = strtoul(arg, (char **)NULL, 0); break; - case 'd': /* --device= */ - args->device = strtol(arg, (char **)NULL, 0); + case 'n': /* --vol_id= */ + args->vol_id = strtol(arg, (char **)NULL, 0); + break; + + case 'd': /* --devn= */ + args->devn = strtol(arg, (char **)NULL, 0); break; case 'b': /* --bufsize= */ @@ -157,10 +165,6 @@ parse_opt(int key, char *arg, struct argp_state *state) args->broken_update = 1; break; - case 'n': /* --volume= */ - args->volume = strtol(arg, (char **)NULL, 0); - break; - case ARGP_KEY_NO_ARGS: /* argp_usage(state); */ break; @@ -175,8 +179,8 @@ parse_opt(int key, char *arg, struct argp_state *state) arguments->strings. _In addition_, by setting `state->next' to the end - of the arguments, we can force argp to stop parsing here and - return. */ + of the arguments, we can force argp to stop parsing + here and return. */ args->options = &state->argv[state->next]; state->next = state->argc; @@ -205,7 +209,7 @@ ubi_truncate_volume(struct args *args, int64_t bytes) char path[MAXPATH]; int old_errno; - snprintf(path, MAXPATH-1, "/dev/ubi%d_%d", args->device, args->volume); + snprintf(path, MAXPATH-1, "/dev/ubi%d_%d", args->devn, args->vol_id); path[MAXPATH-1] = '\0'; ofd = open(path, O_RDWR); @@ -279,7 +283,7 @@ ubi_update_volume(struct args *args) if (!ifp) exit(EXIT_FAILURE); - snprintf(path, MAXPATH-1, "/dev/ubi%d_%d", args->device, args->volume); + snprintf(path, MAXPATH-1, "/dev/ubi%d_%d", args->devn, args->vol_id); path[MAXPATH-1] = '\0'; ofd = open(path, O_RDWR); -- cgit v1.2.3 From c474d44341b1ab9b1b39cf2bbf8686a610b54614 Mon Sep 17 00:00:00 2001 From: Frank Haverkamp Date: Fri, 30 Jun 2006 14:28:31 +0200 Subject: [MTD] UBI: Fixed program versions --- ubi-utils/src/ubimkvol.c | 6 ++++-- ubi-utils/src/ubirmvol.c | 6 ++++-- ubi-utils/src/ubiupdatevol.c | 5 +++-- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/ubi-utils/src/ubimkvol.c b/ubi-utils/src/ubimkvol.c index d35d2f3..04b7fb5 100644 --- a/ubi-utils/src/ubimkvol.c +++ b/ubi-utils/src/ubimkvol.c @@ -39,7 +39,7 @@ #include #include -#define VERSION "1.2" +#define PROGRAM_VERSION "1.2" /* * The variables below are set by command line arguments. @@ -70,9 +70,11 @@ static struct args myargs = { static int param_sanity_check(struct args *args, ubi_lib_t lib); static error_t parse_opt(int key, char *optarg, struct argp_state *state); + +const char *argp_program_version = PROGRAM_VERSION; const char *argp_program_bug_address = PACKAGE_BUGREPORT; -static char doc[] = "\nVersion: " VERSION "\n\t" +static char doc[] = "\nVersion: " PROGRAM_VERSION "\n\t" BUILD_OS" "BUILD_CPU" at "__DATE__" "__TIME__"\n" "\nMake UBI Volume.\n"; diff --git a/ubi-utils/src/ubirmvol.c b/ubi-utils/src/ubirmvol.c index f810263..43679bc 100644 --- a/ubi-utils/src/ubirmvol.c +++ b/ubi-utils/src/ubirmvol.c @@ -36,7 +36,7 @@ #include #include -#define VERSION "1.1" +#define PROGRAM_VERSION "1.1" /* * The below variables are set by command line options. @@ -60,9 +60,11 @@ static struct args myargs = { static int param_sanity_check(struct args *args, ubi_lib_t lib); static error_t parse_opt(int key, char *optarg, struct argp_state *state); + +const char *argp_program_version = PROGRAM_VERSION; const char *argp_program_bug_address = PACKAGE_BUGREPORT; -static char doc[] = "\nVersion: " VERSION "\n\t" +static char doc[] = "\nVersion: " PROGRAM_VERSION "\n\t" BUILD_OS" "BUILD_CPU" at "__DATE__" "__TIME__"\n" "\nMake UBI Volume.\n"; diff --git a/ubi-utils/src/ubiupdatevol.c b/ubi-utils/src/ubiupdatevol.c index fd68dd8..dcb7399 100644 --- a/ubi-utils/src/ubiupdatevol.c +++ b/ubi-utils/src/ubiupdatevol.c @@ -41,7 +41,7 @@ #include #include -#define VERSION "1.0" +#define PROGRAM_VERSION "1.0" #define MAXPATH 1024 #define BUFSIZE 128 * 1024 @@ -72,9 +72,10 @@ static struct args myargs = { static error_t parse_opt (int key, char *arg, struct argp_state *state); static int verbose = 0; +const char *argp_program_version = PROGRAM_VERSION; const char *argp_program_bug_address = PACKAGE_BUGREPORT; -static char doc[] = "\nVersion: " VERSION "\n\t" +static char doc[] = "\nVersion: " PROGRAM_VERSION "\n\t" BUILD_OS" "BUILD_CPU" at "__DATE__" "__TIME__"\n" "\nWrite to UBI Volume.\n"; -- cgit v1.2.3 From 02e51e7e0b4c50d900d3a246b2bce73df3a191ee Mon Sep 17 00:00:00 2001 From: Frank Haverkamp Date: Mon, 10 Jul 2006 15:47:05 +0200 Subject: [MTD] UBI: Fixed 16 KiB blocksize problem. --- ubi-utils/src/pfi2bin.c | 20 +++++++++++++++++--- ubi-utils/src/reader.c | 35 +++++++++++++++++++++++------------ ubi-utils/src/reader.h | 1 + 3 files changed, 41 insertions(+), 15 deletions(-) diff --git a/ubi-utils/src/pfi2bin.c b/ubi-utils/src/pfi2bin.c index 6536c19..4c25faf 100644 --- a/ubi-utils/src/pfi2bin.c +++ b/ubi-utils/src/pfi2bin.c @@ -42,6 +42,8 @@ #include "peb.h" #include "crc32.h" +#define PROGRAM_VERSION "1.2" + #define MAX_FNAME 255 #define DEFAULT_ERASE_COUNT 0 /* Hmmm.... Perhaps */ #define ERR_BUF_SIZE 1024 @@ -71,9 +73,9 @@ static const char copyright [] __attribute__((unused)) = static error_t parse_opt (int key, char *arg, struct argp_state *state); -const char *argp_program_version = PACKAGE_VERSION; +const char *argp_program_version = PROGRAM_VERSION; const char *argp_program_bug_address = PACKAGE_BUGREPORT; -static char doc[] = "\nVersion: " PACKAGE_VERSION "\n\tBuilt on " +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"; @@ -355,6 +357,8 @@ write_ubi_volume_table(pdd_data_t pdd, list_t raw_pebs, 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) @@ -379,11 +383,21 @@ write_ubi_volume_table(pdd_data_t pdd, list_t raw_pebs, 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); + 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, diff --git a/ubi-utils/src/reader.c b/ubi-utils/src/reader.c index e4a8ceb..5de06d5 100644 --- a/ubi-utils/src/reader.c +++ b/ubi-utils/src/reader.c @@ -34,8 +34,9 @@ #include "reader.h" /* @FIXME hard coded offsets right now - get them from Artem? */ -#define NAND_DEFAULT_VID_HDR_OFF 1984 -#define NOR_DEFAULT_VID_HDR_OFF 64 +#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); \ @@ -74,8 +75,27 @@ read_pdd_data(FILE* fp_pdd, pdd_data_t* pdd_data, } 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; - res->vid_hdr_offset = NAND_DEFAULT_VID_HDR_OFF; + + 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; @@ -113,11 +133,6 @@ read_pdd_data(FILE* fp_pdd, pdd_data_t* pdd_data, return rc; } -/** - * FIXME enhance flasing raw PFI content e.g. IPLs for NAND and NOR. - * Here is one of the only places where the flash type and its special - * handling is exposed to the users. - */ 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) @@ -175,10 +190,6 @@ read_pfi_raw(pfi_header pfi_hd, FILE* fp_pfi __unused, pfi_raw_t* pfi_raw, return rc; } -/** - * FIXME Enhance reading raw PFI sections, e.g. IPL. See comment at - * write_pfi_ubi. - */ 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) diff --git a/ubi-utils/src/reader.h b/ubi-utils/src/reader.h index 93c15e3..d00fa17 100644 --- a/ubi-utils/src/reader.h +++ b/ubi-utils/src/reader.h @@ -40,6 +40,7 @@ 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; -- cgit v1.2.3 From 6918e0320c89bc892a6c5d2c6792e5d058e40a6e Mon Sep 17 00:00:00 2001 From: Frank Haverkamp Date: Mon, 6 Nov 2006 16:54:10 +0100 Subject: [MTD] UBI: Update testscripts The testscripts ensure the correct functionality of the UBI code on my reference system. Signed-off-by: Frank Haverkamp --- ubi-utils/scripts/ubi_jffs2_test.sh | 412 ++++++++++++++++++++++++++++++++++++ ubi-utils/scripts/ubi_test.sh | 17 +- ubi-utils/scripts/ubi_tools_test.sh | 10 +- 3 files changed, 434 insertions(+), 5 deletions(-) create mode 100644 ubi-utils/scripts/ubi_jffs2_test.sh diff --git a/ubi-utils/scripts/ubi_jffs2_test.sh b/ubi-utils/scripts/ubi_jffs2_test.sh new file mode 100644 index 0000000..4d97431 --- /dev/null +++ b/ubi-utils/scripts/ubi_jffs2_test.sh @@ -0,0 +1,412 @@ +#!/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 +# +# 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 () +{ + rm -f $TLOG + touch $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 index 622ec7e..3835bcb 100755 --- a/ubi-utils/scripts/ubi_test.sh +++ b/ubi-utils/scripts/ubi_test.sh @@ -5,12 +5,18 @@ # Written in shell language to reduce dependencies to more sophisticated # interpreters, which may not be available on some stupid platforms. # +# Author: Frank Haverkamp +# +# 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=ubiupdateevol +UBIWRITEVOL=ubiwritevol # 128 KiB 131072 # 256 KiB 262144 @@ -135,6 +141,7 @@ mkvol_rmvol_test () 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 @@ -204,8 +211,10 @@ writevol_test () fi passed - echo -n "*** Now writing data to volume ... " + echo "*** Now writing data to volume ... " + # sleep 5 ls -l testdata.bin + echo " $UBIWRITEVOL -d0 -n$MINVOL testdata.bin" $UBIWRITEVOL -d0 -n$MINVOL testdata.bin if [ $? -ne "0" ] ; then exit_failure @@ -265,7 +274,7 @@ if [ $? -ne "0" ]; then fi # Set to zero if not running on example hardware -grep 114218D /proc/cpuinfo > /dev/null +grep 1142 /proc/cpuinfo > /dev/null if [ $? -eq "0" ]; then echo "Running on example hardware" mount -o remount,rw / / @@ -304,8 +313,8 @@ done echo "***********************************************************************" echo "* write to dynamic volumes ... *" echo "***********************************************************************" +echo "VERSION: $VERSION" -#for size in 31313 ; do for size in 131073 131072 2048 1 4096 12800 31313 262144 ; do writevol_test $size dynamic done diff --git a/ubi-utils/scripts/ubi_tools_test.sh b/ubi-utils/scripts/ubi_tools_test.sh index 9163e0c..b4d167e 100755 --- a/ubi-utils/scripts/ubi_tools_test.sh +++ b/ubi-utils/scripts/ubi_tools_test.sh @@ -7,6 +7,12 @@ # Written in shell language to reduce dependencies to more sophisticated # interpreters, which may not be available on some stupid platforms. # +# Author: Frank Haverkamp +# +# 1.0 Initial version +# + +VERSION="1.0" export PATH=$PATH:~/bin:/usr/local/bin:/home/dedekind/work/prj/ubi/tools/flashutils/bin/ @@ -121,7 +127,7 @@ if [ $? -ne "0" ]; then fi # Set to zero if not running on example hardware -grep 114218D /proc/cpuinfo > /dev/null +grep 1142 /proc/cpuinfo > /dev/null if [ $? -eq "0" ]; then echo "Running on example hardware" mount -o remount,rw / / @@ -135,6 +141,7 @@ fi pfiflash_basic () { echo "Calling pfiflash with test-data ... " + echo " $PFIFLASH $test_pfi" $PFIFLASH $test_pfi if [ $? -ne "0" ]; then echo "Uhhh something went wrong!" @@ -232,6 +239,7 @@ pfiflash_advanced () echo "***********************************************************************" echo "* Testing pfiflash ... *" echo "***********************************************************************" +echo "VERSION: $VERSION" pfiflash_basic pfiflash_advanced -- cgit v1.2.3 From 31b015fc08a13a5b63245808f7d1a49eadd8193a Mon Sep 17 00:00:00 2001 From: Drake Dowsett Date: Mon, 6 Nov 2006 16:54:10 +0100 Subject: [MTD] UBI: pfiflash needs to flash raw sections and check CRC Flashing of raw partitions should be possible now. CRC checking of pfi files before flashing the content was added. Signed-off-by: Frank Haverkamp --- ubi-utils/Makefile | 8 +- ubi-utils/src/bootenv.c | 105 +++-- ubi-utils/src/bootenv.h | 9 + ubi-utils/src/error.c | 67 +++- ubi-utils/src/error.h | 2 +- ubi-utils/src/libpfiflash.c | 841 ++++++++++++++++++++++++++++++----------- ubi-utils/src/pfiflash.c | 37 +- ubi-utils/src/pfiflash.h | 13 + ubi-utils/src/pfiflash_error.h | 69 ++++ ubi-utils/src/reader.c | 15 +- ubi-utils/src/reader.h | 2 + 11 files changed, 890 insertions(+), 278 deletions(-) create mode 100644 ubi-utils/src/pfiflash_error.h diff --git a/ubi-utils/Makefile b/ubi-utils/Makefile index 6161f3d..2776c07 100644 --- a/ubi-utils/Makefile +++ b/ubi-utils/Makefile @@ -48,15 +48,15 @@ ubirmvol: ubirmvol.o error.o libubi.o libubi_sysfs.o $(CC) $(LDFLAGS) -o $@ $^ pddcustomize: pddcustomize.o error.o libubimirror.o bootenv.o hashmap.o \ - libubi.o libubi_sysfs.o + libubi.o libubi_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 libubi.o libubi_sysfs.o + bootenv.o hashmap.o pfi.o libubi.o libubi_sysfs.o crc32.o $(CC) $(LDFLAGS) -o $@ $^ ubimirror: ubimirror.o error.o libubimirror.o bootenv.o hashmap.o \ - libubi.o libubi_sysfs.o + libubi.o libubi_sysfs.o crc32.o $(CC) $(LDFLAGS) -o $@ $^ nand2bin: nand2bin.o nandecc.o nandcorr.o @@ -68,7 +68,7 @@ bin2nand: bin2nand.o error.o nandecc.o ubigen: ubigen.o libubigen.o crc32.o $(CC) $(LDFLAGS) -o $@ $^ -mkbootenv: mkbootenv.o bootenv.o hashmap.o error.o +mkbootenv: mkbootenv.o bootenv.o hashmap.o error.o crc32.o $(CC) $(LDFLAGS) -o $@ $^ unubi: unubi.o crc32.o diff --git a/ubi-utils/src/bootenv.c b/ubi-utils/src/bootenv.c index 8871d90..ed15dc7 100644 --- a/ubi-utils/src/bootenv.c +++ b/ubi-utils/src/bootenv.c @@ -27,10 +27,14 @@ #include #include -#include "config.h" #include "hashmap.h" #include "error.h" +#include +#include "crc32.h" + +#define __unused __attribute__((unused)) + #define BOOTENV_MAXLINE 512 /* max line size of a bootenv.txt file */ /* Structures */ @@ -189,9 +193,10 @@ extract_pair(const char *str, bootenv_t env) *val = '\0'; /* split strings */ val++; + rc = bootenv_set(env, key, val); -err: + err: free(key); return rc; } @@ -248,46 +253,36 @@ bootenv_create(bootenv_t* env) 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 = 0; /* current length */ - uint32_t j = 0; /* processed chars */ - uint32_t items = 0; /* processed items */ - int rc = 0; + const char *curr = buf; /* ptr to current key/value pair */ + uint32_t i, j; /* current length, chars processed */ - if (buf[size-1] != '\0') { + if (buf[size - 1] != '\0') /* must end in '\0' */ return BOOTENV_EFMT; - } - while ((i = strlen(curr)) != 0) { - /* there is a key value pair remaining */ - rc = extract_pair(curr, env); - if (rc != 0) { - rc = BOOTENV_EINVAL; - return rc; - } - items++; + 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; - j += i; - if (j >= size) - return 0; /* finished, end of buffer */ - curr += i + 1; + if (i == 1) + return 0; /* no string found */ + + if (extract_pair(curr, env) != 0) + return BOOTENV_EINVAL; } return 0; } -/** - * 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) +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; @@ -306,33 +301,47 @@ bootenv_read(FILE* fp, bootenv_t env, size_t size) */ 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 */ } - - /* log_msg("%c", c); */ /* FIXME DBG */ - - buf[i++] = c; 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); - /* FIXME DBG */ - /* log_msg("\n%s:%d rc=%d\n", __func__, __LINE__, rc); */ - 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) @@ -390,8 +399,8 @@ err: } static int -fill_output_buffer(bootenv_t env, char *buf, size_t buf_size_max, - size_t *written) +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; @@ -404,7 +413,7 @@ fill_output_buffer(bootenv_t env, char *buf, size_t buf_size_max, goto err; for (i = 0; i < keys_size; i++) { - if (wr > buf_size_max) { + if (wr > BOOTENV_MAXSIZE) { rc = -ENOSPC; goto err; } @@ -413,7 +422,7 @@ fill_output_buffer(bootenv_t env, char *buf, size_t buf_size_max, if (rc != 0) goto err; - wr += snprintf(buf + wr, buf_size_max - wr, + wr += snprintf(buf + wr, BOOTENV_MAXSIZE - wr, "%s=%s", keys[i], val); wr++; /* for \0 */ } @@ -428,11 +437,12 @@ err: } int -bootenv_write(FILE* fp, bootenv_t env) +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; @@ -441,10 +451,17 @@ bootenv_write(FILE* fp, bootenv_t env) 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; @@ -456,6 +473,12 @@ err: 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) { diff --git a/ubi-utils/src/bootenv.h b/ubi-utils/src/bootenv.h index 86743ed..9003d70 100644 --- a/ubi-utils/src/bootenv.h +++ b/ubi-utils/src/bootenv.h @@ -230,6 +230,10 @@ int bootenv_size(bootenv_t env, size_t *size); */ 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. @@ -249,6 +253,11 @@ int bootenv_read_txt(FILE* fp, bootenv_t env); */ 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. diff --git a/ubi-utils/src/error.c b/ubi-utils/src/error.c index c8c623c..4aaedad 100644 --- a/ubi-utils/src/error.c +++ b/ubi-utils/src/error.c @@ -25,6 +25,7 @@ #include "error.h" #define MAXLINE 4096 +#define MAXWIDTH 80 static FILE *logfp = NULL; @@ -35,6 +36,9 @@ read_procfile(FILE *fp_out, const char *procfile) { FILE *fp; + if (!fp_out) + return -ENXIO; + fp = fopen(procfile, "r"); if (!fp) return -ENOENT; @@ -57,6 +61,9 @@ read_procfile(FILE *fp_out, const char *procfile) void error_initlog(const char *logfile) { + if (!logfile) + return; + logfp = fopen(logfile, "a+"); read_procfile(logfp, "/proc/cpuinfo"); } @@ -143,7 +150,11 @@ __err_dump(const char *fmt, ...) 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) @@ -166,9 +177,61 @@ err_doit(int errnoflag, int level __attribute__((unused)), if (logfp) { fputs(buf, logfp); fflush(logfp); + return; /* exit when logging completes */ } - fputs(buf, fpout); + 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); diff --git a/ubi-utils/src/error.h b/ubi-utils/src/error.h index e8d7137..05d8078 100644 --- a/ubi-utils/src/error.h +++ b/ubi-utils/src/error.h @@ -78,7 +78,7 @@ void info_msg(const char *fmt, ...); __err_msg(fmt, ##__VA_ARGS__); \ } while (0) #else -#define dbg_msg(fmt, ...) +#define dbg_msg(fmt, ...) do {} while (0) #endif #endif /* __ERROR_H__ */ diff --git a/ubi-utils/src/libpfiflash.c b/ubi-utils/src/libpfiflash.c index ed2af3c..1dc9f10 100644 --- a/ubi-utils/src/libpfiflash.c +++ b/ubi-utils/src/libpfiflash.c @@ -16,16 +16,13 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -/** - * @file pfiflash.c - * - * @author Oliver Lohmann - * - * @brief This library is provides an interface to the pfiflash utility. - * - * Wed Mar 15 11:39:19 CET 2006 Initial creation. - * - * @TODO Comare data before writing it. This implies that the volume +/* + * Authors: Oliver Lohmann + * Drake Dowsett + * Contact: Andreas Arnez + */ + +/* 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. @@ -40,89 +37,130 @@ #include #include -//#include /* FIXME Is this ok here!!?? */ +#include /* FIXME Is this ok here? */ -#include "config.h" +#include "pfiflash_error.h" #include "ubimirror.h" #include "error.h" #include "reader.h" #include "example_ubi.h" #include "bootenv.h" -static const char copyright [] __attribute__((unused)) = +/* ubi-header.h and crc32.h needed for CRC checking */ +#include /* FIXME Is this ok here? */ +#include "crc32.h" + +#define __unused __attribute__((unused)) + +static const char copyright [] __unused = "Copyright (c) International Business Machines Corp., 2006"; -#define EBUF(fmt...) do { \ - snprintf(err_buf, err_buf_size, fmt); \ +/* 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 }; -/**< An array of PDD function pointers indexed by the algorithm. */ - 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_sections(FILE* pfi, list_t pfi_raws, +skip_raw_volumes(FILE* pfi, list_t pfi_raws, char* err_buf, size_t err_buf_size) { - int rc = 0; - + int rc; void *i; list_t ptr; - size_t j, skip_size; if (is_empty(pfi_raws)) return 0; + rc = 0; foreach(i, ptr, pfi_raws) { - skip_size = ((pfi_raw_t)i)->data_size; - for(j = 0; j < skip_size; j++) { - fgetc(pfi); - if (ferror(pfi)) { - EBUF("Cannot skip raw section in PFI."); - rc = -EIO; + 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; } + /** - * @brief Wraps the ubi_mkvol functions and implements a hook for the bootenv - * update. - * @param devno UBI device number. - * @param s Current seqnum. - * @param u Information about the UBI volume from the PFI. - * @param err_buf An error buffer. - * @param err_buf_size The size of the error buffer. - * @return 0 On Sucess. - * @return else Error. - */ + * 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) +my_ubi_mkvol(int devno, int s, pfi_ubi_t u, + char *err_buf, size_t err_buf_size) { - int rc = 0; - int type; - ubi_lib_t ulib = NULL; + int rc, type; + ubi_lib_t ulib; + + rc = 0; + ulib = NULL; - log_msg("%s(vol_id=%d, size=%d, data_size=%d, type=%d, " - "alig=%d, nlen=%d, name=%s)", __func__, + 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; } @@ -130,7 +168,6 @@ my_ubi_mkvol(int devno, int s, pfi_ubi_t u, char *err_buf, size_t err_buf_size) case pfi_ubi_static: type = UBI_STATIC_VOLUME; break; case pfi_ubi_dynamic: - type = UBI_DYNAMIC_VOLUME; break; default: type = UBI_DYNAMIC_VOLUME; } @@ -138,58 +175,78 @@ my_ubi_mkvol(int devno, int s, pfi_ubi_t u, char *err_buf, size_t err_buf_size) rc = ubi_mkvol(ulib, devno, u->ids[s], type, u->size, u->alignment, u->names[s]); if (rc != 0) { - EBUF("Cannot create volume: %d", u->ids[s]); + rc = -PFIFLASH_ERR_UBI_MKVOL; + EBUF(PFIFLASH_ERRSTR[-rc], u->ids[s]); goto err; } err: if (ulib != NULL) ubi_close(&ulib); + return rc; } + /** - * @brief A wrapper around the UBI library function ubi_rmvol. - * @param devno UBI device number. - * @param s Current seqnum. - * @param u Information about the UBI volume from the PFI. - * @param err_buf An error buffer. - * @param err_buf_size The size of the error buffer. + * 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 __unused, size_t err_buf_size __unused) + char *err_buf, size_t err_buf_size) { - int rc = 0; - ubi_lib_t ulib = NULL; - int fd; + int rc, fd; + ubi_lib_t ulib; - log_msg("%s(id=%d)", __func__, id); + rc = 0; + ulib = NULL; + + log_msg("[ ubirmvol id=%d", id); rc = ubi_open(&ulib); - if (rc != 0) + if (rc != 0) { + rc = -PFIFLASH_ERR_UBI_OPEN; + EBUF(PFIFLASH_ERRSTR[-rc]); goto err; + } - /** - * Truncate if it exist or not. - */ + /* truncate whether it exist or not */ fd = ubi_vol_open(ulib, devno, id, O_RDWR); if (fd == -1) - return 0; /* not existent, return */ + return 0; /* not existent, return 0 */ rc = ubi_vol_update(fd, 0); + ubi_vol_close(fd); if (rc < 0) { - fprintf(stderr, "update failed rc=%d errno=%d\n", rc, errno); - ubi_vol_close(fd); + rc = -PFIFLASH_ERR_UBI_VOL_UPDATE; + EBUF(PFIFLASH_ERRSTR[-rc], id); goto err; /* if EBUSY than empty device, continue */ } - ubi_vol_close(fd); rc = ubi_rmvol(ulib, devno, id); if (rc != 0) { - /* @TODO Define a ubi_rmvol return value which says +#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 @@ -198,65 +255,128 @@ my_ubi_rmvol(int devno, uint32_t id, /* if ((errno == EINVAL) || (errno == ENODEV)) return 0; */ /* currently it is EINVAL or ENODEV */ - dbg_msg("Remove UBI volume %d returned with error: %d " - "errno=%d", id, rc, errno); 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 = 0; - ubi_lib_t ulib = NULL; - FILE* fp_in = NULL; + int rc; + FILE* fp_in; + ubi_lib_t ulib; + + rc = 0; + fp_in = NULL; + ulib = NULL; rc = ubi_open(&ulib); - if (rc) - return rc; + 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) { - EBUF("Cannot open bootenv volume"); - rc = -EIO; + rc = -PFIFLASH_ERR_UBI_VOL_FOPEN; + EBUF(PFIFLASH_ERRSTR[-rc], id); goto err; } - log_msg("%s reading old bootenvs", __func__); + log_msg("[ reading old bootenvs ..."); /* Save old bootenvs for reference */ rc = bootenv_read(fp_in, bootenv_old, BOOTENV_MAXSIZE); - if (rc) - EBUF("Cannot read bootenv_old"); + 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, /* new pdd data contained in pfi */ - size_t fp_in_size, /* data size of new pdd data in pfi */ + 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 = 0; - int warnings = 0; - ubi_lib_t ulib = NULL; - bootenv_t bootenv_new = NULL; - bootenv_t bootenv_res = NULL; - size_t update_size = 0; - FILE *fp_out = NULL; - - log_msg("%s(id=%d, fp_in=%p)", __func__, id, fp_in); + 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 @@ -269,43 +389,65 @@ write_bootenv_volume(int devno, uint32_t id, bootenv_t bootenv_old, 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) + if (rc != 0) { + rc = -PFIFLASH_ERR_BOOTENV_CREATE; + EBUF(PFIFLASH_ERRSTR[-rc], " 'new'"); goto err; + } + rc = bootenv_create(&bootenv_res); - if (rc != 0) + if (rc != 0) { + rc = -PFIFLASH_ERR_BOOTENV_CREATE; + EBUF(PFIFLASH_ERRSTR[-rc], " 'res'"); goto err; + } - rc = bootenv_read(fp_in, bootenv_new, fp_in_size); - if (rc != 0) + 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) + if (rc != 0) { + EBUF_PREPEND("handling PDD"); goto err; - if (warnings) { - /* @TODO Do sth with the warning */ + } + else if (warnings) + /* TODO do something with warnings */ dbg_msg("A warning in the PDD operation occured: %d", warnings); - } - log_msg("... (2)"); rc = bootenv_size(bootenv_res, &update_size); - if (rc != 0) + 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 == NULL) + 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) { - EBUF("Write operation on ubi%d_%d failed.", devno, id); - rc = -EIO; + rc = -PFIFLASH_ERR_BOOTENV_WRITE; + EBUF(PFIFLASH_ERRSTR[-rc], devno, id); goto err; } @@ -318,102 +460,327 @@ write_bootenv_volume(int devno, uint32_t id, bootenv_t bootenv_old, 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, - char *err_buf __unused, size_t err_buf_size __unused) + uint32_t pfi_crc, + char *err_buf, size_t err_buf_size) { - int rc = 0; - ubi_lib_t ulib = NULL; - FILE* fp_out = NULL; - int c; - size_t i; + int rc; + uint32_t crc, crc32_table[256]; + size_t bytes_left; + FILE* fp_out; + ubi_lib_t ulib; - log_msg("%s(id=%d, update_size=%d fp_in=%p)", - __func__, id, update_size, fp_in); + 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) - return rc; + 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 == NULL) { - rc = -1; + if (!fp_out) { + rc = -PFIFLASH_ERR_UBI_VOL_FOPEN; + EBUF(PFIFLASH_ERRSTR[-rc], id); goto err; } - log_msg("starting the update ... "); /* FIXME DBG */ - for (i = 0; i < update_size; i++) { - c = getc(fp_in); - if (c == EOF && ferror(fp_in)) { - rc = -EIO; + 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; } - if (putc(c, fp_out) == EOF) { - rc = -EIO; + 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; } - /* FIXME DBG */ - /* if ((i & 0xFFF) == 0xFFF) log_msg("."); */ + bytes_left -= to_rw; } - /* log_msg("\n"); */ /* FIXME DBG */ + + 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; } /** - * @brief ... - * @precondition The PFI file contains at least one ubi_id entry. - * This is assured by the PFI read process. - * @postcondition The used seqnum number is set in the UBI PFI - * header list. - * The UBI volumes specified by seqnum are processed. - */ + * 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; + list_t ptr; + + if (is_empty(pfi_raws)) + return 0; + + if (rawdev == NULL) + return 0; + + rc = 0; + + 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 */ + 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++) { + 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]); + return rc; + } + if ((char)c != pfi_data[k]) { + fclose(mtd); + return -1; + } + } + } + rc = fclose(mtd); + if (rc != 0) { + rc = -PFIFLASH_ERR_MTD_CLOSE; + EBUF(PFIFLASH_ERRSTR[-rc], rawdev); + goto err; + } + } + + err: + 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 = 0; + int rc; pfi_ubi_t u; list_t ptr; + rc = 0; + foreach(u, ptr, pfi_ubis) { int s = seqnum; - if (seqnum > ((int)u->ids_size - 1)) { + + 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); + 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], + } + + 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) + 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, @@ -421,87 +788,67 @@ process_ubi_volumes(FILE* pfi, int seqnum, list_t pfi_ubis, bootenv_old, pdd_f, pfi, u->data_size, + u->crc, err_buf, err_buf_size); - } - else { + 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: - EBUF("Invoked unknown UBI operation."); - rc = -1; - goto err; - - } - if (rc != 0) { + rc = -PFIFLASH_ERR_UBI_UNKNOWN; + EBUF(PFIFLASH_ERRSTR[-rc]); goto err; } } - err: - return rc; - -} - -static int -erase_unmapped_ubi_volumes(int devno, list_t pfi_ubis, - char *err_buf, size_t err_buf_size) -{ - int rc = 0; - list_t ptr; - pfi_ubi_t u; - size_t i; - uint8_t ubi_volumes[PFI_UBI_MAX_VOLUMES]; - - 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) { - EBUF("PFI file contains an invalid " - "volume id: %d", 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) - 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 = 0; - list_t ptr; + int rc; uint32_t j; + list_t ptr; pfi_ubi_t i; - ubi_lib_t ulib = NULL; + ubi_lib_t ulib; - log_msg("%s(...)", __func__); + rc = 0; + ulib = NULL; + + log_msg("[ mirror ..."); rc = ubi_open(&ulib); - if (rc != 0) + if (rc != 0) { + rc = -PFIFLASH_ERR_UBI_OPEN; + EBUF(PFIFLASH_ERRSTR[-rc]); goto err; + } /** * Execute all mirror operations on redundant groups. @@ -510,25 +857,26 @@ mirror_ubi_volumes(uint32_t devno, list_t pfi_ubis, * ubimirror). */ foreach(i, ptr, pfi_ubis) { - for(j = 0; j < i->ids_size; j++) { + 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); + 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); + 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); + rc = ubimirror(devno, i->curr_seqnum, i->ids, i->ids_size, + err_buf, err_buf_size); if (rc != 0) goto err; } @@ -537,44 +885,71 @@ mirror_ubi_volumes(uint32_t devno, list_t pfi_ubis, 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(FILE* pfi, int complete, int seqnum, pdd_handling_t pdd_handling, - char *err_buf, size_t err_buf_size) +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 = 0; - pdd_func_t pdd_f = NULL; + int rc; + bootenv_t bootenv; + pdd_func_t pdd_f; if (pfi == NULL) return -EINVAL; - /** - * If the user didnt specify a seqnum we start per default - * with the index 0 - */ + 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 */ - bootenv_t bootenv; rc = bootenv_create(&bootenv); if (rc != 0) { - EBUF("Cannot create bootenv variable"); + 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); + rc = read_pfi_headers(&pfi_raws, &pfi_ubis, pfi, err_buf, err_buf_size); if (rc != 0) { - EBUF("Cannot read PFI headers."); + EBUF_PREPEND("reading PFI header"); goto err; } - /* @TODO: If you want to implement an IPL update - start here. */ - rc = skip_raw_sections(pfi, pfi_raws, err_buf, err_buf_size); + 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; } @@ -582,33 +957,41 @@ pfiflash(FILE* pfi, int complete, int seqnum, pdd_handling_t pdd_handling, rc = erase_unmapped_ubi_volumes(EXAMPLE_UBI_DEVICE, pfi_ubis, err_buf, err_buf_size); if (rc != 0) { - EBUF("Cannot delete unmapped UBI volumes."); + EBUF_PREPEND("deleting unmapped UBI volumes"); goto err; } } - if (((int)pdd_handling >= 0) && (pdd_handling < PDD_HANDLING_NUM)) { + if (((int)pdd_handling >= 0) && + (pdd_handling < PDD_HANDLING_NUM)) pdd_f = pdd_funcs[pdd_handling]; - } else { - EBUF("Used unknown PDD handling algorithm (pdd_handling)"); + 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) { + 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) + if (rc != 0) { + EBUF_PREPEND("mirroring UBI volumes"); goto err; + } } err: @@ -617,3 +1000,19 @@ pfiflash(FILE* pfi, int complete, int seqnum, pdd_handling_t pdd_handling, 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/pfiflash.c b/ubi-utils/src/pfiflash.c index 04f62df..c49fb1e 100644 --- a/ubi-utils/src/pfiflash.c +++ b/ubi-utils/src/pfiflash.c @@ -21,6 +21,8 @@ * 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. */ #include @@ -34,12 +36,15 @@ #include #include +#undef DEBUG #include "error.h" #include "config.h" -const char *argp_program_version = PACKAGE_VERSION; +#define PROGRAM_VERSION "1.2" + +const char *argp_program_version = PROGRAM_VERSION; const char *argp_program_bug_address = PACKAGE_BUGREPORT; -static char doc[] = "\nVersion: " PACKAGE_VERSION "\n\tBuilt on " +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"; @@ -83,12 +88,17 @@ static struct argp_option options[] = { "'keep', 'merge' or 'overwrite'.", group: 2 }, + { name: "raw-flash", key: 'r', arg: "", flags: 0, + doc: "Flash the raw data. Use the specified mtd device.", + group: 2 }, + { name: NULL, key: 0, arg: NULL, flags: 0, doc: NULL, group: 0 }, }; typedef struct myargs { int verbose; const char *logfile; + const char *raw_dev; pdd_handling_t pdd_handling; int seqnum; @@ -168,7 +178,9 @@ parse_opt(int key, char *arg, struct argp_state *state) "Supported sides are '0' and '1'\n", arg); } break; - + case 'r': + args->raw_dev = arg; + break; case ARGP_KEY_ARG: /* input file */ args->fp_in = fopen(arg, "r"); if ((args->fp_in) == NULL) { @@ -212,9 +224,10 @@ int main (int argc, char** argv) .verbose = 0, .seqnum = -1, .complete = 0, - .logfile = "/tmp/pfiflash.log", + .logfile = NULL, /* "/tmp/pfiflash.log", */ .pdd_handling = PDD_KEEP, - .fp_in = stdin, + .fp_in = stdin, + .raw_dev = NULL, }; argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &args); @@ -227,8 +240,16 @@ int main (int argc, char** argv) goto err; } - rc = pfiflash(args.fp_in, args.complete, args.seqnum, - args.pdd_handling, err_buf, PFIFLASH_MAX_ERR_BUF_SIZE); + 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; } @@ -238,6 +259,6 @@ int main (int argc, char** argv) fclose(args.fp_in); err: if (rc != 0) - err_msg("Error: %s\nrc: %d\n", err_buf, rc); + 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 index fc2eede..a063e7f 100644 --- a/ubi-utils/src/pfiflash.h +++ b/ubi-utils/src/pfiflash.h @@ -44,6 +44,19 @@ typedef enum pdd_handling_t 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. diff --git a/ubi-utils/src/pfiflash_error.h b/ubi-utils/src/pfiflash_error.h new file mode 100644 index 0000000..34b705e --- /dev/null +++ b/ubi-utils/src/pfiflash_error.h @@ -0,0 +1,69 @@ +#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 + * Contact: Andreas Arnez + */ + +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 +}; + +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" +}; + +#endif /* __PFIFLASH_ERROR_H__ */ diff --git a/ubi-utils/src/reader.c b/ubi-utils/src/reader.c index 5de06d5..975caa1 100644 --- a/ubi-utils/src/reader.c +++ b/ubi-utils/src/reader.c @@ -29,10 +29,11 @@ #include #include -#include "config.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 @@ -152,6 +153,12 @@ read_pfi_raw(pfi_header pfi_hd, FILE* fp_pfi __unused, pfi_raw_t* pfi_raw, 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) { @@ -212,6 +219,12 @@ read_pfi_ubi(pfi_header pfi_hd, FILE* fp_pfi __unused, pfi_ubi_t* pfi_ubi, 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."); diff --git a/ubi-utils/src/reader.h b/ubi-utils/src/reader.h index d00fa17..715e464 100644 --- a/ubi-utils/src/reader.h +++ b/ubi-utils/src/reader.h @@ -50,6 +50,7 @@ struct pfi_raw { uint32_t data_size; uint32_t *starts; uint32_t starts_size; + uint32_t crc; }; struct pfi_ubi { @@ -63,6 +64,7 @@ struct pfi_ubi { 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, -- cgit v1.2.3 From e820054fd876faa7306d64957c71c9c5c29db78c Mon Sep 17 00:00:00 2001 From: Drake Dowsett Date: Mon, 6 Nov 2006 16:54:10 +0100 Subject: [MTD] UBI: rework of off-line UBI analysis tool The new version can create a gnuplot graph of the erase count statistics. It can also extract UBI volumes and single blocks with a preanalysis of the EC as well as the VID header. It has a manual page too ;-). Signed-off-by: Frank Haverkamp --- ubi-utils/Makefile | 2 +- ubi-utils/doc/unubi.roff | 123 ++++++ ubi-utils/src/eb_chain.c | 287 +++++++++++++ ubi-utils/src/eb_chain.h | 73 ++++ ubi-utils/src/unubi.c | 975 +++++++++++++++++++++++++++++++----------- ubi-utils/src/unubi_analyze.c | 435 +++++++++++++++++++ ubi-utils/src/unubi_analyze.h | 26 ++ 7 files changed, 1659 insertions(+), 262 deletions(-) create mode 100644 ubi-utils/doc/unubi.roff create mode 100644 ubi-utils/src/eb_chain.c create mode 100644 ubi-utils/src/eb_chain.h create mode 100644 ubi-utils/src/unubi_analyze.c create mode 100644 ubi-utils/src/unubi_analyze.h diff --git a/ubi-utils/Makefile b/ubi-utils/Makefile index 2776c07..0818a9b 100644 --- a/ubi-utils/Makefile +++ b/ubi-utils/Makefile @@ -71,7 +71,7 @@ ubigen: ubigen.o libubigen.o crc32.o mkbootenv: mkbootenv.o bootenv.o hashmap.o error.o crc32.o $(CC) $(LDFLAGS) -o $@ $^ -unubi: unubi.o crc32.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 \ 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 +.sp 0 +Drake Dowsett +.SH CONTACT +Andreas Arnez 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 +#include +#include +#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 +#include +#include + +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/unubi.c b/ubi-utils/src/unubi.c index 6d877e7..4b9ddfd 100644 --- a/ubi-utils/src/unubi.c +++ b/ubi-utils/src/unubi.c @@ -14,13 +14,23 @@ * 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 UBI images. Not yet finished ... */ -#include +/* + * Authors: Frank Haverkamp, haver@vnet.ibm.com + * Drake Dowsett, dowsett@de.ibm.com + */ + +/* + * 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 #include #include @@ -34,359 +44,802 @@ #include #include #include +#include #include "crc32.h" -#include +#include "eb_chain.h" +#include "unubi_analyze.h" + +#define EXEC "unubi" +#define CONTACT "haver@vnet.ibm.com" +#define VERSION "0.9" + +static char doc[] = "\nVersion: " VERSION "\n\t" + BUILD_OS" "BUILD_CPU" at "__DATE__" "__TIME__"\n" + "\nAnalyze raw flash containing UBI data.\n"; + +#define ERR_MSG(fmt...) \ + fprintf(stderr, EXEC ": " fmt) -#define MAXPATH 1024 -#define MIN(x,y) ((x)<(y)?(x):(y)) +#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_%04u" /* 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 { - const char *output_dir; - uint32_t hdr_offs; - uint32_t data_offs; - uint32_t blocksize; - - /* special stuff needed to get additional arguments */ - char *arg1; - char **options; /* [STRING...] */ + uint32_t bsize; + int analyze; + int eb_split; + int vol_split; + char *odir_path; + char *img_path; + uint32_t *vols; + + char **options; }; -static struct args myargs = { - .output_dir = "unubi", - .hdr_offs = 64, - .data_offs = 128, - .blocksize = 128 * 1024, - .arg1 = NULL, - .options = NULL, -}; +static error_t parse_opt(int key, char *arg, struct argp_state *state); -static error_t parse_opt (int key, char *arg, struct argp_state *state); - -const char *argp_program_bug_address = - "... uuuh, lets wait until it looks nicer"; - -static char doc[] = "\nVersion: " PACKAGE_VERSION "\n\t" - BUILD_OS" "BUILD_CPU" at "__DATE__" "__TIME__"\n" - "\nWrite to UBI Volume.\n"; +const char *argp_program_version = VERSION; +const char *argp_program_bug_address = CONTACT; static struct argp_option options[] = { - { .name = "dir", - .key = 'd', - .arg = "", - .flags = 0, - .doc = "output directory", - .group = OPTION_ARG_OPTIONAL }, - - { .name = "blocksize", - .key = 'b', - .arg = "", - .flags = 0, - .doc = "blocksize", - .group = OPTION_ARG_OPTIONAL }, - - { .name = "data-offs", - .key = 'x', - .arg = "", - .flags = 0, - .doc = "data offset", - .group = OPTION_ARG_OPTIONAL }, - - { .name = "hdr-offs", - .key = 'x', - .arg = "", - .flags = 0, - .doc = "hdr offset", - .group = OPTION_ARG_OPTIONAL }, - - { .name = NULL, .key = 0, .arg = NULL, .flags = 0, - .doc = NULL, .group = 0 }, + { + name: NULL, key: 0, arg: NULL, + flags: 0, group: 0, doc: "OPTIONS", + }, + { + name: "rebuild", key: 'r', arg: "", + flags: 0, group: 1, doc: "Extract and rebuild volume", + }, + { + name: "dir", key: 'd', arg: "", + flags: 0, group: 2, doc: "Specify output directory", + }, + { + name: "analyze", key: 'a', arg: NULL, + flags: 0, group: 3, doc: "Analyze image", + }, + { + name: "blocksize", key: 'b', arg: "", + flags: 0, group: 4, doc: "Specify size of eraseblocks " + "in image in bytes (default " + "128KiB)", + }, + { + name: "eb-split", key: 'e', arg: NULL, + flags: 0, group: 5, doc: "Generate individual eraseblock " + "images (all eraseblocks)", + }, + { + name: "vol-split", key: 'v', arg: NULL, + flags: 0, group: 5, doc: "Generate individual eraseblock " + "images (valid eraseblocks only)", + }, + { + name: "vol-split!", key: 'V', arg: NULL, + flags: 0, group: 5, doc: "Raw split by eraseblock " + "(valid eraseblocks only)", + }, + { + name: NULL, key: 0, arg: NULL, + flags: 0, group: 0, doc: NULL, + }, }; static struct argp argp = { .options = options, .parser = parse_opt, .args_doc = "image-file", - .doc = doc, + .doc = doc, .children = NULL, .help_filter = NULL, .argp_domain = NULL, }; -/* - * 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) + +/** + * 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 = str; - ulong num = strtoul(s, &s, 0); + char *s; + ulong num; + + s = str; + 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); - } + 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; } -/* - * @brief Parse the arguments passed into the test case. - * - * @param key The parameter. - * @param arg Argument passed to parameter. - * @param state Location to put information on parameters. - * - * @return error - * - * Get the `input' argument from `argp_parse', which we know is a - * pointer to our arguments structure. - */ + +/** + * parses the arguments passed into the program + * get the input argument from argp_parse, which we know is a + * pointer to our arguments structure; + **/ static error_t parse_opt(int key, char *arg, struct argp_state *state) { + uint32_t i; struct args *args = state->input; switch (key) { - case 'b': /* --blocksize */ - args->blocksize = str_to_num(arg); + case 'a': + args->analyze = 1; break; - - case 'x': /* --data-offs= */ - args->data_offs = str_to_num(arg); + case 'b': + args->bsize = str_to_num(arg); break; - - case 'y': /* --hdr-offs= */ - args->hdr_offs = str_to_num(arg); + case 'd': + args->odir_path = arg; break; - - case 'd': /* --dir= */ - args->output_dir = arg; + case 'e': + args->eb_split = SPLIT_RAW; + break; + case 'r': + i = str_to_num(arg); + if (i < UBI_MAX_VOLUMES) + args->vols[str_to_num(arg)] = 1; + else { + ERR_MSG("volume-id out of bounds\n"); + return ARGP_ERR_UNKNOWN; + } + break; + case 'v': + if (args->vol_split != SPLIT_RAW) + args->vol_split = SPLIT_DATA; + break; + case 'V': + args->vol_split = SPLIT_RAW; break; - case ARGP_KEY_NO_ARGS: - /* argp_usage(state); */ break; - case ARGP_KEY_ARG: - args->arg1 = arg; - /* Now we consume all the rest of the arguments. - `state->next' is the index in `state->argv' of the - next argument to be parsed, which is the first STRING - we're interested in, so we can just use - `&state->argv[state->next]' as the value for - arguments->strings. - - _In addition_, by setting `state->next' to the end - of the arguments, we can force argp to stop parsing - here and return. */ - + args->img_path = arg; args->options = &state->argv[state->next]; state->next = state->argc; break; - case ARGP_KEY_END: - /* argp_usage(state); */ break; - default: - return(ARGP_ERR_UNKNOWN); + return ARGP_ERR_UNKNOWN; } return 0; } -static inline void -hexdump(FILE *fp, const void *p, ssize_t size) + +/** + * 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) { - int k; - const uint8_t *buf = p; + size_t count, i; - for (k = 0; k < size; k++) { - fprintf(fp, "%02x ", buf[k]); - if ((k & 15) == 15) - fprintf(fp, "\n"); - } + if (full_array == NULL) + return 0; + + for (i = 0, count = 0; i < full_len; i++) + if (full_array[i] != 0) + count++; + + return count; } -/* - * This was put together in 1.5 hours and this is exactly how it looks - * like! FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME - * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME - * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME - * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME - * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME - * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME - * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME - * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME - * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME - * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME - * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME - * FIXME FIXME FIXME FIXME FIXME FIXME! - */ -static int extract_volume(struct args *args, const uint8_t *buf, - int len, int volume, FILE *fp) + +/** + * 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) { - int i, rc; - int nrblocks = len / args->blocksize; - int max_lnum = -1, lnum = 0; - const uint8_t *ptr; - uint8_t **vol_tab; - int *vol_len; + size_t i, j; - vol_tab = calloc(nrblocks, sizeof(uint8_t *)); - vol_len = calloc(nrblocks, sizeof(int)); + if ((full_array == NULL) || (coll_array == NULL)) + return 0; - if (!buf || !vol_tab || !vol_len) - exit(EXIT_FAILURE); + for (i = 0, j = 0; (i < full_len) && (j < coll_len); i++) + if (full_array[i] != 0) { + coll_array[j] = i; + j++; + } - for (i = 0, ptr = buf; i < nrblocks; i++, ptr += args->blocksize) { - uint32_t crc; - struct ubi_ec_hdr *ec_hdr = (struct ubi_ec_hdr *)ptr; - struct ubi_vid_hdr *vid_hdr = NULL; - uint8_t *data; + 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; +} - /* default */ - vol_len[lnum] = args->blocksize - (2 * 1024); - /* Check UBI EC header */ - crc = clc_crc32(crc32_table, UBI_CRC32_INIT, ec_hdr, - UBI_EC_HDR_SIZE_CRC); - if (crc != ubi32_to_cpu(ec_hdr->hdr_crc)) - continue; +/** + * 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; + } - vid_hdr = (struct ubi_vid_hdr *) - (ptr + ubi32_to_cpu(ec_hdr->vid_hdr_offset)); - data = (uint8_t *)(ptr + ubi32_to_cpu(ec_hdr->data_offset)); + rc = fsetpos(fpin, &start); + if (rc < 0) + return -1; - crc = clc_crc32(crc32_table, UBI_CRC32_INIT, vid_hdr, - UBI_VID_HDR_SIZE_CRC); - if (crc != ubi32_to_cpu(vid_hdr->hdr_crc)) - continue; + 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; + } - if (volume == (int)ubi32_to_cpu(vid_hdr->vol_id)) { + 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; + } + } - printf("****** block %4d volume %2d **********\n", - i, volume); + err: + if (fpout != NULL) + fclose(fpout); + return rc; +} - hexdump(stdout, ptr, 64); - printf("--- vid_hdr\n"); - hexdump(stdout, vid_hdr, 64); +/** + * 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; - printf("--- data\n"); - hexdump(stdout, data, 64); + rc = 0; - lnum = ubi32_to_cpu(vid_hdr->lnum); - vol_tab[lnum] = data; - if (max_lnum < lnum) - max_lnum = lnum; - if (vid_hdr->vol_type == UBI_VID_STATIC) - vol_len[lnum] = - ubi32_to_cpu(vid_hdr->data_size); + 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; } } - for (lnum = 0; lnum <= max_lnum; lnum++) { - if (vol_tab[lnum]) { - rc = fwrite(vol_tab[lnum], 1, vol_len[lnum], fp); - if (ferror(fp) || (vol_len[lnum] != rc)) { - perror("could not write file"); - exit(EXIT_FAILURE); + 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; } - } else { - /* Fill up empty areas by 0xff, for static - * volumes this means they are broken! - */ - for (i = 0; i < vol_len[lnum]; i++) { - if (fputc(0xff, fp) == EOF) { - perror("could not write char"); - exit(EXIT_FAILURE); - } + } + + 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++; } - free(vol_tab); - free(vol_len); - return 0; + out: + if (vol_id == NULL) + fclose(fpout); + return rc; } -int -main(int argc, char *argv[]) + +/** + * 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) { - int len, rc; - FILE *fp; - struct stat file_info; - uint8_t *buf; - int i; + 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)); - init_crc32_table(crc32_table); + 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; + } - argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &myargs); + /* 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; - if (!myargs.arg1) { - fprintf(stderr, "Please specify input file!\n"); - exit(EXIT_FAILURE); + 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)); } - fp = fopen(myargs.arg1, "r"); - if (!fp) { - perror("Cannot open file"); - exit(EXIT_FAILURE); + out: + for (i = 0; i < vc; i++) { + rc = rebuild_volume(fpin, &vols[i], &head, a->odir_path); + if (rc < 0) + goto err; } - if (fstat(fileno(fp), &file_info) != 0) { - fprintf(stderr, "Cannot fetch file size " - "from input file.\n"); + + /* 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; } - len = file_info.st_size; - buf = malloc(len); - if (!buf) { - perror("out of memory!"); - exit(EXIT_FAILURE); + + 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; } - rc = fread(buf, 1, len, fp); - if (ferror(fp) || (len != rc)) { - perror("could not read file"); - exit(EXIT_FAILURE); + memset(a.vols, 0, sizeof(*a.vols) * UBI_MAX_VOLUMES); + + /* parse args and check for validity */ + argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &a); + if (a.img_path == NULL) { + ERR_MSG("no image file specified\n"); + rc = EINVAL; + goto err; } - if (!myargs.output_dir) { - fprintf(stderr, "No output directory specified!\n"); - exit(EXIT_FAILURE); + 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(myargs.output_dir, 0777); - if (rc && errno != EEXIST) { - perror("Cannot create output directory"); - exit(EXIT_FAILURE); + 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; } - for (i = 0; i < 32; i++) { - char fname[1024]; - FILE *fpout; - - printf("######### VOLUME %d ############################\n", - i); - - sprintf(fname, "%s/ubivol_%d.bin", myargs.output_dir, i); - fpout = fopen(fname, "w+"); - if (!fpout) { - perror("Cannot open file"); - exit(EXIT_FAILURE); + + /* 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; } - extract_volume(&myargs, buf, len, i, fpout); - fclose(fpout); + 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; } - fclose(fp); - free(buf); - exit(EXIT_SUCCESS); + 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..2e94ca9 --- /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 +#include +#include +#include +#include +#include + +#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, "%u %llu %llu", count, + ubi64_to_cpu(cur->outer.ec), + 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, "%d", count); + } + + cur = cur->next; + count++; + } + fprintf(fpplot, ")\n"); + + fprintf(fpplot, "set ylabel \"erase count\"\n"); + fprintf(fpplot, "set xrange [-1:%u]\n", eraseblocks + 1); + fprintf(fpplot, "# set yrange [-1:%llu]\n", + 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, "%u %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, "\"%d\" %d 0", step, count); + } + else + fprintf(fpplot, "\"%d\" %d 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:%u]\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); -- cgit v1.2.3 From c61aca05ac6fe2dba46a133c7c72aec8f7e3ae5b Mon Sep 17 00:00:00 2001 From: Josh Boyer Date: Tue, 5 Dec 2006 15:56:29 -0600 Subject: [PATCH] Try 2: Remove bogus copyright statement Signed-off-by: Josh Boyer Signed-off-by: Frank Haverkamp --- ubi-utils/src/pfi2bin.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/ubi-utils/src/pfi2bin.c b/ubi-utils/src/pfi2bin.c index 4c25faf..ec09a4d 100644 --- a/ubi-utils/src/pfi2bin.c +++ b/ubi-utils/src/pfi2bin.c @@ -64,12 +64,7 @@ typedef enum action_t { } action_t; static const char copyright [] __attribute__((unused)) = - "Licensed Materials - Property of IBM\n" - "IBM Flexible Support Processor Licensed Material\n" - "(c) Copyright IBM Corp 2006 All Rights Reserved.\n" - "US Government Users Restricted Rights - Use, duplication\n" - "or disclosure restricted by GSA ADP Schedule Contract\n" - "with IBM Corp."; + "(c) Copyright IBM Corp 2006\n"; static error_t parse_opt (int key, char *arg, struct argp_state *state); -- cgit v1.2.3 From 0fc595749f057629a3af9506ca0447e6230c0962 Mon Sep 17 00:00:00 2001 From: Josh Boyer Date: Mon, 11 Dec 2006 14:34:22 +0100 Subject: [PATCH 1/13] Convert bin2nand to use getopt option parsing Signed-off-by: Josh Boyer Acked-by: Frank Haverkamp --- ubi-utils/src/bin2nand.c | 175 +++++++++++++++++++++-------------------------- 1 file changed, 79 insertions(+), 96 deletions(-) diff --git a/ubi-utils/src/bin2nand.c b/ubi-utils/src/bin2nand.c index 26d78de..df838af 100644 --- a/ubi-utils/src/bin2nand.c +++ b/ubi-utils/src/bin2nand.c @@ -35,7 +35,6 @@ #include #include #include -#include #include #include #include @@ -63,62 +62,47 @@ typedef enum action_t { #define PADDING 0 /* 0 means, do not adjust anything */ #define BUFSIZE 4096 -const char *argp_program_version = PACKAGE_VERSION; -const char *argp_program_bug_address = PACKAGE_BUGREPORT; +extern char *optarg; +extern int optind; + static char doc[] = "\nVersion: " PACKAGE_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 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 = "pagesize", - .key = 'p', - .arg = "", - .flags = 0, - .doc = "Pagesize in Byte/Mi/ki. Default = 2048", - .group = 1 }, - - { .name = "padding", - .key = 'j', - .arg = "", - .flags = 0, - .doc = "Padding in Byte/Mi/ki. Default = no padding", - .group = 1 }, - - { .name = "output", - .key = 'o', - .arg = "", - .flags = 0, - .doc = "Output filename. Interleaved Data/OOB if output-oob not " - "specified.", - .group = 2 }, - - { .name = "output-oob", - .key = 'q', - .arg = "", - .flags = 0, - .doc = "Write OOB data in separate file.", - .group = 2 }, - - { .name = NULL, - .key = 0, - .arg = NULL, - .flags = 0, - .doc = NULL, - .group = 0 }, +static const char *optionsstr = +" -c, --copyright Print copyright informatoin.\n" +" -j, --padding= Padding in Byte/Mi/ki. Default = no padding\n" +" -p, --pagesize= Pagesize in Byte/Mi/ki. Default = 2048\n" +" -o, --output= Output filename. Interleaved Data/OOB if\n" +" output-oob not specified.\n" +" -q, --output-oob= 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 ] [-p ] [-o ] [-q ]\n" +" [--copyright] [--padding=] [--pagesize=]\n" +" [--output=] [--output-oob=] [--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; @@ -155,62 +139,61 @@ static int ustrtoull(const char *cp, char **endp, unsigned int base) return res; } -static error_t -parse_opt(int key, char *arg, struct argp_state *state) +static int +parse_opt(int argc, char **argv, myargs *args) { - int err = 0; char* endp; - myargs *args = state->input; - - switch (key) { - case 'p': /* pagesize */ - args->pagesize = (size_t) ustrtoull(arg, &endp, 0); - CHECK_ENDP("p", endp); - break; - case 'j': /* padding */ - args->padding = (size_t) ustrtoull(arg, &endp, 0); - CHECK_ENDP("j", endp); - break; - case 'o': /* output */ - args->file_out_data = arg; - break; - case 'q': /* output oob */ - args->file_out_oob = arg; - break; - case ARGP_KEY_ARG: /* input file */ - args->fp_in = fopen(arg, "rb"); - if ((args->fp_in) == NULL) { - err_quit("Cannot open file %s for input\n", arg); + 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", PACKAGE_VERSION); + exit(0); + break; + case 'c': + printf("%s\n", copyright); + exit(0); + default: + printf("%s", usage); + exit(-1); } - args->arg1 = arg; - args->options = &state->argv[state->next]; - state->next = state->argc; - break; - case ARGP_KEY_END: - if (err) { - err_msg("\n"); - argp_usage(state); - exit(EXIT_FAILURE); + } + + 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++]); } - break; - default: - return(ARGP_ERR_UNKNOWN); } return 0; } -static struct argp argp = { - options: options, - parser: parse_opt, - args_doc: 0, - doc: doc, - children: NULL, - help_filter: NULL, - argp_domain: NULL, -}; - static int process_page(uint8_t* buf, size_t pagesize, FILE *fp_data, FILE* fp_oob, size_t* written) @@ -274,7 +257,7 @@ int main (int argc, char** argv) FILE* fp_out_data = stdout; FILE* fp_out_oob = NULL; - argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &args); + parse_opt(argc, argv, &args); uint8_t* buf = calloc(1, BUFSIZE); if (!buf) { -- cgit v1.2.3 From 371c7834a0ed0189b0489e194788fb1ee15eab33 Mon Sep 17 00:00:00 2001 From: Josh Boyer Date: Mon, 11 Dec 2006 14:34:22 +0100 Subject: [PATCH 2/13] Convert mkbootenv to use getopt option parsing Signed-off-by: Josh Boyer Acked-by: Frank Haverkamp --- ubi-utils/src/mkbootenv.c | 132 ++++++++++++++++++++++------------------------ 1 file changed, 63 insertions(+), 69 deletions(-) diff --git a/ubi-utils/src/mkbootenv.c b/ubi-utils/src/mkbootenv.c index 49ce597..a556939 100644 --- a/ubi-utils/src/mkbootenv.c +++ b/ubi-utils/src/mkbootenv.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include @@ -33,8 +32,9 @@ #include "bootenv.h" #include "error.h" -const char *argp_program_version = PACKAGE_VERSION; -const char *argp_program_bug_address = PACKAGE_BUGREPORT; +extern char *optarg; +extern int optind; + static char doc[] = "\nVersion: " PACKAGE_VERSION "\n\tBuilt on " BUILD_CPU" "BUILD_OS" at "__DATE__" "__TIME__"\n" "\n" @@ -44,25 +44,25 @@ static char doc[] = "\nVersion: " PACKAGE_VERSION "\n\tBuilt on " static const char copyright [] __attribute__((unused)) = "Copyright (c) International Business Machines Corp., 2006"; -static struct argp_option options[] = { - { .name = "copyright", - .key = 'c', - .arg = NULL, - .flags = 0, - .doc = "Print copyright information.", - .group = 1 }, - { .name = "output", - .key = 'o', - .arg = "", - .flags = 0, - .doc = "Write the the output data to instead of stdout.", - .group = 1 }, - { .name = NULL, - .key = 0, - .arg = NULL, - .flags = 0, - .doc = NULL, - .group = 0 }, +static const char *optionsstr = +" -c, --copyright Print copyright informatoin.\n" +" -o, --output= Write the output data to 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 ] [--copyright] [--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 { @@ -73,63 +73,57 @@ typedef struct myargs { char **options; /* [STRING...] */ } myargs; - - -static error_t -parse_opt(int key, char *arg, struct argp_state *state) +static int +parse_opt(int argc, char **argv, myargs *args) { - int err = 0; - - myargs *args = state->input; - - switch (key) { - case 'c': - fprintf(stderr, "%s\n", copyright); - exit(0); - break; - case 'o': - args->fp_out = fopen(arg, "wb"); - if ((args->fp_out) == NULL) { - fprintf(stderr, - "Cannot open file %s for output\n", arg); - exit(1); + 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", PACKAGE_VERSION); + exit(0); + break; + default: + printf("%s", usage); + exit(-1); } - break; - case ARGP_KEY_ARG: - args->fp_in = fopen(arg, "rb"); + } + + if (optind < argc) { + args->fp_in = fopen(argv[optind++], "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); + "Cannot open file %s for input\n", argv[optind]); exit(1); } - break; - default: - return(ARGP_ERR_UNKNOWN); } return 0; } -static struct argp argp = { - .options = options, - .parser = parse_opt, - .args_doc = "[bootenv-txt-file]", - .doc = doc, - .children = NULL, - .help_filter = NULL, - .argp_domain = NULL, -}; - int main(int argc, char **argv) { int rc = 0; @@ -142,7 +136,7 @@ main(int argc, char **argv) { .options = NULL, }; - argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &args); + parse_opt(argc, argv, &args); rc = bootenv_create(&env); if (rc != 0) { -- cgit v1.2.3 From 8e584d752fb7fb40fe0dfa949c74d2522503d850 Mon Sep 17 00:00:00 2001 From: Josh Boyer Date: Mon, 11 Dec 2006 14:34:22 +0100 Subject: [PATCH 3/13] Convert nand2bin to use getopt option parsing Signed-off-by: Josh Boyer Acked-by: Frank Haverkamp --- ubi-utils/src/nand2bin.c | 148 +++++++++++++++++++++-------------------------- 1 file changed, 65 insertions(+), 83 deletions(-) diff --git a/ubi-utils/src/nand2bin.c b/ubi-utils/src/nand2bin.c index a5e8bca..a9b68bd 100644 --- a/ubi-utils/src/nand2bin.c +++ b/ubi-utils/src/nand2bin.c @@ -20,7 +20,6 @@ * An utility to decompose NAND images and strip OOB off. Not yet finished ... */ #include -#include #include #include #include @@ -40,6 +39,9 @@ #define MAXPATH 1024 #define MIN(x,y) ((x)<(y)?(x):(y)) +extern char *optarg; +extern int optind; + struct args { const char *oob_file; const char *output_file; @@ -58,48 +60,29 @@ static struct args myargs = { .options = NULL, }; -static error_t parse_opt (int key, char *arg, struct argp_state *state); - -const char *argp_program_bug_address = PACKAGE_BUGREPORT; - static char doc[] = "\nVersion: " PACKAGE_VERSION "\n\t" BUILD_OS" "BUILD_CPU" at "__DATE__" "__TIME__"\n" "\nSplit data and OOB.\n"; -static struct argp_option options[] = { - { .name = "pagesize", - .key = 'p', - .arg = "", - .flags = 0, - .doc = "NAND pagesize", - .group = OPTION_ARG_OPTIONAL }, - - { .name = "output", - .key = 'o', - .arg = "", - .flags = 0, - .doc = "Data output file", - .group = OPTION_ARG_OPTIONAL }, - - { .name = "oob", - .key = 'O', - .arg = "", - .flags = 0, - .doc = "OOB output file", - .group = OPTION_ARG_OPTIONAL }, - - { .name = NULL, .key = 0, .arg = NULL, .flags = 0, - .doc = NULL, .group = 0 }, -}; - -static struct argp argp = { - .options = options, - .parser = parse_opt, - .args_doc = "input.mif", - .doc = doc, - .children = NULL, - .help_filter = NULL, - .argp_domain = NULL, +static const char *optionsstr = +" -o, --output= Data output file\n" +" -O, --oob= OOB output file\n" +" -p, --pagesize= NAND pagesize\n" +" -?, --help Give this help list\n" +" --usage Give a short usage message\n"; + +static const char *usage = +"Usage: nand2bin [-?] [-o ] [-O ] [-p ]\n" +" [--output=] [--oob=] [--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} }; /* @@ -137,52 +120,51 @@ uint32_t str_to_num(char *str) * Get the `input' argument from `argp_parse', which we know is a * pointer to our arguments structure. */ -static error_t -parse_opt(int key, char *arg, struct argp_state *state) +static int +parse_opt(int argc, char **argv, struct args *args) { - struct args *args = state->input; - - switch (key) { - case 'p': /* --pagesize */ - args->pagesize = str_to_num(arg); break; - - case 'o': /* --output= */ - args->output_file = arg; - break; - - case 'O': /* --oob= */ - args->output_file = arg; - break; - - case ARGP_KEY_NO_ARGS: - /* argp_usage(state); */ - break; - - case ARGP_KEY_ARG: - args->arg1 = arg; - /* Now we consume all the rest of the arguments. - `state->next' is the index in `state->argv' of the - next argument to be parsed, which is the first STRING - we're interested in, so we can just use - `&state->argv[state->next]' as the value for - arguments->strings. - - _In addition_, by setting `state->next' to the end - of the arguments, we can force argp to stop parsing here and - return. */ - - args->options = &state->argv[state->next]; - state->next = state->argc; - break; - - case ARGP_KEY_END: - /* argp_usage(state); */ - break; - - default: - return(ARGP_ERR_UNKNOWN); + while (1) { + int key; + + key = getopt_long(argc, argv, "o:O:p:?", long_options, NULL); + if (key == -1) + break; + + switch (key) { + case 'p': /* --pagesize */ + args->pagesize = str_to_num(optarg); + break; + + case 'o': /* --output= */ + args->output_file = optarg; + break; + + case 'O': /* --oob= */ + 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", PACKAGE_VERSION); + exit(0); + break; + + default: + printf("%s", usage); + exit(-1); + } } + if (optind < argc) + args->arg1 = argv[optind++]; + return 0; } @@ -295,7 +277,7 @@ main(int argc, char *argv[]) { FILE *in, *bin, *oob; - argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &myargs); + parse_opt(argc, argv, &myargs); if (!myargs.arg1) { fprintf(stderr, "Please specify input file!\n"); -- cgit v1.2.3 From 1fca362fc54052808008872050345ab2c2a6e280 Mon Sep 17 00:00:00 2001 From: Josh Boyer Date: Mon, 11 Dec 2006 14:34:22 +0100 Subject: [PATCH 4/13] Convert pddcustomize to use getopt option parsing Signed-off-by: Josh Boyer Acked-by: Frank Haverkamp --- ubi-utils/src/pddcustomize.c | 190 +++++++++++++++++++++---------------------- 1 file changed, 94 insertions(+), 96 deletions(-) diff --git a/ubi-utils/src/pddcustomize.c b/ubi-utils/src/pddcustomize.c index f71d916..5d1864a 100644 --- a/ubi-utils/src/pddcustomize.c +++ b/ubi-utils/src/pddcustomize.c @@ -30,7 +30,6 @@ #include #include #include -#include #include #include #include @@ -50,57 +49,56 @@ typedef enum action_t { } action_t; #define ABORT_ARGP do { \ - state->next = state->argc; \ args->action = ACT_ARGP_ABORT; \ } while (0) #define ERR_ARGP do { \ - state->next = state->argc; \ args->action = ACT_ARGP_ERR; \ } while (0) -const char *argp_program_version = PACKAGE_VERSION; -const char *argp_program_bug_address = PACKAGE_BUGREPORT; +extern char *optarg; +extern int optind; + static char doc[] = "\nVersion: " PACKAGE_VERSION "\n\tBuilt on " BUILD_CPU" "BUILD_OS" at "__DATE__" "__TIME__"\n" "\n" "pddcustomize - customize bootenv and pdd values.\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: "input", key: 'i', arg: "", flags: 0, - doc: "Binary input file. For debug purposes.", - group: 1 }, - - { name: "output", key: 'o', arg: "", flags: 0, - doc: "Binary output file. For debug purposes.", - group: 1 }, - - { name: "list", key: 'l', arg: NULL, flags: 0, - doc: "List card bootenv/pdd values.", - group: 1 }, - - { name: "both", key: 'b', arg: NULL, flags: 0, - doc: "Mirror updated PDD to redundand copy.", - group: 1 }, - - { name: "side", key: 's', arg: "", flags: 0, - doc: "The side/seqnum to update.", - group: 1 }, - - { name: "host", key: 'x', arg: NULL, flags: 0, - doc: "use x86 platform for debugging.", - group: 1 }, - - { name: NULL, key: 0, arg: NULL, flags: 0, doc: NULL, group: 0 }, +static const char *optionsstr = +" -b, --both Mirror updated PDD to redundand copy.\n" +" -c, --copyright Print copyright information.\n" +" -i, --input= Binary input file. For debug purposes.\n" +" -l, --list List card bootenv/pdd values.\n" +" -o, --output= Binary output file. For debug purposes.\n" +" -s, --side= 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.orig [-bclx?V] [-i ] [-o ] [-s ]\n" +" [--both] [--copyright] [--input=] [--list]\n" +" [--output=] [--side=] [--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; @@ -154,73 +152,73 @@ err: return rc; } -static error_t -parse_opt(int key, char *arg, struct argp_state *state) +static int +parse_opt(int argc, char **argv, myargs *args) { int rc = 0; - int err = 0; - - myargs *args = state->input; - - 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(arg); - if (args->side < 0) { - err_msg("Unsupported seqnum: %d.\n" - "Supported seqnums are '0' and '1'\n", - args->side, arg); - ERR_ARGP; + + 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.orig [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", PACKAGE_VERSION); + exit(0); + break; + default: + err_msg("%s", usage); + exit(-1); } - break; - case 'i': - args->file_in = arg; - break; - case 'o': - args->file_out = arg; - break; - case ARGP_KEY_ARG: - rc = extract_pair(args->env_in, arg); + } + + if (optind < argc) { + rc = extract_pair(args->env_in, argv[optind++]); if (rc != 0) ERR_ARGP; - break; - case ARGP_KEY_END: - if (err) { - err_msg("\n"); - argp_usage(state); - ERR_ARGP; - } - break; - default: - return(ARGP_ERR_UNKNOWN); } return 0; } -static struct argp argp = { - options: options, - parser: parse_opt, - args_doc: "[key=value] [...]", - doc: doc, - children: NULL, - help_filter: NULL, - argp_domain: NULL, -}; - - static int list_bootenv(bootenv_t env) { @@ -442,7 +440,7 @@ main(int argc, char **argv) { goto err; } - argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &args); + parse_opt(argc, argv, &args); if (args.action == ACT_ARGP_ERR) { rc = -1; goto err; -- cgit v1.2.3 From 6d6841c32370763e585edb52465ad2eb65354f7d Mon Sep 17 00:00:00 2001 From: Josh Boyer Date: Mon, 11 Dec 2006 14:34:22 +0100 Subject: [PATCH 5/13] Convert pfi2bin to use getopt option parsing Signed-off-by: Josh Boyer Acked-by: Frank Haverkamp --- ubi-utils/src/pfi2bin.c | 168 +++++++++++++++++++++++------------------------- 1 file changed, 81 insertions(+), 87 deletions(-) diff --git a/ubi-utils/src/pfi2bin.c b/ubi-utils/src/pfi2bin.c index ec09a4d..044fa24 100644 --- a/ubi-utils/src/pfi2bin.c +++ b/ubi-utils/src/pfi2bin.c @@ -28,9 +28,9 @@ #include #include #include -#include #include #include +#include #include #include @@ -50,6 +50,9 @@ #define MIN(a,b) ((a) < (b) ? (a) : (b)) +extern char *optarg; +extern int optind; + static uint32_t crc32_table[256]; static char err_buf[ERR_BUF_SIZE]; @@ -66,48 +69,40 @@ typedef enum action_t { static const char copyright [] __attribute__((unused)) = "(c) Copyright IBM Corp 2006\n"; -static error_t parse_opt (int key, char *arg, struct argp_state *state); - -const char *argp_program_version = PROGRAM_VERSION; -const char *argp_program_bug_address = PACKAGE_BUGREPORT; 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 struct argp_option options[] = { - /* COMMON */ - { name: NULL, key: 0, arg: NULL, flags: 0, - doc: "Common settings:", - group: OPTION_ARG_OPTIONAL}, - - { name: "verbose", key: 'v', arg: NULL, flags: 0, - doc: "Print more information.", - group: OPTION_ARG_OPTIONAL }, - - { name: "copyright", key: 'c', arg: NULL, flags: 0, - group: OPTION_ARG_OPTIONAL }, - - - /* INPUT */ - { name: NULL, key: 0, arg: NULL, flags: 0, - doc: "Input:", - group: 4}, - - { name: "platform", key: 'j', arg: "pdd-file", flags: 0, - doc: "PDD information which contains the card settings.", - group: 4 }, - - /* OUTPUT */ - { name: NULL, key: 0, arg: NULL, flags: 0, - doc: "Output:", - group: 5}, - - { name: "output", key: 'o', arg: "filename", flags: 0, - doc: "Outputfile, default: stdout.", - group: 5 }, - - { name: NULL, key: 0, arg: NULL, flags: 0, doc: NULL, group: 0 }, +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.orig [-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 { @@ -129,58 +124,57 @@ typedef struct myargs { char **options; /* [STRING...] */ } myargs; -static struct argp argp = { - options: options, - parser: parse_opt, - args_doc: "pfifile", - doc: doc, - children: NULL, - help_filter: NULL, - argp_domain: NULL, -}; - -static error_t -parse_opt(int key, char *arg, struct argp_state *state) +static int +parse_opt(int argc, char **argv, myargs *args) { - myargs *args = state->input; - - switch (key) { - /* common settings */ - case 'v': /* --verbose= */ - args->verbose = 1; - break; - - case 'c': /* --copyright */ - fprintf(stderr, "%s\n", copyright); - exit(0); - break; - - case 'j': /* --platform */ - args->f_in_pdd = arg; - break; - - case 'o': /* --output */ - args->f_out = arg; - break; - - case ARGP_KEY_ARG: - args->f_in_pfi = arg; - /* args->arg1 = arg; */ - args->options = &state->argv[state->next]; - state->next = state->argc; - break; - - case ARGP_KEY_END: - if (args->action == ACT_NOTHING) { - argp_usage(state); - exit(1); + 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= */ + 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", PACKAGE_VERSION); + exit(0); + break; + + default: + printf("%s", usage); + exit(-1); } - break; - - default: - return(ARGP_ERR_UNKNOWN); } + if (optind < argc) + args->f_in_pfi = argv[optind++]; + return 0; } @@ -658,7 +652,7 @@ main(int argc, char *argv[]) }; /* parse arguments */ - argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &args); + parse_opt(argc, argv, &args); if (strcmp(args.f_in_pfi, "") == 0) { err_quit("No PFI input file specified!"); -- cgit v1.2.3 From 1bc3dab78a9a0b08dd10742ee671f180c1b9d77a Mon Sep 17 00:00:00 2001 From: Josh Boyer Date: Mon, 11 Dec 2006 14:34:23 +0100 Subject: [PATCH 6/13] Convert pfiflash to use getopt option parsing Signed-off-by: Josh Boyer Acked-by: Frank Haverkamp --- ubi-utils/src/pfiflash.c | 217 +++++++++++++++++++++++------------------------ 1 file changed, 105 insertions(+), 112 deletions(-) diff --git a/ubi-utils/src/pfiflash.c b/ubi-utils/src/pfiflash.c index c49fb1e..c654be9 100644 --- a/ubi-utils/src/pfiflash.c +++ b/ubi-utils/src/pfiflash.c @@ -31,7 +31,6 @@ #include #include #include -#include #include #include @@ -42,57 +41,53 @@ #define PROGRAM_VERSION "1.2" -const char *argp_program_version = PROGRAM_VERSION; -const char *argp_program_bug_address = PACKAGE_BUGREPORT; +extern char *optarg; +extern int optind; + 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 copyright [] __attribute__((unused)) = - "FIXME: insert license type."; /* FIXME */ - -static struct argp_option options[] = { - /* Output options */ - { name: NULL, key: 0, arg: NULL, flags: 0, - doc: "Standard options:", - group: 1 }, - - { name: "copyright", key: 'c', arg: NULL, flags: 0, - doc: "Print copyright information.", - group: 1 }, - - { name: "verbose", key: 'v', arg: NULL, flags: 0, - doc: "Be verbose during program execution.", - group: 1 }, - - { name: "logfile", key: 'l', arg: "", flags: 0, - doc: "Write a logfile to .", - group: 1 }, - - /* Output options */ - { name: NULL, key: 0, arg: NULL, flags: 0, - doc: "Process options:", - group: 2 }, - - { name: "complete", key: 'C', arg: NULL, flags: 0, - doc: "Execute a complete system update. Updates both sides.", - group: 2 }, - - { name: "side", key: 's', arg: "", flags: 0, - doc: "Select the side which shall be updated.", - group: 2 }, - - { name: "pdd-update", key: 'p', arg: "", flags: 0, - doc: "Specify the pdd-update algorithm. is either " - "'keep', 'merge' or 'overwrite'.", - group: 2 }, +static const char *optionsstr = +" Standard options:\n" +" -c, --copyright Print copyright information.\n" +" -l, --logfile= Write a logfile to .\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= Specify the pdd-update algorithm. is either\n" +" 'keep', 'merge' or 'overwrite'.\n" +" -r, --raw-flash= Flash the raw data. Use the specified mtd device.\n" +" -s, --side= 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.orig [-cvC?V] [-l ] [-p ] [-r ] [-s ]\n" +" [--copyright] [--logfile=] [--verbose] [--complete]\n" +" [--pdd-update=] [--raw-flash=] [--side=]\n" +" [--help] [--usage] [--version] [pfifile]\n"; - { name: "raw-flash", key: 'r', arg: "", flags: 0, - doc: "Flash the raw data. Use the specified mtd device.", - group: 2 }, - - { name: NULL, key: 0, arg: NULL, flags: 0, doc: NULL, group: 0 }, +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 { @@ -140,80 +135,78 @@ get_update_seqnum(const char* str) } -static error_t -parse_opt(int key, char *arg, struct argp_state *state) +static int +parse_opt(int argc, char **argv, myargs *args) { - int err = 0; - - myargs *args = state->input; - - switch (key) { - /* standard options */ - case 'c': - err_msg("%s\n", copyright); - exit(0); - break; - case 'v': - args->verbose = 1; - break; - case 'l': - args->logfile = arg; - break; - /* process options */ - case 'C': - args->complete = 1; - break; - case 'p': - args->pdd_handling = get_pdd_handling(arg); - 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(arg); - if (args->seqnum < 0) { - err_quit("Unsupported side: %s.\n" - "Supported sides are '0' and '1'\n", arg); + 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", PACKAGE_VERSION); + exit(0); + break; + default: + err_msg("%s", usage); + exit(-1); + } - break; - case 'r': - args->raw_dev = arg; - break; - case ARGP_KEY_ARG: /* input file */ - args->fp_in = fopen(arg, "r"); + } + + if (optind < argc) { + args->fp_in = fopen(argv[optind++], "r"); if ((args->fp_in) == NULL) { - err_sys("Cannot open PFI file %s for input", arg); - } - args->arg1 = arg; - args->options = &state->argv[state->next]; - state->next = state->argc; - break; - case ARGP_KEY_END: - if (err) { - err_msg("\n"); - argp_usage(state); - exit(1); + err_sys("Cannot open PFI file %s for input", argv[optind]); } - break; - default: - return(ARGP_ERR_UNKNOWN); } return 0; } -static struct argp argp = { - options: options, - parser: parse_opt, - args_doc: "[pfifile]", - doc: doc, - children: NULL, - help_filter: NULL, - argp_domain: NULL, -}; - int main (int argc, char** argv) { int rc = 0; @@ -230,7 +223,7 @@ int main (int argc, char** argv) .raw_dev = NULL, }; - argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &args); + parse_opt(argc, argv, &args); error_initlog(args.logfile); if (!args.fp_in) { -- cgit v1.2.3 From 5b766e1b61ae0d525d7ef004014e0fdb7276eb24 Mon Sep 17 00:00:00 2001 From: Josh Boyer Date: Mon, 11 Dec 2006 14:34:23 +0100 Subject: [PATCH 7/13] Convert ubigen to use getopt option parsing Signed-off-by: Josh Boyer Acked-by: Frank Haverkamp --- ubi-utils/src/ubigen.c | 349 ++++++++++++++++++++++++------------------------- 1 file changed, 168 insertions(+), 181 deletions(-) diff --git a/ubi-utils/src/ubigen.c b/ubi-utils/src/ubigen.c index 8a464dd..226834f 100644 --- a/ubi-utils/src/ubigen.c +++ b/ubi-utils/src/ubigen.c @@ -27,7 +27,6 @@ #include #include #include -#include #include #include #include @@ -40,16 +39,79 @@ typedef enum action_t { ACT_BROKEN_UPDATE = 0x00000002, } action_t; +extern char *optarg; +extern int optind; -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" "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= Set the alignment size to (default 1).\n" +" Values can be specified as bytes, 'ki' or 'Mi'.\n" +" -B, --blocksize= Set the eraseblock size to (default 128\n" +" KiB).\n" +" Values can be specified as bytes, 'ki' or 'Mi'.\n" +" -E, --erasecount= Set the erase count to (default 0)\n" +" -I, --id= The UBI volume id.\n" +" -O, --offset= Offset from start of an erase block to the UBI\n" +" volume header.\n" +" -T, --type= The UBI volume type:\n" +" 1 = dynamic, 2 = static\n" +" -X, --setver= Set UBI version number to (default 1)\n" +"\n" +" Input/Output:\n" +" -i, --infile= Read input from file.\n" +" -o, --outfile= Write output to file (default is stdout).\n" +"\n" +" Special options:\n" +" -U, --broken-update= Create an ubi image which simulates a broken\n" +" update.\n" +" 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.orig [-cdv?V] [-A ] [-B ] [-E ] [-I ]\n" +" [-O ] [-T ] [-X ] [-i ] [-o ]\n" +" [-U ] [--copyright] [--debug] [--verbose] [--alignment=]\n" +" [--blocksize=] [--erasecount=] [--id=]\n" +" [--offset=] [--type=] [--setver=]\n" +" [--infile=] [--outfile=]\n" +" [--broken-update=] [--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)) = - "FIXME: insert license type"; /* FIXME */ + "Copyright IBM Corp 2006"; #define CHECK_ENDP(option, endp) do { \ if (*endp) { \ @@ -61,86 +123,6 @@ static const char copyright [] __attribute__((unused)) = } \ } while(0) -static struct argp_option options[] = { - /* COMMON */ - { name: NULL, key: 0, arg: NULL, flags: 0, - doc: "Common settings:", - group: 1}, - - { name: "copyright", key: 'c', arg: NULL, flags: 0, - doc: "Print copyright information.", - group: 1 }, - - { name: "verbose", key: 'v', arg: NULL, flags: 0, - doc: "Print more progress information.", - group: 1 }, - - { name: "debug", key: 'd', arg: NULL, flags: 0, - group: 1 }, - - - /* INPUT */ - { name: NULL, key: 0, arg: NULL, flags: 0, - doc: "UBI Settings:", - group: 4}, - - { name: "alignment", key: 'A', arg: "", flags: 0, - doc: "Set the alignment size to (default 1).\n" - "Values can be specified as bytes, 'ki' or 'Mi'.", - group: 4 }, - - { name: "blocksize", key: 'B', arg: "", flags: 0, - doc: "Set the eraseblock size to (default 128 KiB).\n" - "Values can be specified as bytes, 'ki' or 'Mi'.", - group: 4 }, - - { name: "erasecount", key: 'E', arg: "", flags: 0, - doc: "Set the erase count to (default 0)", - group: 4 }, - - { name: "setver", key: 'X', arg: "", flags: 0, - doc: "Set UBI version number to (default 1)", - group: 4 }, - - { name: "id", key: 'I', arg: "", flags: 0, - doc: "The UBI volume id.", - group: 4 }, - - - { name: "offset", key: 'O', arg: "", flags: 0, - doc: "Offset from start of an erase block to the UBI volume header.", - group: 4 }, - - { name: "type", key: 'T', arg: "", flags: 0, - doc: "The UBI volume type:\n1 = dynamic, 2 = static", - group: 4 }, - - /* INPUT/OUTPUT */ - { name: NULL, key: 0, arg: NULL, flags: 0, - doc: "Input/Output:", - group: 5 }, - - { name: "infile", key: 'i', arg: "", flags: 0, - doc: "Read input from file.", - group: 5 }, - - { name: "outfile", key: 'o', arg: "", flags: 0, - doc: "Write output to file (default is stdout).", - group: 5 }, - - /* Special options */ - { name: NULL, key: 0, arg: NULL, flags: 0, - doc: "Special options:", - group: 6 }, - - { name: "broken-update", key: 'U', arg: "", flags: 0, - doc: "Create an ubi image which simulates a broken update.\n" - " specifies the logical eraseblock number to update.\n", - group: 6 }, - - { name: NULL, key: 0, arg: NULL, flags: 0, doc: NULL, group: 0 }, -}; - typedef struct myargs { /* common settings */ action_t action; @@ -183,120 +165,125 @@ static int ustrtoul(const char *cp, char **endp, unsigned int base) return result; } -static error_t -parse_opt(int key, char *arg, struct argp_state *state) +static int +parse_opt(int argc, char **argv, myargs *args) { int err = 0; char* endp; - myargs *args = state->input; - - switch (key) { - case 'c': - fprintf(stderr, "%s\n", copyright); - exit(0); - break; - case 'o': /* output */ - args->fp_out = fopen(arg, "wb"); - if ((args->fp_out) == NULL) { - fprintf(stderr, "Cannot open file %s for output\n", - arg); - exit(1); - } - break; - case 'i': /* input */ - args->fp_in = fopen(arg, "rb"); - if ((args->fp_in) == NULL) { - fprintf(stderr, "Cannot open file %s for input\n", - arg); - exit(1); + 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", PACKAGE_VERSION); + exit(0); + break; + + default: + fprintf(stderr, "%s", usage); + exit(-1); } - break; - case 'v': /* verbose */ - args->verbose = 1; - break; - - case 'B': /* eb_size */ - args->eb_size = (uint32_t) ustrtoul(arg, &endp, 0); - CHECK_ENDP("B", endp); - break; - case 'E': /* erasecount */ - args->ec = (uint64_t) strtoul(arg, &endp, 0); - CHECK_ENDP("E", endp); - break; - case 'I': /* id */ - args->id = (uint16_t) strtoul(arg, &endp, 0); - CHECK_ENDP("I", endp); - break; - case 'T': /* type */ - args->type = (uint16_t) strtoul(arg, &endp, 0); - CHECK_ENDP("T", endp); - break; - case 'X': /* versionnr */ - args->version = (uint8_t) strtoul(arg, &endp, 0); - CHECK_ENDP("X", endp); - break; - case 'O': /* offset for volume hdr */ - args->hdr_offset = - (uint32_t) strtoul(arg, &endp, 0); - CHECK_ENDP("O", endp); - break; - - case 'U': /* broken update */ - args->action = ACT_BROKEN_UPDATE; - args->update_block = - (uint32_t) strtoul(arg, &endp, 0); - CHECK_ENDP("U", endp); - break; - - case ARGP_KEY_ARG: + } + + if (optind < argc) { if (!args->fp_in) { - args->fp_in = fopen(arg, "rb"); + args->fp_in = fopen(argv[optind++], "rb"); if ((args->fp_in) == NULL) { fprintf(stderr, - "Cannot open file %s for input\n", arg); + "Cannot open file %s for input\n", argv[optind]); exit(1); } } - args->arg1 = arg; - args->options = &state->argv[state->next]; - state->next = state->argc; - break; - case ARGP_KEY_END: - - if (args->id < 0) { - err = 1; - fprintf(stderr, + } + if (args->id < 0) { + err = 1; + fprintf(stderr, "Please specify an UBI Volume ID.\n"); - } - if (args->type == 0) { - err = 1; - fprintf(stderr, + } + if (args->type == 0) { + err = 1; + fprintf(stderr, "Please specify an UBI Volume type.\n"); - } - if (err) { - fprintf(stderr, "\n"); - argp_usage(state); - exit(1); - } - break; - default: - return(ARGP_ERR_UNKNOWN); + } + if (err) { + fprintf(stderr, "%s", usage); + exit(1); } return 0; } -static struct argp argp = { - options: options, - parser: parse_opt, - args_doc: 0, - doc: doc, - children: NULL, - help_filter: NULL, - argp_domain: NULL, -}; - int main(int argc, char **argv) @@ -329,7 +316,7 @@ main(int argc, char **argv) ubigen_init(); /* Init CRC32 table in ubigen */ /* parse arguments */ - argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &args); + parse_opt(argc, argv, &args); if (fstat(fileno(args.fp_in), &file_info) != 0) { fprintf(stderr, "Cannot fetch file size " -- cgit v1.2.3 From f381387591d4be691bf363d1f8446942dce96c7c Mon Sep 17 00:00:00 2001 From: Josh Boyer Date: Mon, 11 Dec 2006 14:34:23 +0100 Subject: [PATCH 8/13] Convert ubimirror to use getopt option parsing Signed-off-by: Josh Boyer Acked-by: Frank Haverkamp --- ubi-utils/src/ubimirror.c | 123 ++++++++++++++++++++++++---------------------- 1 file changed, 63 insertions(+), 60 deletions(-) diff --git a/ubi-utils/src/ubimirror.c b/ubi-utils/src/ubimirror.c index e43ba10..1b1d0be 100644 --- a/ubi-utils/src/ubimirror.c +++ b/ubi-utils/src/ubimirror.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include @@ -40,39 +39,44 @@ typedef enum action_t { } action_t; #define ABORT_ARGP do { \ - state->next = state->argc; \ args->action = ACT_ARGP_ABORT; \ } while (0) #define ERR_ARGP do { \ - state->next = state->argc; \ args->action = ACT_ARGP_ERR; \ } while (0) #define VOL_ARGS_MAX 2 +extern char *optarg; +extern int optind; -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" "ubimirror - mirrors ubi volumes.\n"; -static const char copyright [] __attribute__((unused)) = - "(C) IBM Coorporation 2007"; +static const char *optionsstr = +" -c, --copyright Print copyright information.\n" +" -s, --side= Use the side 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 ] [--copyright] [--side=]\n" +" [--help] [--usage] [--version] \n"; -static struct argp_option options[] = { - { name: "copyright", key: 'c', arg: NULL, flags: 0, - doc: "Print copyright information.", - group: 1 }, - - { name: "side", key: 's', arg: "", flags: 0, - doc: "Use the side as source.", - group: 1 }, +static const char copyright [] __attribute__((unused)) = + "(C) IBM Coorporation 2007"; - { name: NULL, key: 0, arg: NULL, flags: 0, doc: NULL, group: 0 }, +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 { @@ -99,59 +103,58 @@ get_update_side(const char* str) } -static error_t -parse_opt(int key, char *arg, struct argp_state *state) +static int +parse_opt(int argc, char **argv, myargs *args) { - int err = 0; - - myargs *args = state->input; - - switch (key) { - case 'c': - err_msg("%s\n", copyright); - ABORT_ARGP; - break; - case 's': - args->side = get_update_side(arg); - if (args->side < 0) { - err_msg("Unsupported seqnum: %s.\n" - "Supported seqnums are '0' and '1'\n", arg); - ERR_ARGP; - } - break; - case ARGP_KEY_ARG: + 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...] \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", PACKAGE_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("\n"); - argp_usage(state); + err_msg("%s", usage); ERR_ARGP; } - args->vol[(args->vol_no)++] = arg; - break; - case ARGP_KEY_END: - if (err) { - err_msg("\n"); - argp_usage(state); - ERR_ARGP; - } - break; - default: - return(ARGP_ERR_UNKNOWN); + args->vol[(args->vol_no)++] = argv[optind++]; } return 0; } -static struct argp argp = { - options: options, - parser: parse_opt, - args_doc: " ", - doc: doc, - children: NULL, - help_filter: NULL, - argp_domain: NULL, -}; - int main(int argc, char **argv) { @@ -167,7 +170,7 @@ main(int argc, char **argv) { .options = NULL, }; - argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &args); + parse_opt(argc, argv, &args); if (args.action == ACT_ARGP_ERR) { rc = 127; goto err; -- cgit v1.2.3 From 6a4b56d60f849a48530bafa8aafb853636de1a04 Mon Sep 17 00:00:00 2001 From: Josh Boyer Date: Mon, 11 Dec 2006 14:34:23 +0100 Subject: [PATCH 9/13] Convert ubimkvol to use getopt option parsing Signed-off-by: Josh Boyer Acked-by: Frank Haverkamp --- ubi-utils/src/ubimkvol.c | 280 +++++++++++++++++++++-------------------------- 1 file changed, 124 insertions(+), 156 deletions(-) diff --git a/ubi-utils/src/ubimkvol.c b/ubi-utils/src/ubimkvol.c index 04b7fb5..7d510eb 100644 --- a/ubi-utils/src/ubimkvol.c +++ b/ubi-utils/src/ubimkvol.c @@ -28,7 +28,6 @@ * 1.2 Reworked the user-interface to use argp. */ -#include #include #include #include @@ -41,6 +40,9 @@ #define PROGRAM_VERSION "1.2" +extern char *optarg; +extern int optind; + /* * The variables below are set by command line arguments. */ @@ -69,72 +71,43 @@ static struct args myargs = { }; static int param_sanity_check(struct args *args, ubi_lib_t lib); -static error_t parse_opt(int key, char *optarg, struct argp_state *state); - -const char *argp_program_version = PROGRAM_VERSION; -const char *argp_program_bug_address = PACKAGE_BUGREPORT; static char doc[] = "\nVersion: " PROGRAM_VERSION "\n\t" BUILD_OS" "BUILD_CPU" at "__DATE__" "__TIME__"\n" "\nMake UBI Volume.\n"; -static struct argp_option options[] = { - { .name = "devn", - .key = 'd', - .arg = "", - .flags = 0, - .doc = "UBI device", - .group = OPTION_ARG_OPTIONAL }, - - { .name = "vol_id", - .key = 'n', - .arg = "", - .flags = 0, - .doc = "UBI volume id, if not specified, the volume ID will be " - "assigned automatically", - .group = OPTION_ARG_OPTIONAL }, - - { .name = "type", - .key = 't', - .arg = "", - .flags = 0, - .doc = "volume type (dynamic, static), default is dynamic", - .group = OPTION_ARG_OPTIONAL }, - - { .name = "size", - .key = 's', - .arg = "", - .flags = 0, - .doc = "volume size volume size in bytes, " - "kilobytes (KiB) or megabytes (MiB)", - .group = OPTION_ARG_OPTIONAL }, - - { .name = "name", - .key = 'N', - .arg = "", - .flags = 0, - .doc = "volume name", - .group = OPTION_ARG_OPTIONAL }, - - { .name = "alignment", - .key = 'a', - .arg = "", - .flags = 0, - .doc = "volume alignment (default is 1)", - .group = OPTION_ARG_OPTIONAL }, - - { .name = NULL, .key = 0, .arg = NULL, .flags = 0, - .doc = NULL, .group = 0 }, -}; - -static struct argp argp = { - .options = options, - .parser = parse_opt, - .args_doc = 0, - .doc = doc, - .children = NULL, - .help_filter = NULL, - .argp_domain = NULL, +static const char *optionsstr = +" -a, --alignment= volume alignment (default is 1)\n" +" -d, --devn= UBI device\n" +" -n, --vol_id= UBI volume id, if not specified, the volume ID\n" +" will be assigned automatically\n" +" -N, --name= volume name\n" +" -s, --size= volume size volume size in bytes, kilobytes (KiB)\n" +" or megabytes (MiB)\n" +" -t, --type= 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 ] [-d ] [-n ]\n" +" [-N ] [-s ] [-t ]\n" +" [--alignment=] [--devn=] [--vol_id=]\n" +" [--name=] [--size=] [--type=] [--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} }; /* @@ -149,105 +122,101 @@ static struct argp argp = { * Get the `input' argument from `argp_parse', which we know is a * pointer to our arguments structure. */ -static error_t -parse_opt(int key, char *optarg, struct argp_state *state) +static int +parse_opt(int argc, char **argv, struct args *args) { char *endp; - struct args *args = state->input; - - 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= */ - 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; - } - break; - case 'n': /* --volid= */ - 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 ARGP_KEY_NO_ARGS: - /* argp_usage(state); */ - break; - - case ARGP_KEY_ARG: - args->arg1 = optarg; - /* Now we consume all the rest of the arguments. - `state->next' is the index in `state->argv' of the - next argument to be parsed, which is the first STRING - we're interested in, so we can just use - `&state->argv[state->next]' as the value for - arguments->strings. - - _In addition_, by setting `state->next' to the end - of the arguments, we can force argp to stop parsing - here and return. */ - - args->options = &state->argv[state->next]; - state->next = state->argc; - break; - - case ARGP_KEY_END: - /* argp_usage(state); */ - break; - - default: - return(ARGP_ERR_UNKNOWN); + 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= */ + 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; + } + break; + case 'n': /* --volid= */ + 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", PACKAGE_VERSION); + exit(0); + break; + + default: + fprintf(stderr, "%s", usage); + exit(-1); + } } return 0; out: - return(ARGP_ERR_UNKNOWN); + return -1; } static int param_sanity_check(struct args *args, ubi_lib_t lib) @@ -292,8 +261,7 @@ int main(int argc, char * const argv[]) int err; ubi_lib_t lib; - err = argp_parse(&argp, argc, (char **)argv, ARGP_IN_ORDER, 0, - &myargs); + err = parse_opt(argc, (char **)argv, &myargs); if (err) { fprintf(stderr, "Wrong options ...\n"); return err == 1 ? 0 : -1; -- cgit v1.2.3 From 5f758f67b71d15c22aedcdd094390e71664a7afe Mon Sep 17 00:00:00 2001 From: Josh Boyer Date: Mon, 11 Dec 2006 14:34:23 +0100 Subject: [PATCH 10/13] Convert ubirmvol to use getopt option parsing Signed-off-by: Josh Boyer Acked-by: Frank Haverkamp --- ubi-utils/src/ubirmvol.c | 157 +++++++++++++++++++++-------------------------- 1 file changed, 69 insertions(+), 88 deletions(-) diff --git a/ubi-utils/src/ubirmvol.c b/ubi-utils/src/ubirmvol.c index 43679bc..69e4d0e 100644 --- a/ubi-utils/src/ubirmvol.c +++ b/ubi-utils/src/ubirmvol.c @@ -25,7 +25,6 @@ * 1.1 Reworked the userinterface to use argp. */ -#include #include #include #include @@ -38,6 +37,9 @@ #define PROGRAM_VERSION "1.1" +extern char *optarg; +extern int optind; + /* * The below variables are set by command line options. */ @@ -59,43 +61,30 @@ static struct args myargs = { }; static int param_sanity_check(struct args *args, ubi_lib_t lib); -static error_t parse_opt(int key, char *optarg, struct argp_state *state); - -const char *argp_program_version = PROGRAM_VERSION; -const char *argp_program_bug_address = PACKAGE_BUGREPORT; static char doc[] = "\nVersion: " PROGRAM_VERSION "\n\t" BUILD_OS" "BUILD_CPU" at "__DATE__" "__TIME__"\n" "\nMake UBI Volume.\n"; -static struct argp_option options[] = { - { .name = "devn", - .key = 'd', - .arg = "", - .flags = 0, - .doc = "UBI device", - .group = OPTION_ARG_OPTIONAL }, - - { .name = "vol_id", - .key = 'n', - .arg = "", - .flags = 0, - .doc = "UBI volume id, if not specified, the volume ID will be " - "assigned automatically", - .group = OPTION_ARG_OPTIONAL }, - - { .name = NULL, .key = 0, .arg = NULL, .flags = 0, - .doc = NULL, .group = 0 }, -}; - -static struct argp argp = { - .options = options, - .parser = parse_opt, - .args_doc = 0, - .doc = doc, - .children = NULL, - .help_filter = NULL, - .argp_domain = NULL, +static const char *optionsstr = +" -d, --devn= UBI device\n" +" -n, --vol_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 ] [-n ] [--devn=]\n" +" [--vol_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} }; /* @@ -110,66 +99,59 @@ static struct argp argp = { * Get the `input' argument from `argp_parse', which we know is a * pointer to our arguments structure. */ -static error_t -parse_opt(int key, char *optarg, struct argp_state *state) +static int +parse_opt(int argc, char **argv, struct args *args) { char *endp; - struct args *args = state->input; - - switch (key) { - case 'd': /* --devn= */ - 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; - } - break; - case 'n': /* --volid= */ - 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 ARGP_KEY_NO_ARGS: - /* argp_usage(state); */ - break; - - case ARGP_KEY_ARG: - args->arg1 = optarg; - /* Now we consume all the rest of the arguments. - `state->next' is the index in `state->argv' of the - next argument to be parsed, which is the first STRING - we're interested in, so we can just use - `&state->argv[state->next]' as the value for - arguments->strings. - - _In addition_, by setting `state->next' to the end - of the arguments, we can force argp to stop parsing - here and return. */ - - args->options = &state->argv[state->next]; - state->next = state->argc; - break; - - case ARGP_KEY_END: - /* argp_usage(state); */ - break; - - default: - return(ARGP_ERR_UNKNOWN); + while (1) { + int key; + + key = getopt_long(argc, argv, "d:n:?V", long_options, NULL); + if (key == -1) + break; + + switch (key) { + case 'd': /* --devn= */ + 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; + } + break; + case 'n': /* --volid= */ + 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", PACKAGE_VERSION); + exit(0); + break; + default: + fprintf(stderr, "%s", usage); + exit(-1); + } } return 0; out: - return(ARGP_ERR_UNKNOWN); + return -1; } static int param_sanity_check(struct args *args, ubi_lib_t lib) @@ -203,8 +185,7 @@ int main(int argc, char * const argv[]) int err, old_errno; ubi_lib_t lib; - err = argp_parse(&argp, argc, (char **)argv, ARGP_IN_ORDER, 0, - &myargs); + err = parse_opt(argc, (char **)argv, &myargs); if (err) return err == 1 ? 0 : -1; -- cgit v1.2.3 From 0ea0e4e31b7c97c0986d9fd3099c5aab0d2e3392 Mon Sep 17 00:00:00 2001 From: Josh Boyer Date: Mon, 11 Dec 2006 14:34:23 +0100 Subject: [PATCH 11/13] Convert ubiupdatevol to use getopt option parsing Signed-off-by: Josh Boyer Acked-by: Frank Haverkamp --- ubi-utils/src/ubiupdatevol.c | 185 +++++++++++++++++++------------------------ 1 file changed, 80 insertions(+), 105 deletions(-) diff --git a/ubi-utils/src/ubiupdatevol.c b/ubi-utils/src/ubiupdatevol.c index dcb7399..e57dbee 100644 --- a/ubi-utils/src/ubiupdatevol.c +++ b/ubi-utils/src/ubiupdatevol.c @@ -24,7 +24,6 @@ * 1.0 Reworked the userinterface to use argp. */ -#include #include #include #include @@ -47,6 +46,9 @@ #define BUFSIZE 128 * 1024 #define MIN(x,y) ((x)<(y)?(x):(y)) +extern char *optarg; +extern int optind; + struct args { int devn; int vol_id; @@ -69,57 +71,35 @@ static struct args myargs = { .options = NULL, }; -static error_t parse_opt (int key, char *arg, struct argp_state *state); - static int verbose = 0; -const char *argp_program_version = PROGRAM_VERSION; -const char *argp_program_bug_address = PACKAGE_BUGREPORT; static char doc[] = "\nVersion: " PROGRAM_VERSION "\n\t" BUILD_OS" "BUILD_CPU" at "__DATE__" "__TIME__"\n" "\nWrite to UBI Volume.\n"; -static struct argp_option options[] = { - { .name = "devn", - .key = 'd', - .arg = "", - .flags = 0, - .doc = "UBI device", - .group = OPTION_ARG_OPTIONAL }, - - { .name = "vol_id", - .key = 'n', - .arg = "", - .flags = 0, - .doc = "UBI volume id", - .group = OPTION_ARG_OPTIONAL }, - - { .name = "truncate", - .key = 't', - .arg = NULL, - .flags = 0, - .doc = "truncate volume", - .group = OPTION_ARG_OPTIONAL }, - - { .name = "broken-update", - .key = 'B', - .arg = NULL, - .flags = 0, - .doc = "broken update, this is for testing", - .group = OPTION_ARG_OPTIONAL }, - - { .name = NULL, .key = 0, .arg = NULL, .flags = 0, - .doc = NULL, .group = 0 }, -}; - -static struct argp argp = { - .options = options, - .parser = parse_opt, - .args_doc = 0, - .doc = doc, - .children = NULL, - .help_filter = NULL, - .argp_domain = NULL, +static const char *optionsstr = +" -B, --broken-update broken update, this is for testing\n" +" -d, --devn= UBI device\n" +" -n, --vol_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 ] [-n ] [--broken-update]\n" +" [--devn=] [--vol_id=] [--truncate] [--help]\n" +" [--usage] [--version]\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} }; /* @@ -134,65 +114,60 @@ static struct argp argp = { * Get the `input' argument from `argp_parse', which we know is a * pointer to our arguments structure. */ -static error_t -parse_opt(int key, char *arg, struct argp_state *state) +static int +parse_opt(int argc, char **argv, struct args *args) { - struct args *args = state->input; - - switch (key) { - case 'v': /* --verbose= */ - verbose = strtoul(arg, (char **)NULL, 0); - break; - - case 'n': /* --vol_id= */ - args->vol_id = strtol(arg, (char **)NULL, 0); - break; - - case 'd': /* --devn= */ - args->devn = strtol(arg, (char **)NULL, 0); - break; - - case 'b': /* --bufsize= */ - args->bufsize = strtol(arg, (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 ARGP_KEY_NO_ARGS: - /* argp_usage(state); */ - break; - - case ARGP_KEY_ARG: - args->arg1 = arg; - /* Now we consume all the rest of the arguments. - `state->next' is the index in `state->argv' of the - next argument to be parsed, which is the first STRING - we're interested in, so we can just use - `&state->argv[state->next]' as the value for - arguments->strings. - - _In addition_, by setting `state->next' to the end - of the arguments, we can force argp to stop parsing - here and return. */ - - args->options = &state->argv[state->next]; - state->next = state->argc; - break; - - case ARGP_KEY_END: - /* argp_usage(state); */ - break; - - default: - return(ARGP_ERR_UNKNOWN); + 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= */ + verbose = strtoul(optarg, (char **)NULL, 0); + break; + + case 'n': /* --vol_id= */ + args->vol_id = strtol(optarg, (char **)NULL, 0); + break; + + case 'd': /* --devn= */ + args->devn = strtol(optarg, (char **)NULL, 0); + break; + + case 'b': /* --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...]\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", PACKAGE_VERSION); + exit(0); + break; + + default: + fprintf(stderr, "%s", usage); + exit(-1); + } } return 0; @@ -335,7 +310,7 @@ main(int argc, char *argv[]) { int rc; - argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &myargs); + parse_opt(argc, argv, &myargs); if (myargs.truncate) { rc = ubi_truncate_volume(&myargs, 0LL); -- cgit v1.2.3 From c573347818b96a54827a073336838da725b8892e Mon Sep 17 00:00:00 2001 From: Josh Boyer Date: Mon, 11 Dec 2006 14:34:23 +0100 Subject: [PATCH 12/13] Convert unubi to use getopt option parsing Signed-off-by: Josh Boyer Acked-by: Frank Haverkamp --- ubi-utils/src/unubi.c | 201 ++++++++++++++++++++++++-------------------------- 1 file changed, 98 insertions(+), 103 deletions(-) diff --git a/ubi-utils/src/unubi.c b/ubi-utils/src/unubi.c index 4b9ddfd..f8045d8 100644 --- a/ubi-utils/src/unubi.c +++ b/ubi-utils/src/unubi.c @@ -31,7 +31,6 @@ /* TODO: consideration for dynamic vs. static volumes */ -#include #include #include #include @@ -54,10 +53,40 @@ #define CONTACT "haver@vnet.ibm.com" #define VERSION "0.9" +extern char *optarg; +extern int optind; + 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= Extract and rebuild volume\n" +"\n" +" -d, --dir= Specify output directory\n" +"\n" +" -a, --analyze Analyze image\n" +"\n" +" -b, --blocksize= 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 ] [-d ] [-b ]\n" +" [--rebuild=] [--dir=] [--analyze]\n" +" [--blocksize=] [--eb-split] [--vol-split]\n" +" [--vol-split!] [--help] [--usage] [--version] image-file\n"; + #define ERR_MSG(fmt...) \ fprintf(stderr, EXEC ": " fmt) @@ -104,66 +133,20 @@ struct args { char **options; }; -static error_t parse_opt(int key, char *arg, struct argp_state *state); - -const char *argp_program_version = VERSION; -const char *argp_program_bug_address = CONTACT; - -static struct argp_option options[] = { - { - name: NULL, key: 0, arg: NULL, - flags: 0, group: 0, doc: "OPTIONS", - }, - { - name: "rebuild", key: 'r', arg: "", - flags: 0, group: 1, doc: "Extract and rebuild volume", - }, - { - name: "dir", key: 'd', arg: "", - flags: 0, group: 2, doc: "Specify output directory", - }, - { - name: "analyze", key: 'a', arg: NULL, - flags: 0, group: 3, doc: "Analyze image", - }, - { - name: "blocksize", key: 'b', arg: "", - flags: 0, group: 4, doc: "Specify size of eraseblocks " - "in image in bytes (default " - "128KiB)", - }, - { - name: "eb-split", key: 'e', arg: NULL, - flags: 0, group: 5, doc: "Generate individual eraseblock " - "images (all eraseblocks)", - }, - { - name: "vol-split", key: 'v', arg: NULL, - flags: 0, group: 5, doc: "Generate individual eraseblock " - "images (valid eraseblocks only)", - }, - { - name: "vol-split!", key: 'V', arg: NULL, - flags: 0, group: 5, doc: "Raw split by eraseblock " - "(valid eraseblocks only)", - }, - { - name: NULL, key: 0, arg: NULL, - flags: 0, group: 0, doc: NULL, - }, -}; - -static struct argp argp = { - .options = options, - .parser = parse_opt, - .args_doc = "image-file", - .doc = doc, - .children = NULL, - .help_filter = NULL, - .argp_domain = NULL, +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 @@ -198,54 +181,66 @@ str_to_num(char *str) * get the input argument from argp_parse, which we know is a * pointer to our arguments structure; **/ -static error_t -parse_opt(int key, char *arg, struct argp_state *state) +static int +parse_opt(int argc, char **argv, struct args *args) { uint32_t i; - struct args *args = state->input; - - switch (key) { - case 'a': - args->analyze = 1; - break; - case 'b': - args->bsize = str_to_num(arg); - break; - case 'd': - args->odir_path = arg; - break; - case 'e': - args->eb_split = SPLIT_RAW; - break; - case 'r': - i = str_to_num(arg); - if (i < UBI_MAX_VOLUMES) - args->vols[str_to_num(arg)] = 1; - else { - ERR_MSG("volume-id out of bounds\n"); - return ARGP_ERR_UNKNOWN; + + 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); } - break; - case 'v': - if (args->vol_split != SPLIT_RAW) - args->vol_split = SPLIT_DATA; - break; - case 'V': - args->vol_split = SPLIT_RAW; - break; - case ARGP_KEY_NO_ARGS: - break; - case ARGP_KEY_ARG: - args->img_path = arg; - args->options = &state->argv[state->next]; - state->next = state->argc; - break; - case ARGP_KEY_END: - break; - default: - return ARGP_ERR_UNKNOWN; } + if (optind < argc) + args->img_path = argv[optind++]; return 0; } @@ -769,7 +764,7 @@ main(int argc, char *argv[]) memset(a.vols, 0, sizeof(*a.vols) * UBI_MAX_VOLUMES); /* parse args and check for validity */ - argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &a); + parse_opt(argc, argv, &a); if (a.img_path == NULL) { ERR_MSG("no image file specified\n"); rc = EINVAL; -- cgit v1.2.3 From fbd2f11e12df8fd142aed37c0c25c08a1a26e44d Mon Sep 17 00:00:00 2001 From: Josh Boyer Date: Mon, 11 Dec 2006 14:34:23 +0100 Subject: [PATCH 13/13] Include stdlib.h for malloc Signed-off-by: Josh Boyer Acked-by: Frank Haverkamp --- ubi-utils/src/libpfiflash.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ubi-utils/src/libpfiflash.c b/ubi-utils/src/libpfiflash.c index 1dc9f10..dd55203 100644 --- a/ubi-utils/src/libpfiflash.c +++ b/ubi-utils/src/libpfiflash.c @@ -33,6 +33,7 @@ #include #define __USE_GNU #include +#include #include #include -- cgit v1.2.3 From 2798420f85492334c893554f5e69e9f92b4199ef Mon Sep 17 00:00:00 2001 From: Frank Haverkamp Date: Mon, 11 Dec 2006 14:34:23 +0100 Subject: [MTD] UBI Utils: Fix ubiupdatevol argument parsing The file containing the data needs to be added as argument. The support got lost when removing the argp parsing. Signed-off-by: Frank Haverkamp --- ubi-utils/src/ubiupdatevol.c | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/ubi-utils/src/ubiupdatevol.c b/ubi-utils/src/ubiupdatevol.c index e57dbee..753ad61 100644 --- a/ubi-utils/src/ubiupdatevol.c +++ b/ubi-utils/src/ubiupdatevol.c @@ -20,8 +20,10 @@ * 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. */ #include @@ -40,12 +42,13 @@ #include #include -#define PROGRAM_VERSION "1.0" +#define PROGRAM_VERSION "1.1" #define MAXPATH 1024 #define BUFSIZE 128 * 1024 #define MIN(x,y) ((x)<(y)?(x):(y)) +/* FIXME is this not covered by including getopt.h? */ extern char *optarg; extern int optind; @@ -104,15 +107,6 @@ struct option long_options[] = { /* * @brief Parse the arguments passed into the test case. - * - * @param key The parameter. - * @param arg Argument passed to parameter. - * @param state Location to put information on parameters. - * - * @return error - * - * Get the `input' argument from `argp_parse', which we know is a - * pointer to our arguments structure. */ static int parse_opt(int argc, char **argv, struct args *args) @@ -152,11 +146,11 @@ parse_opt(int argc, char **argv, struct args *args) break; case '?': /* help */ - fprintf(stderr, "Usage: ubiupdatevol [OPTION...]\n"); - fprintf(stderr, "%s", doc); - fprintf(stderr, "%s", optionsstr); - fprintf(stderr, "\nReport bugs to %s\n", PACKAGE_BUGREPORT); - exit(0); + fprintf(stderr, "Usage: " + "ubiupdatevol [OPTION...]\n%s%s" + "\nReport bugs to %s\n", + doc, optionsstr, PACKAGE_BUGREPORT); + exit(EXIT_SUCCESS); break; case 'V': @@ -166,10 +160,14 @@ parse_opt(int argc, char **argv, struct args *args) default: fprintf(stderr, "%s", usage); - exit(-1); + exit(EXIT_FAILURE); } } + if (optind < argc) { + /* only one additional argument required */ + args->arg1 = argv[optind++]; + } return 0; } -- cgit v1.2.3 From 084fb764956460b5e67865b6072ea9cc4a2b5af9 Mon Sep 17 00:00:00 2001 From: Frank Haverkamp Date: Mon, 11 Dec 2006 14:34:23 +0100 Subject: [MTD] UBI Utils: Fix wrong handling of table containing strings --- ubi-utils/src/pfi.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/ubi-utils/src/pfi.c b/ubi-utils/src/pfi.c index c8d5ee4..7b57559 100644 --- a/ubi-utils/src/pfi.c +++ b/ubi-utils/src/pfi.c @@ -113,7 +113,9 @@ static const int key_descriptors_max[] = { sizeof(key_desc_v1)/sizeof(struct key_descriptor), /* version 1 */ }; -static const char* modes[] = {"raw", "ubi", NULL}; /* order isn't arbitrary! */ +#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; @@ -131,16 +133,11 @@ static const struct key_descriptor *key_desc = key_desc_v1; static int get_mode_no(const char* mode) { - const char* ptr = modes[0]; - int i = 0; - while (ptr != NULL) { - if(strcmp(ptr, mode) == 0) { - return i; - } - ptr++; - i++; - } + int i; + for (i = 0; i < (int)ARRAY_SIZE(modes); i++) + if (strcmp(mode, modes[i]) == 0) + return i; return -1; } -- cgit v1.2.3 From 1fea122705bfecebf38e57774762663b7cd913d2 Mon Sep 17 00:00:00 2001 From: Frank Haverkamp Date: Mon, 11 Dec 2006 14:34:23 +0100 Subject: [MTD] UBI Utils: Fix pfiflash Various little fixes including some whitespace fixups. Signed-off-by: Frank Haverkamp --- ubi-utils/src/libpfiflash.c | 15 +++++++++++---- ubi-utils/src/pfiflash.c | 21 ++++++++++++++------- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/ubi-utils/src/libpfiflash.c b/ubi-utils/src/libpfiflash.c index dd55203..0be1f2c 100644 --- a/ubi-utils/src/libpfiflash.c +++ b/ubi-utils/src/libpfiflash.c @@ -593,6 +593,8 @@ process_raw_volumes(FILE* pfi, list_t pfi_raws, const char* rawdev, rc = 0; + pfi_data = NULL; + log_msg("[ rawupdate dev=%s", rawdev); crc = UBI_CRC32_INIT; @@ -603,6 +605,8 @@ process_raw_volumes(FILE* pfi, list_t pfi_raws, const char* rawdev, 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); @@ -629,7 +633,7 @@ process_raw_volumes(FILE* pfi, list_t pfi_raws, const char* rawdev, /* open device */ mtd = fopen(rawdev, "r+"); if (mtd == NULL) { - rc = PFIFLASH_ERR_MTD_OPEN; + rc = -PFIFLASH_ERR_MTD_OPEN; EBUF(PFIFLASH_ERRSTR[-rc], rawdev); goto err; } @@ -642,11 +646,12 @@ process_raw_volumes(FILE* pfi, list_t pfi_raws, const char* rawdev, fclose(mtd); rc = -PFIFLASH_ERR_EOF; EBUF(PFIFLASH_ERRSTR[-rc]); - return rc; + goto err; } if ((char)c != pfi_data[k]) { fclose(mtd); - return -1; + rc = -1; + goto err; } } } @@ -659,6 +664,8 @@ process_raw_volumes(FILE* pfi, list_t pfi_raws, const char* rawdev, } err: + if (pfi_data != NULL) + free(pfi_data); return rc; } @@ -692,7 +699,7 @@ erase_unmapped_ubi_volumes(int devno, list_t pfi_ubis, 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) { + if (u->ids[i] >= PFI_UBI_MAX_VOLUMES) { rc = -PFIFLASH_ERR_UBI_VID_OOB; EBUF(PFIFLASH_ERRSTR[-rc], u->ids[i]); goto err; diff --git a/ubi-utils/src/pfiflash.c b/ubi-utils/src/pfiflash.c index c654be9..259a647 100644 --- a/ubi-utils/src/pfiflash.c +++ b/ubi-utils/src/pfiflash.c @@ -23,6 +23,8 @@ * 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. */ #include @@ -39,7 +41,7 @@ #include "error.h" #include "config.h" -#define PROGRAM_VERSION "1.2" +#define PROGRAM_VERSION "1.3" extern char *optarg; extern int optind; @@ -141,7 +143,8 @@ 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); + key = getopt_long(argc, argv, "cl:vCp:r:s:?V", + long_options, NULL); if (key == -1) break; @@ -165,15 +168,17 @@ parse_opt(int argc, char **argv, myargs *args) 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'"); + "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); + "Supported sides are '0' " + "and '1'\n", optarg); } break; case 'r': @@ -183,7 +188,8 @@ parse_opt(int argc, char **argv, myargs *args) err_msg("Usage: pfiflash [OPTION...] [pfifile]"); err_msg("%s", doc); err_msg("%s", optionsstr); - err_msg("\nReport bugs to %s\n", PACKAGE_BUGREPORT); + err_msg("\nReport bugs to %s\n", + PACKAGE_BUGREPORT); exit(0); break; case 'V': @@ -200,7 +206,8 @@ parse_opt(int argc, char **argv, myargs *args) 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]); + err_sys("Cannot open PFI file %s for input", + argv[optind]); } } -- cgit v1.2.3 From da59698ccf81a1df3a250edb81a91f1b8a3decf0 Mon Sep 17 00:00:00 2001 From: Frank Haverkamp Date: Mon, 11 Dec 2006 14:34:23 +0100 Subject: [MTD] UBI Utils: Tools should have individual version numbers The tools had a mixture of different version numbers. This is changed now. The internal change to move to remove glibc dependencies should be reflected by an increase of the version number, so that we can react if trouble is seen with the new code. Singed-off-by: Frank Haverkamp --- ubi-utils/src/bin2nand.c | 24 +++++++++++++++--------- ubi-utils/src/config.h | 4 ++-- ubi-utils/src/mkbootenv.c | 19 ++++++++++++------- ubi-utils/src/nand2bin.c | 12 +++++++++--- ubi-utils/src/pddcustomize.c | 22 +++++++++++++++------- ubi-utils/src/pfi2bin.c | 9 ++++++--- ubi-utils/src/pfiflash.c | 2 +- ubi-utils/src/ubigen.c | 35 +++++++++++++++++++++-------------- ubi-utils/src/ubimirror.c | 17 ++++++++++++----- ubi-utils/src/ubimkvol.c | 16 ++++++++++------ ubi-utils/src/ubirmvol.c | 11 +++++++---- ubi-utils/src/ubiupdatevol.c | 2 +- ubi-utils/src/unubi.c | 4 +++- 13 files changed, 114 insertions(+), 63 deletions(-) diff --git a/ubi-utils/src/bin2nand.c b/ubi-utils/src/bin2nand.c index df838af..4bab1ad 100644 --- a/ubi-utils/src/bin2nand.c +++ b/ubi-utils/src/bin2nand.c @@ -22,10 +22,11 @@ * 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.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. */ #include @@ -44,6 +45,8 @@ #include "config.h" #include "nandecc.h" +#define PROGRAM_VERSION "1.4" + #define CHECK_ENDP(option, endp) do { \ if (*endp) { \ fprintf(stderr, \ @@ -65,7 +68,7 @@ typedef enum action_t { extern char *optarg; extern int optind; -static char doc[] = "\nVersion: " PACKAGE_VERSION "\n\tBuilt on " +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 " @@ -153,11 +156,13 @@ parse_opt(int argc, char **argv, myargs *args) switch (key) { case 'p': /* pagesize */ - args->pagesize = (size_t) ustrtoull(optarg, &endp, 0); + args->pagesize = (size_t) + ustrtoull(optarg, &endp, 0); CHECK_ENDP("p", endp); break; case 'j': /* padding */ - args->padding = (size_t) ustrtoull(optarg, &endp, 0); + args->padding = (size_t) + ustrtoull(optarg, &endp, 0); CHECK_ENDP("j", endp); break; case 'o': /* output */ @@ -172,7 +177,7 @@ parse_opt(int argc, char **argv, myargs *args) exit(0); break; case 'V': - printf("%s\n", PACKAGE_VERSION); + printf("%s\n", PROGRAM_VERSION); exit(0); break; case 'c': @@ -187,7 +192,8 @@ parse_opt(int argc, char **argv, myargs *args) 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++]); + err_quit("Cannot open file %s for input\n", + argv[optind++]); } } diff --git a/ubi-utils/src/config.h b/ubi-utils/src/config.h index b5bbd5b..8c4dd54 100644 --- a/ubi-utils/src/config.h +++ b/ubi-utils/src/config.h @@ -20,8 +20,8 @@ * Author: Frank Haverkamp */ -#define PACKAGE_VERSION "1.1" -#define PACKAGE_BUGREPORT "haver@vnet.ibm.com, dedekind@linutronix.de, or tglx@linutronix.de" +#define PACKAGE_BUGREPORT \ + "haver@vnet.ibm.com, dedekind@linutronix.de, or tglx@linutronix.de" #define __unused __attribute__((unused)) diff --git a/ubi-utils/src/mkbootenv.c b/ubi-utils/src/mkbootenv.c index a556939..c05f4e2 100644 --- a/ubi-utils/src/mkbootenv.c +++ b/ubi-utils/src/mkbootenv.c @@ -18,6 +18,8 @@ * Author: Oliver Lohmann * * Create boot-parameter/pdd data from an ASCII-text input file. + * + * 1.2 Removed argp because we want to use uClibc. */ #include @@ -32,10 +34,12 @@ #include "bootenv.h" #include "error.h" +#define PROGRAM_VERSION "1.2" + extern char *optarg; extern int optind; -static char doc[] = "\nVersion: " PACKAGE_VERSION "\n\tBuilt on " +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 " @@ -91,19 +95,20 @@ parse_opt(int argc, char **argv, myargs *args) case 'o': args->fp_out = fopen(optarg, "wb"); if ((args->fp_out) == NULL) { - fprintf(stderr, - "Cannot open file %s for output\n", optarg); + 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); + printf("\nReport bugs to %s\n", + PACKAGE_BUGREPORT); exit(0); break; case 'V': - printf("%s\n", PACKAGE_VERSION); + printf("%s\n", PROGRAM_VERSION); exit(0); break; default: @@ -115,8 +120,8 @@ parse_opt(int argc, char **argv, myargs *args) 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]); + fprintf(stderr, "Cannot open file %s for input\n", + argv[optind]); exit(1); } } diff --git a/ubi-utils/src/nand2bin.c b/ubi-utils/src/nand2bin.c index a9b68bd..34cce40 100644 --- a/ubi-utils/src/nand2bin.c +++ b/ubi-utils/src/nand2bin.c @@ -18,7 +18,10 @@ * 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. */ + #include #include #include @@ -36,6 +39,8 @@ #include "config.h" #include "nandecc.h" +#define PROGRAM_VERSION "1.2" + #define MAXPATH 1024 #define MIN(x,y) ((x)<(y)?(x):(y)) @@ -60,7 +65,7 @@ static struct args myargs = { .options = NULL, }; -static char doc[] = "\nVersion: " PACKAGE_VERSION "\n\t" +static char doc[] = "\nVersion: " PROGRAM_VERSION "\n\t" BUILD_OS" "BUILD_CPU" at "__DATE__" "__TIME__"\n" "\nSplit data and OOB.\n"; @@ -147,12 +152,13 @@ parse_opt(int argc, char **argv, struct args *args) printf("Usage: nand2bin [OPTION...] input.mif\n"); printf("%s", doc); printf("%s", optionsstr); - printf("\nReport bugs to %s\n", PACKAGE_BUGREPORT); + printf("\nReport bugs to %s\n", + PACKAGE_BUGREPORT); exit(0); break; case 'V': - printf("%s\n", PACKAGE_VERSION); + printf("%s\n", PROGRAM_VERSION); exit(0); break; diff --git a/ubi-utils/src/pddcustomize.c b/ubi-utils/src/pddcustomize.c index 5d1864a..5c2a5f0 100644 --- a/ubi-utils/src/pddcustomize.c +++ b/ubi-utils/src/pddcustomize.c @@ -23,6 +23,8 @@ * 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. */ #include @@ -41,6 +43,8 @@ #include "libubi.h" #include "ubimirror.h" +#define PROGRAM_VERSION "1.3" + typedef enum action_t { ACT_NORMAL = 0, ACT_LIST, @@ -59,7 +63,7 @@ typedef enum action_t { extern char *optarg; extern int optind; -static char doc[] = "\nVersion: " PACKAGE_VERSION "\n\tBuilt on " +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"; @@ -160,7 +164,8 @@ parse_opt(int argc, char **argv, myargs *args) while (1) { int key; - key = getopt_long(argc, argv, "clbxs:i:o:?V", long_options, NULL); + key = getopt_long(argc, argv, "clbxs:i:o:?V", + long_options, NULL); if (key == -1) break; @@ -182,8 +187,9 @@ parse_opt(int argc, char **argv, myargs *args) 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); + "Supported seqnums are " + "'0' and '1'\n", + args->side, optarg); ERR_ARGP; } break; @@ -194,14 +200,16 @@ parse_opt(int argc, char **argv, myargs *args) args->file_out = optarg; break; case '?': /* help */ - err_msg("Usage: pddcustomize.orig [OPTION...] [key=value] [...]"); + err_msg("Usage: pddcustomize.orig [OPTION...] " + "[key=value] [...]"); err_msg("%s", doc); err_msg("%s", optionsstr); - err_msg("\nReport bugs to %s", PACKAGE_BUGREPORT); + err_msg("\nReport bugs to %s", + PACKAGE_BUGREPORT); exit(0); break; case 'V': - err_msg("%s", PACKAGE_VERSION); + err_msg("%s", PROGRAM_VERSION); exit(0); break; default: diff --git a/ubi-utils/src/pfi2bin.c b/ubi-utils/src/pfi2bin.c index 044fa24..9265fd5 100644 --- a/ubi-utils/src/pfi2bin.c +++ b/ubi-utils/src/pfi2bin.c @@ -22,6 +22,8 @@ * 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. */ #include @@ -42,7 +44,7 @@ #include "peb.h" #include "crc32.h" -#define PROGRAM_VERSION "1.2" +#define PROGRAM_VERSION "1.3" #define MAX_FNAME 255 #define DEFAULT_ERASE_COUNT 0 /* Hmmm.... Perhaps */ @@ -157,12 +159,13 @@ parse_opt(int argc, char **argv, myargs *args) printf("pfi2bin [OPTION...] pfifile\n"); printf("%s", doc); printf("%s", optionsstr); - printf("\nReport bugs to %s\n", PACKAGE_BUGREPORT); + printf("\nReport bugs to %s\n", + PACKAGE_BUGREPORT); exit(0); break; case 'V': - printf("%s\n", PACKAGE_VERSION); + printf("%s\n", PROGRAM_VERSION); exit(0); break; diff --git a/ubi-utils/src/pfiflash.c b/ubi-utils/src/pfiflash.c index 259a647..63487b9 100644 --- a/ubi-utils/src/pfiflash.c +++ b/ubi-utils/src/pfiflash.c @@ -193,7 +193,7 @@ parse_opt(int argc, char **argv, myargs *args) exit(0); break; case 'V': - err_msg("%s", PACKAGE_VERSION); + err_msg("%s", PROGRAM_VERSION); exit(0); break; default: diff --git a/ubi-utils/src/ubigen.c b/ubi-utils/src/ubigen.c index 226834f..877f469 100644 --- a/ubi-utils/src/ubigen.c +++ b/ubi-utils/src/ubigen.c @@ -21,6 +21,7 @@ * * 1.0 Initial version * 1.1 Different CRC32 start value + * 1.2 Removed argp because we want to use uClibc. */ #include @@ -34,6 +35,8 @@ #include "ubigen.h" #include "config.h" +#define PROGRAM_VERSION "1.2" + typedef enum action_t { ACT_NORMAL = 0x00000001, ACT_BROKEN_UPDATE = 0x00000002, @@ -42,7 +45,7 @@ typedef enum action_t { extern char *optarg; extern int optind; -static char doc[] = "\nVersion: " PACKAGE_VERSION "\n\tBuilt on " +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"; @@ -187,16 +190,16 @@ parse_opt(int argc, char **argv, myargs *args) case 'o': /* output */ args->fp_out = fopen(optarg, "wb"); if ((args->fp_out) == NULL) { - fprintf(stderr, "Cannot open file %s for output\n", - optarg); + 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); + fprintf(stderr, "Cannot open file %s " + "for input\n", optarg); exit(1); } break; @@ -205,23 +208,26 @@ parse_opt(int argc, char **argv, myargs *args) break; case 'B': /* eb_size */ - args->eb_size = (uint32_t) ustrtoul(optarg, &endp, 0); + 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); + args->ec = (uint64_t)strtoul(optarg, &endp, 0); CHECK_ENDP("E", endp); break; case 'I': /* id */ - args->id = (uint16_t) strtoul(optarg, &endp, 0); + args->id = (uint16_t)strtoul(optarg, &endp, 0); CHECK_ENDP("I", endp); break; case 'T': /* type */ - args->type = (uint16_t) strtoul(optarg, &endp, 0); + args->type = + (uint16_t)strtoul(optarg, &endp, 0); CHECK_ENDP("T", endp); break; case 'X': /* versionnr */ - args->version = (uint8_t) strtoul(optarg, &endp, 0); + args->version = + (uint8_t)strtoul(optarg, &endp, 0); CHECK_ENDP("X", endp); break; case 'O': /* offset for volume hdr */ @@ -241,12 +247,13 @@ parse_opt(int argc, char **argv, myargs *args) fprintf(stderr, "Usage: ubigen [OPTION...]\n"); fprintf(stderr, "%s", doc); fprintf(stderr, "%s", optionsstr); - fprintf(stderr, "\nReport bugs to %s\n", PACKAGE_BUGREPORT); + fprintf(stderr, "\nReport bugs to %s\n", + PACKAGE_BUGREPORT); exit(0); break; case 'V': - fprintf(stderr, "%s\n", PACKAGE_VERSION); + fprintf(stderr, "%s\n", PROGRAM_VERSION); exit(0); break; @@ -260,8 +267,8 @@ parse_opt(int argc, char **argv, myargs *args) 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]); + fprintf(stderr, "Cannot open file %s for " + "input\n", argv[optind]); exit(1); } } diff --git a/ubi-utils/src/ubimirror.c b/ubi-utils/src/ubimirror.c index 1b1d0be..533a0ee 100644 --- a/ubi-utils/src/ubimirror.c +++ b/ubi-utils/src/ubimirror.c @@ -16,6 +16,8 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Oliver Lohmann + * + * 1.2 Removed argp because we want to use uClibc. */ #include @@ -32,6 +34,8 @@ #include "example_ubi.h" #include "ubimirror.h" +#define PROGRAM_VERSION "1.2" + typedef enum action_t { ACT_NORMAL = 0, ACT_ARGP_ABORT, @@ -51,7 +55,7 @@ typedef enum action_t { extern char *optarg; extern int optind; -static char doc[] = "\nVersion: " PACKAGE_VERSION "\n\tBuilt on " +static char doc[] = "\nVersion: " PROGRAM_VERSION "\n\tBuilt on " BUILD_CPU" "BUILD_OS" at "__DATE__" "__TIME__"\n" "\n" "ubimirror - mirrors ubi volumes.\n"; @@ -122,19 +126,22 @@ parse_opt(int argc, char **argv, myargs *args) args->side = get_update_side(optarg); if (args->side < 0) { err_msg("Unsupported seqnum: %s.\n" - "Supported seqnums are '0' and '1'\n", optarg); + "Supported seqnums are '0' " + "and '1'\n", optarg); ERR_ARGP; } break; case '?': /* help */ - err_msg("Usage: ubimirror [OPTION...] \n"); + err_msg("Usage: ubimirror [OPTION...] " + " \n"); err_msg("%s", doc); err_msg("%s", optionsstr); - err_msg("\nReport bugs to %s\n", PACKAGE_BUGREPORT); + err_msg("\nReport bugs to %s\n", + PACKAGE_BUGREPORT); exit(0); break; case 'V': - err_msg("%s", PACKAGE_VERSION); + err_msg("%s", PROGRAM_VERSION); exit(0); break; default: diff --git a/ubi-utils/src/ubimkvol.c b/ubi-utils/src/ubimkvol.c index 7d510eb..e878d70 100644 --- a/ubi-utils/src/ubimkvol.c +++ b/ubi-utils/src/ubimkvol.c @@ -26,6 +26,7 @@ * 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. */ #include @@ -38,7 +39,7 @@ #include #include -#define PROGRAM_VERSION "1.2" +#define PROGRAM_VERSION "1.3" extern char *optarg; extern int optind; @@ -179,10 +180,12 @@ parse_opt(int argc, char **argv, struct args *args) break; case 'n': /* --volid= */ args->vol_id = strtoul(optarg, &endp, 0); - if (*endp != '\0' || endp == optarg || - (args->vol_id < 0 && args->vol_id != UBI_DYNAMIC_VOLUME)) { + if (*endp != '\0' || + endp == optarg || + (args->vol_id < 0 && + args->vol_id != UBI_DYNAMIC_VOLUME)) { fprintf(stderr, "Bad volume ID: " - "\"%s\"\n", optarg); + "\"%s\"\n", optarg); goto out; } break; @@ -199,12 +202,13 @@ parse_opt(int argc, char **argv, struct args *args) fprintf(stderr, "Usage: ubimkvol [OPTION...]\n"); fprintf(stderr, "%s", doc); fprintf(stderr, "%s", optionsstr); - fprintf(stderr, "\nReport bugs to %s\n", PACKAGE_BUGREPORT); + fprintf(stderr, "\nReport bugs to %s\n", + PACKAGE_BUGREPORT); exit(0); break; case 'V': - fprintf(stderr, "%s\n", PACKAGE_VERSION); + fprintf(stderr, "%s\n", PROGRAM_VERSION); exit(0); break; diff --git a/ubi-utils/src/ubirmvol.c b/ubi-utils/src/ubirmvol.c index 69e4d0e..04ec085 100644 --- a/ubi-utils/src/ubirmvol.c +++ b/ubi-utils/src/ubirmvol.c @@ -23,6 +23,7 @@ * Frank Haverkamp * * 1.1 Reworked the userinterface to use argp. + * 1.2 Removed argp because we want to use uClibc. */ #include @@ -35,7 +36,7 @@ #include #include -#define PROGRAM_VERSION "1.1" +#define PROGRAM_VERSION "1.2" extern char *optarg; extern int optind; @@ -123,7 +124,8 @@ parse_opt(int argc, char **argv, struct args *args) case 'n': /* --volid= */ args->vol_id = strtoul(optarg, &endp, 0); if (*endp != '\0' || endp == optarg || - (args->vol_id < 0 && args->vol_id != UBI_DYNAMIC_VOLUME)) { + (args->vol_id < 0 && + args->vol_id != UBI_DYNAMIC_VOLUME)) { fprintf(stderr, "Bad volume ID: " "\"%s\"\n", optarg); goto out; @@ -136,11 +138,12 @@ parse_opt(int argc, char **argv, struct args *args) fprintf(stderr, "Usage: ubirmvol [OPTION...]\n"); fprintf(stderr, "%s", doc); fprintf(stderr, "%s", optionsstr); - fprintf(stderr, "\nReport bugs to %s\n", PACKAGE_BUGREPORT); + fprintf(stderr, "\nReport bugs to %s\n", + PACKAGE_BUGREPORT); exit(0); break; case 'V': - fprintf(stderr, "%s\n", PACKAGE_VERSION); + fprintf(stderr, "%s\n", PROGRAM_VERSION); exit(0); break; default: diff --git a/ubi-utils/src/ubiupdatevol.c b/ubi-utils/src/ubiupdatevol.c index 753ad61..011d532 100644 --- a/ubi-utils/src/ubiupdatevol.c +++ b/ubi-utils/src/ubiupdatevol.c @@ -154,7 +154,7 @@ parse_opt(int argc, char **argv, struct args *args) break; case 'V': - fprintf(stderr, "%s\n", PACKAGE_VERSION); + fprintf(stderr, "%s\n", PROGRAM_VERSION); exit(0); break; diff --git a/ubi-utils/src/unubi.c b/ubi-utils/src/unubi.c index f8045d8..0f8945f 100644 --- a/ubi-utils/src/unubi.c +++ b/ubi-utils/src/unubi.c @@ -19,6 +19,8 @@ /* * Authors: Frank Haverkamp, haver@vnet.ibm.com * Drake Dowsett, dowsett@de.ibm.com + * + * 1.2 Removed argp because we want to use uClibc. */ /* @@ -51,7 +53,7 @@ #define EXEC "unubi" #define CONTACT "haver@vnet.ibm.com" -#define VERSION "0.9" +#define VERSION "1.0" extern char *optarg; extern int optind; -- cgit v1.2.3 From d45b87985d5371b2e47458ee0666f7979d5a8c21 Mon Sep 17 00:00:00 2001 From: Josh Boyer Date: Tue, 12 Dec 2006 09:22:48 +0100 Subject: [PATCH] Minor ubi utils cleanups Remove getopt extern declarations Minor whitespace cleanups Remove incorrect program names that snuck in during getopt rewrite Change comments to match reality Signed-off-by: Josh Boyer Acked-by: Frank Haverkamp --- ubi-utils/src/bin2nand.c | 6 ++---- ubi-utils/src/mkbootenv.c | 6 ++---- ubi-utils/src/nand2bin.c | 14 +++++--------- ubi-utils/src/pddcustomize.c | 10 ++++------ ubi-utils/src/pfi2bin.c | 8 +++----- ubi-utils/src/pfiflash.c | 8 +++----- ubi-utils/src/ubigen.c | 8 +++----- ubi-utils/src/ubimirror.c | 6 ++---- ubi-utils/src/ubimkvol.c | 35 ++++++++++++++++++----------------- ubi-utils/src/ubirmvol.c | 25 ++++++++++++------------- ubi-utils/src/ubiupdatevol.c | 7 ++----- ubi-utils/src/unubi.c | 19 +++++++------------ 12 files changed, 63 insertions(+), 89 deletions(-) diff --git a/ubi-utils/src/bin2nand.c b/ubi-utils/src/bin2nand.c index 4bab1ad..20fc90a 100644 --- a/ubi-utils/src/bin2nand.c +++ b/ubi-utils/src/bin2nand.c @@ -27,6 +27,7 @@ * 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 */ #include @@ -45,7 +46,7 @@ #include "config.h" #include "nandecc.h" -#define PROGRAM_VERSION "1.4" +#define PROGRAM_VERSION "1.5" #define CHECK_ENDP(option, endp) do { \ if (*endp) { \ @@ -65,9 +66,6 @@ typedef enum action_t { #define PADDING 0 /* 0 means, do not adjust anything */ #define BUFSIZE 4096 -extern char *optarg; -extern int optind; - static char doc[] = "\nVersion: " PROGRAM_VERSION "\n\tBuilt on " BUILD_CPU" "BUILD_OS" at "__DATE__" "__TIME__"\n" "\n" diff --git a/ubi-utils/src/mkbootenv.c b/ubi-utils/src/mkbootenv.c index c05f4e2..4a8cc6a 100644 --- a/ubi-utils/src/mkbootenv.c +++ b/ubi-utils/src/mkbootenv.c @@ -20,6 +20,7 @@ * 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 @@ -34,10 +35,7 @@ #include "bootenv.h" #include "error.h" -#define PROGRAM_VERSION "1.2" - -extern char *optarg; -extern int optind; +#define PROGRAM_VERSION "1.3" static char doc[] = "\nVersion: " PROGRAM_VERSION "\n\tBuilt on " BUILD_CPU" "BUILD_OS" at "__DATE__" "__TIME__"\n" diff --git a/ubi-utils/src/nand2bin.c b/ubi-utils/src/nand2bin.c index 34cce40..636ee6f 100644 --- a/ubi-utils/src/nand2bin.c +++ b/ubi-utils/src/nand2bin.c @@ -20,6 +20,7 @@ * 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 @@ -39,14 +40,11 @@ #include "config.h" #include "nandecc.h" -#define PROGRAM_VERSION "1.2" +#define PROGRAM_VERSION "1.3" #define MAXPATH 1024 #define MIN(x,y) ((x)<(y)?(x):(y)) -extern char *optarg; -extern int optind; - struct args { const char *oob_file; const char *output_file; @@ -116,14 +114,12 @@ uint32_t str_to_num(char *str) /* * @brief Parse the arguments passed into the test case. * - * @param key The parameter. - * @param arg Argument passed to parameter. - * @param state Location to put information on parameters. + * @param argc The number of arguments + * @param argv The argument list + * @param args Pointer to program args structure * * @return error * - * Get the `input' argument from `argp_parse', which we know is a - * pointer to our arguments structure. */ static int parse_opt(int argc, char **argv, struct args *args) diff --git a/ubi-utils/src/pddcustomize.c b/ubi-utils/src/pddcustomize.c index 5c2a5f0..764f2e7 100644 --- a/ubi-utils/src/pddcustomize.c +++ b/ubi-utils/src/pddcustomize.c @@ -25,6 +25,7 @@ * PDD modification. * * 1.3 Removed argp because we want to use uClibc. + * 1.4 Minor cleanups */ #include @@ -43,7 +44,7 @@ #include "libubi.h" #include "ubimirror.h" -#define PROGRAM_VERSION "1.3" +#define PROGRAM_VERSION "1.4" typedef enum action_t { ACT_NORMAL = 0, @@ -60,9 +61,6 @@ typedef enum action_t { args->action = ACT_ARGP_ERR; \ } while (0) -extern char *optarg; -extern int optind; - static char doc[] = "\nVersion: " PROGRAM_VERSION "\n\tBuilt on " BUILD_CPU" "BUILD_OS" at "__DATE__" "__TIME__"\n" "\n" @@ -81,7 +79,7 @@ static const char *optionsstr = " -V, --version Print program version\n"; static const char *usage = -"Usage: pddcustomize.orig [-bclx?V] [-i ] [-o ] [-s ]\n" +"Usage: pddcustomize [-bclx?V] [-i ] [-o ] [-s ]\n" " [--both] [--copyright] [--input=] [--list]\n" " [--output=] [--side=] [--host] [--help] [--usage]\n" " [--version] [key=value] [...]\n"; @@ -200,7 +198,7 @@ parse_opt(int argc, char **argv, myargs *args) args->file_out = optarg; break; case '?': /* help */ - err_msg("Usage: pddcustomize.orig [OPTION...] " + err_msg("Usage: pddcustomize [OPTION...] " "[key=value] [...]"); err_msg("%s", doc); err_msg("%s", optionsstr); diff --git a/ubi-utils/src/pfi2bin.c b/ubi-utils/src/pfi2bin.c index 9265fd5..57c4ea5 100644 --- a/ubi-utils/src/pfi2bin.c +++ b/ubi-utils/src/pfi2bin.c @@ -24,6 +24,7 @@ * 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 @@ -44,7 +45,7 @@ #include "peb.h" #include "crc32.h" -#define PROGRAM_VERSION "1.3" +#define PROGRAM_VERSION "1.4" #define MAX_FNAME 255 #define DEFAULT_ERASE_COUNT 0 /* Hmmm.... Perhaps */ @@ -52,9 +53,6 @@ #define MIN(a,b) ((a) < (b) ? (a) : (b)) -extern char *optarg; -extern int optind; - static uint32_t crc32_table[256]; static char err_buf[ERR_BUF_SIZE]; @@ -92,7 +90,7 @@ static const char *optionsstr = " -V, --version Print program version\n"; static const char *usage = -"Usage: pfi2bin.orig [-cv?V] [-j pdd-file] [-o filename] [--copyright]\n" +"Usage: pfi2bin [-cv?V] [-j pdd-file] [-o filename] [--copyright]\n" " [--verbose] [--platform=pdd-file] [--output=filename] [--help]\n" " [--usage] [--version] pfifile\n"; diff --git a/ubi-utils/src/pfiflash.c b/ubi-utils/src/pfiflash.c index 63487b9..0b0da56 100644 --- a/ubi-utils/src/pfiflash.c +++ b/ubi-utils/src/pfiflash.c @@ -25,6 +25,7 @@ * 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 */ #include @@ -41,10 +42,7 @@ #include "error.h" #include "config.h" -#define PROGRAM_VERSION "1.3" - -extern char *optarg; -extern int optind; +#define PROGRAM_VERSION "1.4" static char doc[] = "\nVersion: " PROGRAM_VERSION "\n\tBuilt on " BUILD_CPU" "BUILD_OS" at "__DATE__" "__TIME__"\n" @@ -70,7 +68,7 @@ static const char *optionsstr = " -V, --version Print program version\n"; static const char *usage = -"Usage: pfiflash.orig [-cvC?V] [-l ] [-p ] [-r ] [-s ]\n" +"Usage: pfiflash [-cvC?V] [-l ] [-p ] [-r ] [-s ]\n" " [--copyright] [--logfile=] [--verbose] [--complete]\n" " [--pdd-update=] [--raw-flash=] [--side=]\n" " [--help] [--usage] [--version] [pfifile]\n"; diff --git a/ubi-utils/src/ubigen.c b/ubi-utils/src/ubigen.c index 877f469..d99ba2d 100644 --- a/ubi-utils/src/ubigen.c +++ b/ubi-utils/src/ubigen.c @@ -22,6 +22,7 @@ * 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 @@ -35,16 +36,13 @@ #include "ubigen.h" #include "config.h" -#define PROGRAM_VERSION "1.2" +#define PROGRAM_VERSION "1.3" typedef enum action_t { ACT_NORMAL = 0x00000001, ACT_BROKEN_UPDATE = 0x00000002, } action_t; -extern char *optarg; -extern int optind; - static char doc[] = "\nVersion: " PROGRAM_VERSION "\n\tBuilt on " BUILD_CPU" "BUILD_OS" at "__DATE__" "__TIME__"\n" "\n" @@ -85,7 +83,7 @@ static const char *optionsstr = " -V, --version Print program version\n"; static const char *usage = -"Usage: ubigen.orig [-cdv?V] [-A ] [-B ] [-E ] [-I ]\n" +"Usage: ubigen [-cdv?V] [-A ] [-B ] [-E ] [-I ]\n" " [-O ] [-T ] [-X ] [-i ] [-o ]\n" " [-U ] [--copyright] [--debug] [--verbose] [--alignment=]\n" " [--blocksize=] [--erasecount=] [--id=]\n" diff --git a/ubi-utils/src/ubimirror.c b/ubi-utils/src/ubimirror.c index 533a0ee..eeedb3a 100644 --- a/ubi-utils/src/ubimirror.c +++ b/ubi-utils/src/ubimirror.c @@ -18,6 +18,7 @@ * Author: Oliver Lohmann * * 1.2 Removed argp because we want to use uClibc. + * 1.3 Minor cleanups */ #include @@ -34,7 +35,7 @@ #include "example_ubi.h" #include "ubimirror.h" -#define PROGRAM_VERSION "1.2" +#define PROGRAM_VERSION "1.3" typedef enum action_t { ACT_NORMAL = 0, @@ -52,9 +53,6 @@ typedef enum action_t { #define VOL_ARGS_MAX 2 -extern char *optarg; -extern int optind; - static char doc[] = "\nVersion: " PROGRAM_VERSION "\n\tBuilt on " BUILD_CPU" "BUILD_OS" at "__DATE__" "__TIME__"\n" "\n" diff --git a/ubi-utils/src/ubimkvol.c b/ubi-utils/src/ubimkvol.c index e878d70..879dcb6 100644 --- a/ubi-utils/src/ubimkvol.c +++ b/ubi-utils/src/ubimkvol.c @@ -27,6 +27,7 @@ * 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 */ #include @@ -39,10 +40,7 @@ #include #include -#define PROGRAM_VERSION "1.3" - -extern char *optarg; -extern int optind; +#define PROGRAM_VERSION "1.4" /* * The variables below are set by command line arguments. @@ -114,14 +112,12 @@ struct option long_options[] = { /* * @brief Parse the arguments passed into the test case. * - * @param key The parameter. - * @param arg Argument passed to parameter. - * @param state Location to put information on parameters. + * @param argc The number of arguments + * @param argv The list of arguments + * @param args Pointer to argument structure * * @return error * - * Get the `input' argument from `argp_parse', which we know is a - * pointer to our arguments structure. */ static int parse_opt(int argc, char **argv, struct args *args) @@ -142,16 +138,18 @@ parse_opt(int argc, char **argv, struct args *args) else if (!strcmp(optarg, "static")) args->vol_type = UBI_STATIC_VOLUME; else { - fprintf(stderr, "Bad volume type: \"%s\"\n", - optarg); + 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); + fprintf(stderr, + "Bad volume size: \"%s\"\n", + optarg); goto out; } if (endp != '\0') { @@ -172,9 +170,11 @@ parse_opt(int argc, char **argv, struct args *args) break; case 'd': /* --devn= */ args->devn = strtoul(optarg, &endp, 0); - if (*endp != '\0' || endp == optarg || args->devn < 0) { - fprintf(stderr, "Bad UBI device number: " - "\"%s\"\n", optarg); + if (*endp != '\0' || endp == optarg || + args->devn < 0) { + fprintf(stderr, + "Bad UBI device number: " + "\"%s\"\n", optarg); goto out; } break; @@ -199,7 +199,8 @@ parse_opt(int argc, char **argv, struct args *args) goto out; case '?': /* help */ - fprintf(stderr, "Usage: ubimkvol [OPTION...]\n"); + fprintf(stderr, + "Usage: ubimkvol [OPTION...]\n"); fprintf(stderr, "%s", doc); fprintf(stderr, "%s", optionsstr); fprintf(stderr, "\nReport bugs to %s\n", diff --git a/ubi-utils/src/ubirmvol.c b/ubi-utils/src/ubirmvol.c index 04ec085..f458e8a 100644 --- a/ubi-utils/src/ubirmvol.c +++ b/ubi-utils/src/ubirmvol.c @@ -24,6 +24,7 @@ * * 1.1 Reworked the userinterface to use argp. * 1.2 Removed argp because we want to use uClibc. + * 1.3 Minor cleanups */ #include @@ -36,10 +37,7 @@ #include #include -#define PROGRAM_VERSION "1.2" - -extern char *optarg; -extern int optind; +#define PROGRAM_VERSION "1.3" /* * The below variables are set by command line options. @@ -91,14 +89,12 @@ struct option long_options[] = { /* * @brief Parse the arguments passed into the test case. * - * @param key The parameter. - * @param arg Argument passed to parameter. - * @param state Location to put information on parameters. + * @param argc The number of arguments + * @param argv The list of arguments + * @param args Pointer to argument structure * * @return error * - * Get the `input' argument from `argp_parse', which we know is a - * pointer to our arguments structure. */ static int parse_opt(int argc, char **argv, struct args *args) @@ -115,9 +111,11 @@ parse_opt(int argc, char **argv, struct args *args) switch (key) { case 'd': /* --devn= */ args->devn = strtoul(optarg, &endp, 0); - if (*endp != '\0' || endp == optarg || args->devn < 0) { - fprintf(stderr, "Bad UBI device number: " - "\"%s\"\n", optarg); + if (*endp != '\0' || endp == optarg || + args->devn < 0) { + fprintf(stderr, + "Bad UBI device number: " + "\"%s\"\n", optarg); goto out; } break; @@ -135,7 +133,8 @@ parse_opt(int argc, char **argv, struct args *args) fprintf(stderr, "Parameter is missing\n"); goto out; case '?': /* help */ - fprintf(stderr, "Usage: ubirmvol [OPTION...]\n"); + fprintf(stderr, + "Usage: ubirmvol [OPTION...]\n"); fprintf(stderr, "%s", doc); fprintf(stderr, "%s", optionsstr); fprintf(stderr, "\nReport bugs to %s\n", diff --git a/ubi-utils/src/ubiupdatevol.c b/ubi-utils/src/ubiupdatevol.c index 011d532..b3c4bf1 100644 --- a/ubi-utils/src/ubiupdatevol.c +++ b/ubi-utils/src/ubiupdatevol.c @@ -24,6 +24,7 @@ * * 1.0 Reworked the userinterface to use argp. * 1.1 Removed argp parsing because we want to use uClib. + * 1.2 Minor cleanups */ #include @@ -42,16 +43,12 @@ #include #include -#define PROGRAM_VERSION "1.1" +#define PROGRAM_VERSION "1.2" #define MAXPATH 1024 #define BUFSIZE 128 * 1024 #define MIN(x,y) ((x)<(y)?(x):(y)) -/* FIXME is this not covered by including getopt.h? */ -extern char *optarg; -extern int optind; - struct args { int devn; int vol_id; diff --git a/ubi-utils/src/unubi.c b/ubi-utils/src/unubi.c index 0f8945f..cade1e1 100644 --- a/ubi-utils/src/unubi.c +++ b/ubi-utils/src/unubi.c @@ -21,6 +21,7 @@ * Drake Dowsett, dowsett@de.ibm.com * * 1.2 Removed argp because we want to use uClibc. + * 1.3 Minor cleanups */ /* @@ -53,10 +54,7 @@ #define EXEC "unubi" #define CONTACT "haver@vnet.ibm.com" -#define VERSION "1.0" - -extern char *optarg; -extern int optind; +#define VERSION "1.3" static char doc[] = "\nVersion: " VERSION "\n\t" BUILD_OS" "BUILD_CPU" at "__DATE__" "__TIME__"\n" @@ -177,12 +175,6 @@ str_to_num(char *str) return num; } - -/** - * parses the arguments passed into the program - * get the input argument from argp_parse, which we know is a - * pointer to our arguments structure; - **/ static int parse_opt(int argc, char **argv, struct args *args) { @@ -225,10 +217,13 @@ parse_opt(int argc, char **argv, struct args *args) args->vol_split = SPLIT_RAW; break; case '?': /* help */ - fprintf(stderr, "Usage: unubi [OPTION...] image-file\n"); + fprintf(stderr, + "Usage: unubi [OPTION...] " + "image-file\n"); fprintf(stderr, "%s", doc); fprintf(stderr, "%s", optionsstr); - fprintf(stderr, "\nReport bugs to %s\n", CONTACT); + fprintf(stderr, + "\nReport bugs to %s\n", CONTACT); exit(0); break; case 'J': -- cgit v1.2.3 From d9283ef6116a0c7fbd8ee62a5bc137895066a570 Mon Sep 17 00:00:00 2001 From: Frank Haverkamp Date: Wed, 20 Dec 2006 15:09:36 +0100 Subject: [MTD] UBI Utils: Update testscripts --- ubi-utils/scripts/ubi_test.sh | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/ubi-utils/scripts/ubi_test.sh b/ubi-utils/scripts/ubi_test.sh index 3835bcb..8e62fb4 100755 --- a/ubi-utils/scripts/ubi_test.sh +++ b/ubi-utils/scripts/ubi_test.sh @@ -8,15 +8,16 @@ # Author: Frank Haverkamp # # 1.0 Initial version +# 1.1 Use ubiupdatevol instead of ubiwritevol # -VERSION="1.0" +VERSION="1.1" export PATH=$PATH:~/bin:/usr/local/bin:/home/dedekind/work/prj/ubi/tools/flashutils/bin/ UBIMKVOL=ubimkvol UBIRMVOL=ubirmvol -UBIWRITEVOL=ubiwritevol +UBIUPDATEVOL=ubiupdatevol # 128 KiB 131072 # 256 KiB 262144 @@ -97,7 +98,7 @@ delete_volume () if [ -e /sys/class/ubi/$volume -o -e /sys/class/ubi/ubi0/$volume ]; then echo -n "*** Truncate volume if it exists ... " - $UBIWRITEVOL -d0 -n$volume -t + $UBIUPDATEVOL -d0 -n$volume -t if [ $? -ne "0" ] ; then exit_failure fi @@ -214,8 +215,8 @@ writevol_test () echo "*** Now writing data to volume ... " # sleep 5 ls -l testdata.bin - echo " $UBIWRITEVOL -d0 -n$MINVOL testdata.bin" - $UBIWRITEVOL -d0 -n$MINVOL testdata.bin + echo " $UBIUPDATEVOL -d0 -n$MINVOL testdata.bin" + $UBIUPDATEVOL -d0 -n$MINVOL testdata.bin if [ $? -ne "0" ] ; then exit_failure fi -- cgit v1.2.3 From 5327be2927984d4ba365c76a5f4b22839f6e680b Mon Sep 17 00:00:00 2001 From: Timo Lindhorst Date: Fri, 26 Jan 2007 11:50:04 +0100 Subject: [PATCH] UBI Utils: Fix syntax description for ubiupdatevol Fix the syntax description in the help and usage messages for ubiupdatevol. Signed-off-by: Timo Lindhorst --- ubi-utils/src/ubiupdatevol.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ubi-utils/src/ubiupdatevol.c b/ubi-utils/src/ubiupdatevol.c index b3c4bf1..c0b4178 100644 --- a/ubi-utils/src/ubiupdatevol.c +++ b/ubi-utils/src/ubiupdatevol.c @@ -89,7 +89,7 @@ static const char *optionsstr = static const char *usage = "Usage: ubiupdatevol [-Bt?V] [-d ] [-n ] [--broken-update]\n" " [--devn=] [--vol_id=] [--truncate] [--help]\n" -" [--usage] [--version]\n"; +" [--usage] [--version] \n"; struct option long_options[] = { { .name = "broken-update", .has_arg = 0, .flag = NULL, .val = 'B' }, @@ -144,7 +144,7 @@ parse_opt(int argc, char **argv, struct args *args) case '?': /* help */ fprintf(stderr, "Usage: " - "ubiupdatevol [OPTION...]\n%s%s" + "ubiupdatevol [OPTION...] \n%s%s" "\nReport bugs to %s\n", doc, optionsstr, PACKAGE_BUGREPORT); exit(EXIT_SUCCESS); @@ -239,7 +239,7 @@ ubi_update_volume(struct args *args) } if (fname == NULL) { - fprintf(stderr, "Please specify an existing file.\n"); + fprintf(stderr, "Please specify an existing image file.\n"); exit(EXIT_FAILURE); } -- cgit v1.2.3 From 9fe86e65fb6bd77a02955d07d6f336ace4984e2d Mon Sep 17 00:00:00 2001 From: Josh Boyer Date: Mon, 12 Feb 2007 20:41:17 -0600 Subject: Fix several issues when compiling for 64bit systems Signed-off-by: Josh Boyer --- ubi-utils/src/hashmap.c | 2 +- ubi-utils/src/libubi.c | 4 ++-- ubi-utils/src/nand2bin.c | 2 +- ubi-utils/src/pfi.c | 2 +- ubi-utils/src/reader.c | 7 ++++--- ubi-utils/src/unubi.c | 2 +- ubi-utils/src/unubi_analyze.c | 20 ++++++++++---------- 7 files changed, 20 insertions(+), 19 deletions(-) diff --git a/ubi-utils/src/hashmap.c b/ubi-utils/src/hashmap.c index 250f71f..3511d56 100644 --- a/ubi-utils/src/hashmap.c +++ b/ubi-utils/src/hashmap.c @@ -323,7 +323,7 @@ hashmap_dump(hashmap_t map) for(i = 0; i < map->maxsize; i++) { if (map->data[i] != NULL) { - printf("[%d]: ", i); + printf("[%zd]: ", i); print_all(map->data[i]); } } diff --git a/ubi-utils/src/libubi.c b/ubi-utils/src/libubi.c index 979f157..7cb74bf 100644 --- a/ubi-utils/src/libubi.c +++ b/ubi-utils/src/libubi.c @@ -698,8 +698,8 @@ ubi_vol_update(int vol_fd, unsigned long long bytes) err = ioctl(vol_fd, UBI_IOCVOLUP, &bytes); if (err) { ubi_err("%s failure calling update ioctl\n" - " IOCTL(%08x) err=%d errno=%d\n", - __func__, UBI_IOCVOLUP, err, errno); + " IOCTL(%08lx) err=%d errno=%d\n", + __func__, (long unsigned int)UBI_IOCVOLUP, err, errno); } return err; } diff --git a/ubi-utils/src/nand2bin.c b/ubi-utils/src/nand2bin.c index 636ee6f..b8e4ea3 100644 --- a/ubi-utils/src/nand2bin.c +++ b/ubi-utils/src/nand2bin.c @@ -203,7 +203,7 @@ process_page(uint8_t* buf, uint8_t *oobbuf, size_t pagesize) case 2048: oobsize = 64; eccpoi = 64 / 2; break; case 512: oobsize = 16; eccpoi = 16 / 2; break; default: - fprintf(stderr, "Unsupported page size: %d\n", pagesize); + fprintf(stderr, "Unsupported page size: %zd\n", pagesize); return -EINVAL; } memset(oobbuf, 0xff, oobsize); diff --git a/ubi-utils/src/pfi.c b/ubi-utils/src/pfi.c index 7b57559..fa835e2 100644 --- a/ubi-utils/src/pfi.c +++ b/ubi-utils/src/pfi.c @@ -253,7 +253,7 @@ int pfi_header_setvalue (pfi_header head, { int key_id = find_key_by_name(key); - if ((uint32_t)value == (uint32_t)NULL) + if (value == NULL) return PFI_EINSUFF; if ((key_id < 0) || (key_id >= num_keys)) diff --git a/ubi-utils/src/reader.c b/ubi-utils/src/reader.c index 975caa1..7935a15 100644 --- a/ubi-utils/src/reader.c +++ b/ubi-utils/src/reader.c @@ -178,7 +178,8 @@ read_pfi_raw(pfi_header pfi_hd, FILE* fp_pfi __unused, pfi_raw_t* pfi_raw, } rc = bootenv_list_to_num_vector(raw_start_list, - &(res->starts_size), &(res->starts)); + (void *) &(res->starts_size), + &(res->starts)); if (rc != 0) { EBUF_PFI("Cannot create numeric value array: %s", tmp_str); goto err; @@ -246,7 +247,7 @@ read_pfi_ubi(pfi_header pfi_hd, FILE* fp_pfi __unused, pfi_ubi_t* pfi_ubi, goto err; } - rc = bootenv_list_to_num_vector(ubi_id_list, &(res->ids_size), + rc = bootenv_list_to_num_vector(ubi_id_list, (void *) &(res->ids_size), &(res->ids)); if (rc != 0) { EBUF_PFI("Cannot create numeric value array: %s", tmp_str); @@ -298,7 +299,7 @@ read_pfi_ubi(pfi_header pfi_hd, FILE* fp_pfi __unused, pfi_ubi_t* pfi_ubi, EBUF_PFI("Cannot translate PFI value: %s", tmp_str); goto err; } - rc = bootenv_list_to_vector(ubi_name_list, &(res->names_size), + rc = bootenv_list_to_vector(ubi_name_list, (void *) &(res->names_size), &(tmp_names)); if (rc != 0) { EBUF_PFI("Cannot create string array: %s", tmp_str); diff --git a/ubi-utils/src/unubi.c b/ubi-utils/src/unubi.c index cade1e1..811a6db 100644 --- a/ubi-utils/src/unubi.c +++ b/ubi-utils/src/unubi.c @@ -102,7 +102,7 @@ static const char *usage = #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_%04u" /* split volume */ +#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]; diff --git a/ubi-utils/src/unubi_analyze.c b/ubi-utils/src/unubi_analyze.c index 2e94ca9..6009fc0 100644 --- a/ubi-utils/src/unubi_analyze.c +++ b/ubi-utils/src/unubi_analyze.c @@ -167,9 +167,9 @@ unubi_analyze_ec_hdr(eb_info_t first, const char *path) (crc != ubi32_to_cpu(cur->outer.hdr_crc))) fprintf(fpdata, "# "); - fprintf(fpdata, "%u %llu %llu", count, - ubi64_to_cpu(cur->outer.ec), - erase_counts[count]); + 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", @@ -198,7 +198,7 @@ unubi_analyze_ec_hdr(eb_info_t first, const char *path) if ((count % EC_X_INT) == 0) { if (count > 0) fprintf(fpplot, ", "); - fprintf(fpplot, "%d", count); + fprintf(fpplot, "%zd", count); } cur = cur->next; @@ -207,9 +207,9 @@ unubi_analyze_ec_hdr(eb_info_t first, const char *path) fprintf(fpplot, ")\n"); fprintf(fpplot, "set ylabel \"erase count\"\n"); - fprintf(fpplot, "set xrange [-1:%u]\n", eraseblocks + 1); + fprintf(fpplot, "set xrange [-1:%zu]\n", eraseblocks + 1); fprintf(fpplot, "# set yrange [-1:%llu]\n", - erase_counts[eraseblocks - 1] + 1); + (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", @@ -299,7 +299,7 @@ unubi_analyze_vid_hdr(eb_info_t *head, const char *path) if ((y1 == -1) || (y2 == -1)) return -1; - fprintf(fpdata, "%u %u %u %u %u %u %u\n", + fprintf(fpdata, "%zu %u %u %u %u %u %u\n", count, ubi32_to_cpu(cur->inner.vol_id), ubi32_to_cpu(cur->inner.lnum), @@ -325,10 +325,10 @@ unubi_analyze_vid_hdr(eb_info_t *head, const char *path) fprintf(fpplot, ", "); if (step != ubi32_to_cpu(cur->inner.vol_id)) { step = ubi32_to_cpu(cur->inner.vol_id); - fprintf(fpplot, "\"%d\" %d 0", step, count); + fprintf(fpplot, "\"%zd\" %zd 0", step, count); } else - fprintf(fpplot, "\"%d\" %d 1", + fprintf(fpplot, "\"%d\" %zd 1", ubi32_to_cpu(cur->inner.lnum), count); cur = cur->next; count++; @@ -384,7 +384,7 @@ unubi_analyze_vid_hdr(eb_info_t *head, const char *path) 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:%u]\n", count + 1); + 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\" " -- cgit v1.2.3 From e1e4f5429a155a6bd9a9d384f91d597cab157ee3 Mon Sep 17 00:00:00 2001 From: Frank Haverkamp Date: Fri, 16 Feb 2007 16:22:26 +0100 Subject: UBI Utils: Fix uninitialized written parameter The variable written was not properly initialized. That causes the -j option to fail. Signed-off-by: Frank Haverkamp Signed-off-by: Josh Boyer --- ubi-utils/src/bin2nand.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ubi-utils/src/bin2nand.c b/ubi-utils/src/bin2nand.c index 20fc90a..7c4c816 100644 --- a/ubi-utils/src/bin2nand.c +++ b/ubi-utils/src/bin2nand.c @@ -1,5 +1,5 @@ /* - * Copyright (c) International Business Machines Corp., 2006 + * 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 @@ -28,6 +28,7 @@ * 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 @@ -46,7 +47,7 @@ #include "config.h" #include "nandecc.h" -#define PROGRAM_VERSION "1.5" +#define PROGRAM_VERSION "1.6" #define CHECK_ENDP(option, endp) do { \ if (*endp) { \ @@ -248,7 +249,7 @@ int main (int argc, char** argv) { int rc = -1; int res = 0; - size_t written, read; + size_t written = 0, read; myargs args = { .action = ACT_NORMAL, .pagesize = PAGESIZE, -- cgit v1.2.3 From 04d0f33ea243f307e045605603b96419ea458a26 Mon Sep 17 00:00:00 2001 From: Alexander Schmidt Date: Wed, 21 Feb 2007 10:40:13 +0100 Subject: UBI Utils: pfiflash did not erase block before writing to it Pfiflash should erase raw flash regions before overwriting them and check for bad blocks in case of NAND flash. Signed-off-by: Alexander Schmidt Signed-off-by: Frank Haverkamp Signed-off-by: Josh Boyer --- ubi-utils/src/libpfiflash.c | 52 +++++++++++++++++++++++++++++++++++++++++- ubi-utils/src/pfiflash_error.h | 6 +++-- 2 files changed, 55 insertions(+), 3 deletions(-) diff --git a/ubi-utils/src/libpfiflash.c b/ubi-utils/src/libpfiflash.c index 0be1f2c..4dfafea 100644 --- a/ubi-utils/src/libpfiflash.c +++ b/ubi-utils/src/libpfiflash.c @@ -34,11 +34,13 @@ #define __USE_GNU #include #include +#include #include #include #include /* FIXME Is this ok here? */ +#include #include "pfiflash_error.h" #include "ubimirror.h" @@ -554,6 +556,45 @@ write_normal_volume(int devno, uint32_t id, size_t update_size, FILE* fp_in, 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 @@ -582,7 +623,7 @@ process_raw_volumes(FILE* pfi, list_t pfi_raws, const char* rawdev, void *i; uint32_t crc, crc32_table[256]; size_t j, k; - FILE* mtd; + FILE* mtd = NULL; list_t ptr; if (is_empty(pfi_raws)) @@ -639,6 +680,12 @@ process_raw_volumes(FILE* pfi, list_t pfi_raws, const char* rawdev, } 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); @@ -656,6 +703,7 @@ process_raw_volumes(FILE* pfi, list_t pfi_raws, const char* rawdev, } } rc = fclose(mtd); + mtd = NULL; if (rc != 0) { rc = -PFIFLASH_ERR_MTD_CLOSE; EBUF(PFIFLASH_ERRSTR[-rc], rawdev); @@ -664,6 +712,8 @@ process_raw_volumes(FILE* pfi, list_t pfi_raws, const char* rawdev, } err: + if (mtd != NULL) + fclose(mtd); if (pfi_data != NULL) free(pfi_data); return rc; diff --git a/ubi-utils/src/pfiflash_error.h b/ubi-utils/src/pfiflash_error.h index 34b705e..c06232a 100644 --- a/ubi-utils/src/pfiflash_error.h +++ b/ubi-utils/src/pfiflash_error.h @@ -41,7 +41,8 @@ enum pfiflash_err { PFIFLASH_ERR_PDD_UNKNOWN, PFIFLASH_ERR_MTD_OPEN, PFIFLASH_ERR_MTD_CLOSE, - PFIFLASH_ERR_CRC_CHECK + PFIFLASH_ERR_CRC_CHECK, + PFIFLASH_ERR_MTD_ERASE }; const char *const PFIFLASH_ERRSTR[] = { @@ -63,7 +64,8 @@ const char *const PFIFLASH_ERRSTR[] = { "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" + "CRC check failed: given=0x%08x, calculated=0x%08x", + "couldn't erase raw mtd region" }; #endif /* __PFIFLASH_ERROR_H__ */ -- cgit v1.2.3 From 65561f4974a21d0d9f02ba359522fa2d539f4142 Mon Sep 17 00:00:00 2001 From: Frank Haverkamp Date: Wed, 21 Feb 2007 10:40:13 +0100 Subject: UBI-Utils: Increment version of pfiflash Incrementing the version number was forgotten in the previous patch. Signed-off-by: Frank Haverkamp Signed-off-by: Josh Boyer --- ubi-utils/src/pfiflash.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ubi-utils/src/pfiflash.c b/ubi-utils/src/pfiflash.c index 0b0da56..d5b5406 100644 --- a/ubi-utils/src/pfiflash.c +++ b/ubi-utils/src/pfiflash.c @@ -25,7 +25,8 @@ * 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.4 Minor cleanups. + * 1.5 Forgot to delete raw block before updating it. */ #include @@ -42,7 +43,7 @@ #include "error.h" #include "config.h" -#define PROGRAM_VERSION "1.4" +#define PROGRAM_VERSION "1.5" static char doc[] = "\nVersion: " PROGRAM_VERSION "\n\tBuilt on " BUILD_CPU" "BUILD_OS" at "__DATE__" "__TIME__"\n" -- cgit v1.2.3 From 8bc5349626f1a83f49dd400ca6f45e0bb8347a4e Mon Sep 17 00:00:00 2001 From: Alexander Schmidt Date: Wed, 28 Feb 2007 10:29:34 +0100 Subject: UBI Tools - problem with multiple ubi devices. Fix problem with multiple ubi devices Signed-off-by: Alexander Schmidt Signed-off-by: Josh Boyer --- ubi-utils/src/libubi.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/ubi-utils/src/libubi.c b/ubi-utils/src/libubi.c index 7cb74bf..62216ad 100644 --- a/ubi-utils/src/libubi.c +++ b/ubi-utils/src/libubi.c @@ -138,7 +138,7 @@ static int get_ubi_info(ubi_lib_t desc, struct ubi_info *ubi) { int err; - int n = 1; + int dev_count = 0; char *path; struct stat stat; @@ -147,31 +147,31 @@ get_ubi_info(ubi_lib_t desc, struct ubi_info *ubi) return -1; /* Calculate number of UBI devices */ - do { + while (!err) { char dir[20]; - sprintf(&dir[0], "ubi%d", n); - path = mkpath(desc->sysfs_root, dir); + sprintf(&dir[0], "ubi%d", dev_count); + path = mkpath(desc->ubi_root, dir); if (!path) return ENOMEM; err = lstat(path, &stat); if (err == 0) - n += 1; + dev_count += 1; free(path); - } while (err == 0); + } if (errno != ENOENT) return -1; - if (n == 0) { + if (dev_count == 0) { ubi_err("no UBI devices found"); errno = EINVAL; return -1; } errno = 0; - ubi->dev_count = n; + ubi->dev_count = dev_count; return 0; } -- cgit v1.2.3 From 55d6da34828c784b7f3f9ffddb4a6dd5bdf65d46 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 16 Mar 2007 18:06:30 +0200 Subject: UBI-Utils: Updated headers for latest UBI Signed-off-by: Adrian Hunter --- include/mtd/ubi-header.h | 309 ++++++++++++++++++++++------------------------- include/mtd/ubi-user.h | 66 +++++++--- 2 files changed, 193 insertions(+), 182 deletions(-) diff --git a/include/mtd/ubi-header.h b/include/mtd/ubi-header.h index ca96fc9..c003d98 100644 --- a/include/mtd/ubi-header.h +++ b/include/mtd/ubi-header.h @@ -24,36 +24,30 @@ /* * This file defines the layout of UBI headers and all the other UBI on-flash - * data structures. + * data structures. May be included by user-space. */ #ifndef __UBI_HEADER_H__ #define __UBI_HEADER_H__ -#include +#include -/* The version of this UBI implementation */ +/* The version of UBI images supported by this implementation */ #define UBI_VERSION 1 -/* The highest erase counter value supported by this implementation of UBI */ +/* The highest erase counter value supported by this implementation */ #define UBI_MAX_ERASECOUNTER 0x7FFFFFFF /* The initial CRC32 value used when calculating CRC checksums */ #define UBI_CRC32_INIT 0xFFFFFFFFU -/* - * Magic numbers of the UBI headers. - * - * @UBI_EC_HDR_MAGIC: erase counter header magic number (ASCII "UBI#") - * @UBI_VID_HDR_MAGIC: volume identifier header magic number (ASCII "UBI!") - */ -enum { - UBI_EC_HDR_MAGIC = 0x55424923, - UBI_VID_HDR_MAGIC = 0x55424921 -}; +/* Erase counter header magic number (ASCII "UBI#") */ +#define UBI_EC_HDR_MAGIC 0x55424923 +/* Volume identifier header magic number (ASCII "UBI!") */ +#define UBI_VID_HDR_MAGIC 0x55424921 /* - * Molume type constants used in volume identifier headers. + * Volume type constants used in the volume identifier header. * * @UBI_VID_DYNAMIC: dynamic volume * @UBI_VID_STATIC: static volume @@ -97,59 +91,50 @@ typedef struct { } __attribute__ ((packed)) ubi64_t; /* - * In this implementation UBI uses the big-endian format for on-flash integers. - * The below are the corresponding endianess conversion macros. + * In this implementation of UBI uses the big-endian format for on-flash + * integers. The below are the corresponding conversion macros. */ -#define cpu_to_ubi16(x) ((ubi16_t){cpu_to_be16(x)}) -#define ubi16_to_cpu(x) ((uint16_t)be16_to_cpu((x).int16)) +#define cpu_to_ubi16(x) ((ubi16_t){__cpu_to_be16(x)}) +#define ubi16_to_cpu(x) ((uint16_t)__be16_to_cpu((x).int16)) -#define cpu_to_ubi32(x) ((ubi32_t){cpu_to_be32(x)}) -#define ubi32_to_cpu(x) ((uint32_t)be32_to_cpu((x).int32)) +#define cpu_to_ubi32(x) ((ubi32_t){__cpu_to_be32(x)}) +#define ubi32_to_cpu(x) ((uint32_t)__be32_to_cpu((x).int32)) -#define cpu_to_ubi64(x) ((ubi64_t){cpu_to_be64(x)}) -#define ubi64_to_cpu(x) ((uint64_t)be64_to_cpu((x).int64)) +#define cpu_to_ubi64(x) ((ubi64_t){__cpu_to_be64(x)}) +#define ubi64_to_cpu(x) ((uint64_t)__be64_to_cpu((x).int64)) -/* - * Sizes of UBI headers. - */ +/* Sizes of UBI headers */ #define UBI_EC_HDR_SIZE sizeof(struct ubi_ec_hdr) #define UBI_VID_HDR_SIZE sizeof(struct ubi_vid_hdr) -/* - * Sizes of UBI headers without the ending CRC. - */ +/* Sizes of UBI headers without the ending CRC */ #define UBI_EC_HDR_SIZE_CRC (UBI_EC_HDR_SIZE - sizeof(ubi32_t)) #define UBI_VID_HDR_SIZE_CRC (UBI_VID_HDR_SIZE - sizeof(ubi32_t)) -/* - * How much private data may internal volumes store in the VID header. - */ -#define UBI_VID_HDR_IVOL_DATA_SIZE 12 - /** * struct ubi_ec_hdr - UBI erase counter header. - * - * @magic: the erase counter header magic number (%UBI_EC_HDR_MAGIC) - * @version: the version of UBI implementation which is supposed to accept this - * UBI image (%UBI_VERSION) + * @magic: erase counter header magic number (%UBI_EC_HDR_MAGIC) + * @version: version of UBI implementation which is supposed to accept this + * UBI image * @padding1: reserved for future, zeroes * @ec: the erase counter - * @vid_hdr_offset: where the VID header begins - * @data_offset: where the user data begins + * @vid_hdr_offset: where the VID header starts + * @data_offset: where the user data start * @padding2: reserved for future, zeroes - * @hdr_crc: the erase counter header CRC checksum + * @hdr_crc: erase counter header CRC checksum * * The erase counter header takes 64 bytes and has a plenty of unused space for * future usage. The unused fields are zeroed. The @version field is used to * indicate the version of UBI implementation which is supposed to be able to * work with this UBI image. If @version is greater then the current UBI - * version, the image is rejecter. This may be useful in future if something + * version, the image is rejected. This may be useful in future if something * is changed radically. This field is duplicated in the volume identifier * header. * * The @vid_hdr_offset and @data_offset fields contain the offset of the the * volume identifier header and user data, relative to the beginning of the - * eraseblock. These values have to be the same for all eraseblocks. + * physical eraseblock. These values have to be the same for all physical + * eraseblocks. */ struct ubi_ec_hdr { ubi32_t magic; @@ -164,77 +149,108 @@ struct ubi_ec_hdr { /** * struct ubi_vid_hdr - on-flash UBI volume identifier header. - * * @magic: volume identifier header magic number (%UBI_VID_HDR_MAGIC) * @version: UBI implementation version which is supposed to accept this UBI * image (%UBI_VERSION) * @vol_type: volume type (%UBI_VID_DYNAMIC or %UBI_VID_STATIC) - * @copy_flag: a flag indicating if this physical eraseblock was created by - * means of copying an original physical eraseblock to ensure wear-leveling. - * @compat: compatibility of this volume (%UBI_COMPAT_DELETE, + * @copy_flag: if this logical eraseblock was copied from another physical + * eraseblock (for wear-leveling reasons) + * @compat: compatibility of this volume (%0, %UBI_COMPAT_DELETE, * %UBI_COMPAT_IGNORE, %UBI_COMPAT_PRESERVE, or %UBI_COMPAT_REJECT) - * @vol_id: volume ID + * @vol_id: ID of this volume * @lnum: logical eraseblock number - * @leb_ver: eraseblock copy number - * @data_size: how many bytes of data this eraseblock contains. + * @leb_ver: version of this logical eraseblock (IMPORTANT: obsolete, to be + * removed, kept only for not breaking older UBI users) + * @data_size: how many bytes of data this logical eraseblock contains * @used_ebs: total number of used logical eraseblocks in this volume - * @data_pad: how many bytes at the end of this eraseblock are not used - * @data_crc: CRC checksum of data containing in this eraseblock + * @data_pad: how many bytes at the end of this physical eraseblock are not + * used + * @data_crc: CRC checksum of the data stored in this logical eraseblock * @padding1: reserved for future, zeroes - * @ivol_data: private data of internal volumes + * @sqnum: sequence number + * @padding2: reserved for future, zeroes * @hdr_crc: volume identifier header CRC checksum * - * The @leb_ver and the @copy_flag fields are used to distinguish between older - * and newer copies of logical eraseblocks, as well as to guarantee robustness - * to unclean reboots. As UBI erases logical eraseblocks asynchronously, it has - * to distinguish between older and newer copies of eraseblocks. This is done - * using the @version field. On the other hand, when UBI moves an eraseblock, - * its version is also increased and the @copy_flag is set to 1. Additionally, - * when moving eraseblocks, UBI calculates data CRC and stores it in the - * @data_crc field, even for dynamic volumes. + * The @sqnum is the value of the global sequence counter at the time when this + * VID header was created. The global sequence counter is incremented each time + * UBI writes a new VID header to the flash, i.e. when it maps a logical + * eraseblock to a new physical eraseblock. The global sequence counter is an + * unsigned 64-bit integer and we assume it never overflows. The @sqnum + * (sequence number) is used to distinguish between older and newer versions of + * logical eraseblocks. + * + * There are 2 situations when there may be more then one physical eraseblock + * corresponding to the same logical eraseblock, i.e., having the same @vol_id + * and @lnum values in the volume identifier header. Suppose we have a logical + * eraseblock L and it is mapped to the physical eraseblock P. + * + * 1. Because UBI may erase physical eraseblocks asynchronously, the following + * situation is possible: L is asynchronously erased, so P is scheduled for + * erasure, then L is written to,i.e. mapped to another physical eraseblock P1, + * so P1 is written to, then an unclean reboot happens. Result - there are 2 + * physical eraseblocks P and P1 corresponding to the same logical eraseblock + * L. But P1 has greater sequence number, so UBI picks P1 when it attaches the + * flash. * - * Thus, if there are 2 eraseblocks of the same volume and logical number, UBI - * uses the following algorithm to pick one of them. It first picks the one - * with larger version (say, A). If @copy_flag is not set, then A is picked. If - * @copy_flag is set, UBI checks the CRC of the eraseblock (@data_crc). This is - * needed to ensure that copying was finished. If the CRC is all right, A is - * picked. If not, the older eraseblock is picked. + * 2. From time to time UBI moves logical eraseblocks to other physical + * eraseblocks for wear-leveling reasons. If, for example, UBI moves L from P + * to P1, and an unclean reboot happens before P is physically erased, there + * are two physical eraseblocks P and P1 corresponding to L and UBI has to + * select one of them when the flash is attached. The @sqnum field says which + * PEB is the original (obviously P will have lower @sqnum) and the copy. But + * it is not enough to select the physical eraseblock with the higher sequence + * number, because the unclean reboot could have happen in the middle of the + * copying process, so the data in P is corrupted. It is also not enough to + * just select the physical eraseblock with lower sequence number, because the + * data there may be old (consider a case if more data was added to P1 after + * the copying). Moreover, the unclean reboot may happen when the erasure of P + * was just started, so it result in unstable P, which is "mostly" OK, but + * still has unstable bits. * - * Note, the @leb_ver field may overflow. Thus, if you have 2 versions A and B, - * then A > B if abs(A-B) < 0x7FFFFFFF, and A < B otherwise. + * UBI uses the @copy_flag field to indicate that this logical eraseblock is a + * copy. UBI also calculates data CRC when the data is moved and stores it at + * the @data_crc field of the copy (P1). So when UBI needs to pick one physical + * eraseblock of two (P or P1), the @copy_flag of the newer one (P1) is + * examined. If it is cleared, the situation* is simple and the newer one is + * picked. If it is set, the data CRC of the copy (P1) is examined. If the CRC + * checksum is correct, this physical eraseblock is selected (P1). Otherwise + * the older one (P) is selected. + * + * Note, there is an obsolete @leb_ver field which was used instead of @sqnum + * in the past. But it is not used anymore and we keep it in order to be able + * to deal with old UBI images. It will be removed at some point. * * There are 2 sorts of volumes in UBI: user volumes and internal volumes. - * Internal volumes are not seen from outside and are used for different - * internal UBI purposes. In this implementation there are only two internal - * volumes: the layout volume and the update volume. Internal volumes are the - * main mechanism of UBI extensions. For example, in future one may introduce a - * journal internal volume. + * Internal volumes are not seen from outside and are used for various internal + * UBI purposes. In this implementation there is only one internal volume - the + * layout volume. Internal volumes are the main mechanism of UBI extensions. + * For example, in future one may introduce a journal internal volume. Internal + * volumes have their own reserved range of IDs. * - * The @compat field is only used for internal volumes and contains the degree - * of their compatibility. This field is always zero for user volumes. This - * field provides a mechanism to introduce UBI extensions and to be still - * compatible with older UBI binaries. For example, if someone introduced an - * journal internal volume in future, he would probably use %UBI_COMPAT_DELETE - * compatibility. And in this case, older UBI binaries, which know nothing - * about the journal volume, would just delete this and work perfectly fine. - * This is somewhat similar to what Ext2fs does when it is fed by an Ext3fs - * image - it just ignores the Ext3fs journal. + * The @compat field is only used for internal volumes and contains the "degree + * of their compatibility". It is always zero for user volumes. This field + * provides a mechanism to introduce UBI extensions and to be still compatible + * with older UBI binaries. For example, if someone introduced a journal in + * future, he would probably use %UBI_COMPAT_DELETE compatibility for the + * journal volume. And in this case, older UBI binaries, which know nothing + * about the journal volume, would just delete this volume and work perfectly + * fine. This is similar to what Ext2fs does when it is fed by an Ext3fs image + * - it just ignores the Ext3fs journal. * * The @data_crc field contains the CRC checksum of the contents of the logical - * eraseblock if this is a static volume. In case of dynamic volumes, it does + * eraseblock if this is a static volume. In case of dynamic volumes, it does * not contain the CRC checksum as a rule. The only exception is when the - * logical eraseblock was moved by the wear-leveling unit, then the - * wear-leveling unit calculates the eraseblocks' CRC and stores it at - * @data_crc. + * data of the physical eraseblock was moved by the wear-leveling unit, then + * the wear-leveling unit calculates the data CRC and stores it in the + * @data_crc field. And of course, the @copy_flag is %in this case. * - * The @data_size field is always used for static volumes because we want to - * know about how many bytes of data are stored in this eraseblock. For - * dynamic eraseblocks, this field usually contains zero. The only exception is - * when the logical eraseblock is moved to another physical eraseblock due to + * The @data_size field is used only for static volumes because UBI has to know + * how many bytes of data are stored in this eraseblock. For dynamic volumes, + * this field usually contains zero. The only exception is when the data of the + * physical eraseblock was moved to another physical eraseblock for * wear-leveling reasons. In this case, UBI calculates CRC checksum of the * contents and uses both @data_crc and @data_size fields. In this case, the - * @data_size field contains the size of logical eraseblock of this volume - * (which may vary owing to @alignment). + * @data_size field contains data size. * * The @used_ebs field is used only for static volumes and indicates how many * eraseblocks the data of the volume takes. For dynamic volumes this field is @@ -244,11 +260,6 @@ struct ubi_ec_hdr { * parameter. So, effectively, the @data_pad field reduces the size of logical * eraseblocks of this volume. This is very handy when one uses block-oriented * software (say, cramfs) on top of the UBI volume. - * - * The @ivol_data contains private data of internal volumes. This might be very - * handy to store data in the VID header, not in the eraseblock's contents. For - * example it may make life of simple boot-loaders easier. The @ivol_data field - * contains zeroes for user volumes. */ struct ubi_vid_hdr { ubi32_t magic; @@ -258,117 +269,87 @@ struct ubi_vid_hdr { uint8_t compat; ubi32_t vol_id; ubi32_t lnum; - ubi32_t leb_ver; + ubi32_t leb_ver; /* obsolete, to be removed, don't use */ ubi32_t data_size; ubi32_t used_ebs; ubi32_t data_pad; ubi32_t data_crc; - uint8_t padding1[12]; - uint8_t ivol_data[UBI_VID_HDR_IVOL_DATA_SIZE]; + uint8_t padding1[4]; + ubi64_t sqnum; + uint8_t padding2[12]; ubi32_t hdr_crc; } __attribute__ ((packed)); -/** - * struct ubi_vid_hdr_upd_vol - private data of the update internal volume - * stored in volume identifier headers. - * - * @vol_id: volume ID of the volume under update - * @padding: zeroes - */ -struct ubi_vid_hdr_upd_vol { - ubi32_t vol_id; - uint8_t padding[UBI_VID_HDR_IVOL_DATA_SIZE-4]; -} __attribute__ ((packed)); - -/* - * Count of internal UBI volumes. - */ -#define UBI_INT_VOL_COUNT 2 +/* Internal UBI volumes count */ +#define UBI_INT_VOL_COUNT 1 /* - * Internal volume IDs start from this digit. There is a reserved room for 4096 - * internal volumes. + * Starting ID of internal volumes. There is reserved room for 4096 internal + * volumes. */ #define UBI_INTERNAL_VOL_START (0x7FFFFFFF - 4096) /* - * enum ubi_internal_volume_numbers - volume IDs of internal UBI volumes. + * IDs of internal UBI volumes. * - * %UBI_LAYOUT_VOL_ID: volume ID of the layout volume - * %UBI_UPDATE_VOL_ID: volume ID of the update volume + * %UBI_LAYOUT_VOL_ID: layout volume ID */ enum { UBI_LAYOUT_VOL_ID = UBI_INTERNAL_VOL_START, - UBI_UPDATE_VOL_ID = UBI_INTERNAL_VOL_START + 1 }; + +/* The layout volume contains the volume table */ -/* - * Number of logical eraseblocks reserved for internal volumes. - */ -#define UBI_LAYOUT_VOLUME_EBS 2 -#define UBI_UPDATE_VOLUME_EBS 1 - -/* - * Names of internal volumes - */ -#define UBI_LAYOUT_VOLUME_NAME "The layout volume" -#define UBI_UPDATE_VOLUME_NAME "The update volume" - -/* - * Compatibility flags of internal volumes. - */ +#define UBI_LAYOUT_VOLUME_EBS 2 +#define UBI_LAYOUT_VOLUME_NAME "The layout volume" #define UBI_LAYOUT_VOLUME_COMPAT UBI_COMPAT_REJECT -#define UBI_UPDATE_VOLUME_COMPAT UBI_COMPAT_REJECT -/* - * The maximum number of volumes per one UBI device. - */ +/* The maximum number of volumes per one UBI device */ #define UBI_MAX_VOLUMES 128 -/* - * The maximum volume name length. - */ +/* The maximum volume name length */ #define UBI_VOL_NAME_MAX 127 -/* - * Size of volume table records. - */ +/* Size of the volume table record */ #define UBI_VTBL_RECORD_SIZE sizeof(struct ubi_vol_tbl_record) -/* - * Size of volume table records without the ending CRC. - */ +/* Size of the volume table record without the ending CRC */ #define UBI_VTBL_RECORD_SIZE_CRC (UBI_VTBL_RECORD_SIZE - sizeof(ubi32_t)) /** * struct ubi_vol_tbl_record - a record in the volume table. - * * @reserved_pebs: how many physical eraseblocks are reserved for this volume * @alignment: volume alignment - * @data_pad: how many bytes are not used at the end of the eraseblocks to - * satisfy the requested alignment + * @data_pad: how many bytes are unused at the end of the each physical + * eraseblock to satisfy the requested alignment * @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) - * @padding1: reserved, zeroes - * @name_len: the volume name length + * @upd_marker: if volume update was started but not finished + * @name_len: volume name length * @name: the volume name * @padding2: reserved, zeroes * @crc: a CRC32 checksum of the record * - * The layout volume consists of 2 logical eraseblock, each of which contains - * the volume table (i.e., the volume table is duplicated). The volume table is - * an array of &struct ubi_vol_tbl_record objects indexed by the volume ID. + * The volume table records are stored in the volume table, which is stored in + * the layout volume. The layout volume consists of 2 logical eraseblock, each + * of which contains a copy of the volume table (i.e., the volume table is + * duplicated). The volume table is an array of &struct ubi_vol_tbl_record + * objects indexed by the volume ID. * * If the size of the logical eraseblock is large enough to fit - * %UBI_MAX_VOLUMES, the volume table contains %UBI_MAX_VOLUMES records. - * Otherwise, it contains as much records as can be fit (i.e., size of logical - * eraseblock divided by sizeof(struct ubi_vol_tbl_record)). + * %UBI_MAX_VOLUMES records, the volume table contains %UBI_MAX_VOLUMES + * records. Otherwise, it contains as many records as it can fit (i.e., size of + * logical eraseblock divided by sizeof(struct ubi_vol_tbl_record)). + * + * The @upd_marker flag is used to implement volume update. It is set to %1 + * before update and set to %0 after the update. So if the update operation was + * interrupted, UBI knows that the volume is corrupted. * * The @alignment field is specified when the volume is created and cannot be * later changed. It may be useful, for example, when a block-oriented file * system works on top of UBI. The @data_pad field is calculated using the * logical eraseblock size and @alignment. The alignment must be multiple to the * minimal flash I/O unit. If @alignment is 1, all the available space of - * eraseblocks is used. + * the physical eraseblocks is used. * * Empty records contain all zeroes and the CRC checksum of those zeroes. */ @@ -377,7 +358,7 @@ struct ubi_vol_tbl_record { ubi32_t alignment; ubi32_t data_pad; uint8_t vol_type; - uint8_t padding1; + uint8_t upd_marker; ubi16_t name_len; uint8_t name[UBI_VOL_NAME_MAX+1]; uint8_t padding2[24]; diff --git a/include/mtd/ubi-user.h b/include/mtd/ubi-user.h index 0eb1470..bb0aca6 100644 --- a/include/mtd/ubi-user.h +++ b/include/mtd/ubi-user.h @@ -21,9 +21,43 @@ #ifndef __UBI_USER_H__ #define __UBI_USER_H__ -#ifndef __KERNEL__ -#define __user -#endif +/* + * UBI volume creation + * ~~~~~~~~~~~~~~~~~~~ + * + * UBI volumes are created via the %UBI_IOCMKVOL IOCTL command of UBI character + * device. A &struct ubi_mkvol_req object has to be properly filled and a + * pointer to it has to be passed to the IOCTL. + * + * UBI volume deletion + * ~~~~~~~~~~~~~~~~~~~ + * + * To delete a volume, the %UBI_IOCRMVOL IOCTL command of the UBI character + * device should be used. A pointer to the 32-bit volume ID hast to be passed + * to the IOCTL. + * + * UBI volume re-size + * ~~~~~~~~~~~~~~~~~~ + * + * To re-size a volume, the %UBI_IOCRSVOL IOCTL command of the UBI character + * device should be used. A &struct ubi_rsvol_req object has to be properly + * filled and a pointer to it has to be passed to the IOCTL. + * + * UBI volume update + * ~~~~~~~~~~~~~~~~~ + * + * Volume update should be done via the %UBI_IOCVOLUP IOCTL command of the + * corresponding UBI volume character device. A pointer to a 64-bit update + * size should be passed to the IOCTL. After then, UBI expects user to write + * this number of bytes to the volume character device. The update is finished + * when the claimed number of bytes is passed. So, the volume update sequence + * is something like: + * + * fd = open("/dev/my_volume"); + * ioctl(fd, UBI_IOCVOLUP, &image_size); + * write(fd, buf, image_size); + * close(fd); + */ /* * When a new volume is created, users may either specify the volume number they @@ -35,9 +69,7 @@ /* Maximum volume name length */ #define UBI_MAX_VOLUME_NAME 127 -/* - * IOCTL commands of UBI character devices - */ +/* IOCTL commands of UBI character devices */ #define UBI_IOC_MAGIC 'o' @@ -48,16 +80,14 @@ /* Re-size an UBI volume */ #define UBI_IOCRSVOL _IOW(UBI_IOC_MAGIC, 2, struct ubi_rsvol_req) -/* - * IOCTL commands of UBI volume character devices. - */ +/* IOCTL commands of UBI volume character devices */ #define UBI_VOL_IOC_MAGIC 'O' /* Start UBI volume update */ #define UBI_IOCVOLUP _IOW(UBI_VOL_IOC_MAGIC, 0, int64_t) -/* An eraseblock erasure command, used for debugging, disabled by dafault */ -#define UBI_IOCEBER _IOW(UBI_VOL_IOC_MAGIC, 0, int32_t) +/* An eraseblock erasure command, used for debugging, disabled by default */ +#define UBI_IOCEBER _IOW(UBI_VOL_IOC_MAGIC, 1, int32_t) /* * UBI volume type constants. @@ -73,13 +103,13 @@ enum { /** * struct ubi_mkvol_req - volume description data structure used in * volume creation requests. - * * @vol_id: volume number * @alignment: volume alignment * @bytes: volume size in bytes * @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) - * @padding: reserved for future, not used + * @padding1: reserved for future, not used * @name_len: volume name length + * @padding2: reserved for future, not used * @name: volume name * * This structure is used by userspace programs when creating new volumes. The @@ -88,7 +118,7 @@ enum { * The @alignment field specifies the required alignment of the volume logical * eraseblock. This means, that the size of logical eraseblocks will be aligned * to this number, i.e., - * (UBI device logical eraseblock size) mod (@alignment) = 0. + * (UBI device logical eraseblock size) mod (@alignment) = 0. * * To put it differently, the logical eraseblock of this volume may be slightly * shortened in order to make it properly aligned. The alignment has to be @@ -99,21 +129,21 @@ enum { * a block device on top of an UBI volume. In this case, it is desirable to fit * an integer number of blocks in logical eraseblocks of this UBI volume. With * alignment it is possible to update this volume using plane UBI volume image - * BLOBs, without caring about how to properly write them. + * BLOBs, without caring about how to properly align them. */ struct ubi_mkvol_req { int32_t vol_id; int32_t alignment; int64_t bytes; int8_t vol_type; - int8_t padding[9]; + int8_t padding1; int16_t name_len; - __user const char *name; + int8_t padding2[4]; + char name[UBI_MAX_VOLUME_NAME+1]; } __attribute__ ((packed)); /** * struct ubi_rsvol_req - a data structure used in volume re-size requests. - * * @vol_id: ID of the volume to re-size * @bytes: new size of the volume in bytes * -- cgit v1.2.3 From 2271f47026e986ecd038db8fbf7a1d14c11a7b64 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 16 Mar 2007 18:07:43 +0200 Subject: UBI-Utils: Updated libubi for latest UBI Signed-off-by: Adrian Hunter --- ubi-utils/src/libubi.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/ubi-utils/src/libubi.c b/ubi-utils/src/libubi.c index 62216ad..da4919b 100644 --- a/ubi-utils/src/libubi.c +++ b/ubi-utils/src/libubi.c @@ -311,7 +311,7 @@ ubi_open(ubi_lib_t *desc) if (err) goto error; - res->vdev_path = mkpath(res->udev_path, "%d/"); + res->vdev_path = mkpath(res->ubi_root, "ubi%d_%d/"); if (!res->vdev_path) goto error; @@ -595,6 +595,11 @@ ubi_mkvol(ubi_lib_t desc, int devn, int vol_id, int vol_type, 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; @@ -603,8 +608,9 @@ ubi_mkvol(ubi_lib_t desc, int devn, int vol_id, int vol_type, req.bytes = bytes; req.vol_type = vol_type; req.alignment = alignment; - req.name_len = strlen(name); - req.name = name; + + 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, -- cgit v1.2.3 From a8389fa276651f708a9da7d1feb6d5f25afc6704 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 16 Mar 2007 18:08:21 +0200 Subject: UBI-Utils: Updated scripts for latest UBI Signed-off-by: Adrian Hunter --- ubi-utils/scripts/ubi_test.sh | 4 ++-- ubi-utils/scripts/ubi_tools_test.sh | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ubi-utils/scripts/ubi_test.sh b/ubi-utils/scripts/ubi_test.sh index 8e62fb4..73e4b19 100755 --- a/ubi-utils/scripts/ubi_test.sh +++ b/ubi-utils/scripts/ubi_test.sh @@ -95,7 +95,7 @@ delete_volume () volume=$1 ### FIXME broken sysfs!!!! - if [ -e /sys/class/ubi/$volume -o -e /sys/class/ubi/ubi0/$volume ]; then + 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 @@ -325,4 +325,4 @@ echo "* Congratulations, no errors found! *" echo "* Have fun with your cool UBI system! *" echo "***********************************************************************" -exit_success \ No newline at end of file +exit_success diff --git a/ubi-utils/scripts/ubi_tools_test.sh b/ubi-utils/scripts/ubi_tools_test.sh index b4d167e..7f121f1 100755 --- a/ubi-utils/scripts/ubi_tools_test.sh +++ b/ubi-utils/scripts/ubi_tools_test.sh @@ -96,7 +96,7 @@ delete_volume () volume=$1 ### FIXME broken sysfs!!!! - if [ -e /sys/class/ubi/$volume -o -e /sys/class/ubi/ubi0/$volume ]; then + 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 @@ -249,4 +249,4 @@ echo "* Congratulations, no errors found! *" echo "* Have fun with your cool UBI system! *" echo "***********************************************************************" -exit_success \ No newline at end of file +exit_success -- cgit v1.2.3 From db0fa8cfb0fae8c07c4c88f73bbc71176df36d7c Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 21 Mar 2007 11:45:07 +0200 Subject: UBI-Utils: Rename old ubi library Signed-off-by: Adrian Hunter --- ubi-utils/inc/libubi.h | 310 ---------------- ubi-utils/inc/libubiold.h | 310 ++++++++++++++++ ubi-utils/src/libubi.c | 768 ---------------------------------------- ubi-utils/src/libubi_int.h | 119 ------- ubi-utils/src/libubi_sysfs.c | 232 ------------ ubi-utils/src/libubi_sysfs.h | 109 ------ ubi-utils/src/libubiold.c | 768 ++++++++++++++++++++++++++++++++++++++++ ubi-utils/src/libubiold_int.h | 119 +++++++ ubi-utils/src/libubiold_sysfs.c | 232 ++++++++++++ ubi-utils/src/libubiold_sysfs.h | 109 ++++++ 10 files changed, 1538 insertions(+), 1538 deletions(-) delete mode 100644 ubi-utils/inc/libubi.h create mode 100644 ubi-utils/inc/libubiold.h delete mode 100644 ubi-utils/src/libubi.c delete mode 100644 ubi-utils/src/libubi_int.h delete mode 100644 ubi-utils/src/libubi_sysfs.c delete mode 100644 ubi-utils/src/libubi_sysfs.h create mode 100644 ubi-utils/src/libubiold.c create mode 100644 ubi-utils/src/libubiold_int.h create mode 100644 ubi-utils/src/libubiold_sysfs.c create mode 100644 ubi-utils/src/libubiold_sysfs.h diff --git a/ubi-utils/inc/libubi.h b/ubi-utils/inc/libubi.h deleted file mode 100644 index f13d812..0000000 --- a/ubi-utils/inc/libubi.h +++ /dev/null @@ -1,310 +0,0 @@ -#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 -#include - -#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/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 +#include + +#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/src/libubi.c b/ubi-utils/src/libubi.c deleted file mode 100644 index da4919b..0000000 --- a/ubi-utils/src/libubi.c +++ /dev/null @@ -1,768 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "libubi.h" -#include "libubi_int.h" -#include "libubi_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/libubi_int.h b/ubi-utils/src/libubi_int.h deleted file mode 100644 index 830a682..0000000 --- a/ubi-utils/src/libubi_int.h +++ /dev/null @@ -1,119 +0,0 @@ -/* - * 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/libubi_sysfs.c b/ubi-utils/src/libubi_sysfs.c deleted file mode 100644 index 95fd3de..0000000 --- a/ubi-utils/src/libubi_sysfs.c +++ /dev/null @@ -1,232 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#include -#include -#include -#include - -#include "config.h" -#include "libubi_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/libubi_sysfs.h b/ubi-utils/src/libubi_sysfs.h deleted file mode 100644 index 2fb6072..0000000 --- a/ubi-utils/src/libubi_sysfs.h +++ /dev/null @@ -1,109 +0,0 @@ -/* - * 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/libubiold.c b/ubi-utils/src/libubiold.c new file mode 100644 index 0000000..da4919b --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libubi.h" +#include "libubi_int.h" +#include "libubi_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..95fd3de --- /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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "libubi_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); -- cgit v1.2.3 From 9c37b558705b776e51f7d522f376de019a6ea203 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 21 Mar 2007 11:53:25 +0200 Subject: UBI-Utils: Convert to new ubi library Signed-off-by: Adrian Hunter --- ubi-utils/Makefile | 20 +- ubi-utils/inc/libubi.h | 268 ++++++++++++ ubi-utils/src/libpfiflash.c | 2 +- ubi-utils/src/libubi.c | 917 ++++++++++++++++++++++++++++++++++++++++ ubi-utils/src/libubi_int.h | 129 ++++++ ubi-utils/src/libubimirror.c | 10 +- ubi-utils/src/libubiold.c | 6 +- ubi-utils/src/libubiold_sysfs.c | 2 +- ubi-utils/src/pddcustomize.c | 2 +- ubi-utils/src/reader.c | 13 +- ubi-utils/src/ubimkvol.c | 33 +- ubi-utils/src/ubirmvol.c | 24 +- ubi-utils/src/ubiupdatevol.c | 46 +- 13 files changed, 1409 insertions(+), 63 deletions(-) create mode 100644 ubi-utils/inc/libubi.h create mode 100644 ubi-utils/src/libubi.c create mode 100644 ubi-utils/src/libubi_int.h diff --git a/ubi-utils/Makefile b/ubi-utils/Makefile index 0818a9b..797807d 100644 --- a/ubi-utils/Makefile +++ b/ubi-utils/Makefile @@ -16,11 +16,12 @@ 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)\" + -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 + bin2nand nand2bin ubigen mkbootenv unubi pfi2bin ubicrc32 vpath %.c ./src @@ -38,25 +39,25 @@ IGNORE=${wildcard .*.c.dep} clean: rm -rf *.o $(TARGETS) .*.c.dep -ubiupdatevol: ubiupdatevol.o error.o libubi.o libubi_sysfs.o +ubiupdatevol: ubiupdatevol.o error.o libubi.o $(CC) $(LDFLAGS) -o $@ $^ -ubimkvol: ubimkvol.o error.o libubi.o libubi_sysfs.o +ubimkvol: ubimkvol.o error.o libubi.o $(CC) $(LDFLAGS) -o $@ $^ -ubirmvol: ubirmvol.o error.o libubi.o libubi_sysfs.o +ubirmvol: ubirmvol.o error.o libubi.o $(CC) $(LDFLAGS) -o $@ $^ pddcustomize: pddcustomize.o error.o libubimirror.o bootenv.o hashmap.o \ - libubi.o libubi_sysfs.o crc32.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 libubi.o libubi_sysfs.o crc32.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 \ - libubi.o libubi_sysfs.o crc32.o + libubiold.o libubiold_sysfs.o crc32.o $(CC) $(LDFLAGS) -o $@ $^ nand2bin: nand2bin.o nandecc.o nandcorr.o @@ -78,6 +79,9 @@ 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}/ 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 +#include +#include +#include + +#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 ) + * + * 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/src/libpfiflash.c b/ubi-utils/src/libpfiflash.c index 4dfafea..4f1f5cd 100644 --- a/ubi-utils/src/libpfiflash.c +++ b/ubi-utils/src/libpfiflash.c @@ -36,7 +36,7 @@ #include #include -#include +#include #include #include /* FIXME Is this ok here? */ 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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/libubimirror.c b/ubi-utils/src/libubimirror.c index e5715fc..d8ea548 100644 --- a/ubi-utils/src/libubimirror.c +++ b/ubi-utils/src/libubimirror.c @@ -22,7 +22,7 @@ #include #include -#include +#include #include "ubimirror.h" #define COMPARE_BUF_SIZE (128 * 1024) @@ -207,11 +207,11 @@ ubimirror(uint32_t devno, int seqnum, uint32_t *ids, ssize_t ids_size, fd_out = -1; } err: - if (ulib != NULL) - ubi_close(&ulib); - if (fd_in != -1) - ubi_vol_close(fd_in); 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 index da4919b..0ff8bae 100644 --- a/ubi-utils/src/libubiold.c +++ b/ubi-utils/src/libubiold.c @@ -36,9 +36,9 @@ #include #include -#include "libubi.h" -#include "libubi_int.h" -#include "libubi_sysfs.h" +#include "libubiold.h" +#include "libubiold_int.h" +#include "libubiold_sysfs.h" /** * struct ubi_lib - UBI library descriptor. diff --git a/ubi-utils/src/libubiold_sysfs.c b/ubi-utils/src/libubiold_sysfs.c index 95fd3de..c4860f6 100644 --- a/ubi-utils/src/libubiold_sysfs.c +++ b/ubi-utils/src/libubiold_sysfs.c @@ -33,7 +33,7 @@ #include #include "config.h" -#include "libubi_int.h" +#include "libubiold_int.h" int sysfs_read_data(const char *file, void *buf, int len) diff --git a/ubi-utils/src/pddcustomize.c b/ubi-utils/src/pddcustomize.c index 764f2e7..a86e942 100644 --- a/ubi-utils/src/pddcustomize.c +++ b/ubi-utils/src/pddcustomize.c @@ -41,7 +41,7 @@ #include "bootenv.h" #include "error.h" #include "example_ubi.h" -#include "libubi.h" +#include "libubiold.h" #include "ubimirror.h" #define PROGRAM_VERSION "1.4" diff --git a/ubi-utils/src/reader.c b/ubi-utils/src/reader.c index 7935a15..0ea8c6d 100644 --- a/ubi-utils/src/reader.c +++ b/ubi-utils/src/reader.c @@ -142,6 +142,7 @@ read_pfi_raw(pfi_header pfi_hd, FILE* fp_pfi __unused, pfi_raw_t* pfi_raw, 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) @@ -178,8 +179,9 @@ read_pfi_raw(pfi_header pfi_hd, FILE* fp_pfi __unused, pfi_raw_t* pfi_raw, } rc = bootenv_list_to_num_vector(raw_start_list, - (void *) &(res->starts_size), - &(res->starts)); + &size, &(res->starts)); + res->starts_size = size; + if (rc != 0) { EBUF_PFI("Cannot create numeric value array: %s", tmp_str); goto err; @@ -209,6 +211,7 @@ read_pfi_ubi(pfi_header pfi_hd, FILE* fp_pfi __unused, pfi_ubi_t* pfi_ubi, 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) @@ -247,8 +250,9 @@ read_pfi_ubi(pfi_header pfi_hd, FILE* fp_pfi __unused, pfi_ubi_t* pfi_ubi, goto err; } - rc = bootenv_list_to_num_vector(ubi_id_list, (void *) &(res->ids_size), + 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; @@ -299,8 +303,9 @@ read_pfi_ubi(pfi_header pfi_hd, FILE* fp_pfi __unused, pfi_ubi_t* pfi_ubi, EBUF_PFI("Cannot translate PFI value: %s", tmp_str); goto err; } - rc = bootenv_list_to_vector(ubi_name_list, (void *) &(res->names_size), + 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; diff --git a/ubi-utils/src/ubimkvol.c b/ubi-utils/src/ubimkvol.c index 879dcb6..1368671 100644 --- a/ubi-utils/src/ubimkvol.c +++ b/ubi-utils/src/ubimkvol.c @@ -28,6 +28,7 @@ * 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 @@ -40,7 +41,7 @@ #include #include -#define PROGRAM_VERSION "1.4" +#define PROGRAM_VERSION "1.5" /* * The variables below are set by command line arguments. @@ -53,6 +54,7 @@ struct args { int alignment; char *name; int nlen; + char node[256]; /* special stuff needed to get additional arguments */ char *arg1; @@ -69,7 +71,7 @@ static struct args myargs = { .nlen = 0, }; -static int param_sanity_check(struct args *args, ubi_lib_t lib); +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" @@ -177,6 +179,7 @@ parse_opt(int argc, char **argv, struct args *args) "\"%s\"\n", optarg); goto out; } + sprintf(args->node, "/dev/ubi%d", args->devn); break; case 'n': /* --volid= */ args->vol_id = strtoul(optarg, &endp, 0); @@ -224,7 +227,7 @@ parse_opt(int argc, char **argv, struct args *args) return -1; } -static int param_sanity_check(struct args *args, ubi_lib_t lib) +static int param_sanity_check(struct args *args, libubi_t libubi) { int err, len; struct ubi_info ubi; @@ -239,7 +242,7 @@ static int param_sanity_check(struct args *args, ubi_lib_t lib) goto out; } - err = ubi_get_info(lib, &ubi); + err = ubi_get_info(libubi, &ubi); if (err) return -1; @@ -264,7 +267,8 @@ out: int main(int argc, char * const argv[]) { int err; - ubi_lib_t lib; + libubi_t libubi; + struct ubi_mkvol_request req; err = parse_opt(argc, (char **)argv, &myargs); if (err) { @@ -278,21 +282,26 @@ int main(int argc, char * const argv[]) return -1; } - err = ubi_open(&lib); - if (err) { + libubi = libubi_open(); + if (libubi == NULL) { perror("Cannot open libubi"); return -1; } - err = param_sanity_check(&myargs, lib); + 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_mkvol(lib, myargs.devn, myargs.vol_id, myargs.vol_type, - myargs.bytes, myargs.alignment, myargs.name); + 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); @@ -304,10 +313,10 @@ int main(int argc, char * const argv[]) "dynamic" : "static", name); */ myargs.vol_id = err; - ubi_close(&lib); + libubi_close(libubi); return 0; out_libubi: - ubi_close(&lib); + libubi_close(libubi); return -1; } diff --git a/ubi-utils/src/ubirmvol.c b/ubi-utils/src/ubirmvol.c index f458e8a..f32cbe0 100644 --- a/ubi-utils/src/ubirmvol.c +++ b/ubi-utils/src/ubirmvol.c @@ -25,6 +25,7 @@ * 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 @@ -37,7 +38,7 @@ #include #include -#define PROGRAM_VERSION "1.3" +#define PROGRAM_VERSION "1.4" /* * The below variables are set by command line options. @@ -45,6 +46,7 @@ struct args { int devn; int vol_id; + char node[256]; /* special stuff needed to get additional arguments */ char *arg1; @@ -59,7 +61,7 @@ static struct args myargs = { .options = NULL, }; -static int param_sanity_check(struct args *args, ubi_lib_t lib); +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" @@ -118,6 +120,7 @@ parse_opt(int argc, char **argv, struct args *args) "\"%s\"\n", optarg); goto out; } + sprintf(args->node, "/dev/ubi%d", args->devn); break; case 'n': /* --volid= */ args->vol_id = strtoul(optarg, &endp, 0); @@ -156,7 +159,7 @@ parse_opt(int argc, char **argv, struct args *args) return -1; } -static int param_sanity_check(struct args *args, ubi_lib_t lib) +static int param_sanity_check(struct args *args, libubi_t libubi) { int err; struct ubi_info ubi; @@ -166,7 +169,7 @@ static int param_sanity_check(struct args *args, ubi_lib_t lib) goto out; } - err = ubi_get_info(lib, &ubi); + err = ubi_get_info(libubi, &ubi); if (err) return -1; @@ -185,7 +188,7 @@ out: int main(int argc, char * const argv[]) { int err, old_errno; - ubi_lib_t lib; + libubi_t libubi; err = parse_opt(argc, (char **)argv, &myargs); if (err) @@ -197,20 +200,20 @@ int main(int argc, char * const argv[]) return -1; } - err = ubi_open(&lib); - if (err) { + libubi = libubi_open(); + if (libubi == NULL) { perror("Cannot open libubi"); return -1; } - err = param_sanity_check(&myargs, lib); + 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(lib, myargs.devn, myargs.vol_id); + err = ubi_rmvol(libubi, myargs.node, myargs.vol_id); old_errno = errno; if (err < 0) { perror("Cannot remove volume"); @@ -218,9 +221,10 @@ int main(int argc, char * const argv[]) goto out_libubi; } + libubi_close(libubi); return 0; out_libubi: - ubi_close(&lib); + libubi_close(libubi); return -1; } diff --git a/ubi-utils/src/ubiupdatevol.c b/ubi-utils/src/ubiupdatevol.c index c0b4178..5401eb1 100644 --- a/ubi-utils/src/ubiupdatevol.c +++ b/ubi-utils/src/ubiupdatevol.c @@ -25,6 +25,7 @@ * 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 @@ -36,14 +37,12 @@ #include #include #include -#include #include -#include #include #include -#define PROGRAM_VERSION "1.2" +#define PROGRAM_VERSION "1.3" #define MAXPATH 1024 #define BUFSIZE 128 * 1024 @@ -174,7 +173,7 @@ parse_opt(int argc, char **argv, struct args *args) * some reason nothing is written. The volume is unusable after this. */ static int -ubi_truncate_volume(struct args *args, int64_t bytes) +ubi_truncate_volume(struct args *args, int64_t bytes,libubi_t libubi) { int rc, ofd; char path[MAXPATH]; @@ -188,7 +187,7 @@ ubi_truncate_volume(struct args *args, int64_t bytes) fprintf(stderr, "Cannot open volume %s\n", path); exit(EXIT_FAILURE); } - rc = ioctl(ofd, UBI_IOCVOLUP, &bytes); + rc = ubi_update_start(libubi, ofd, bytes); old_errno = errno; if (rc < 0) { perror("UBI volume update ioctl"); @@ -220,7 +219,7 @@ static ssize_t ubi_write(int fd, const void *buf, size_t count) } static int -ubi_update_volume(struct args *args) +ubi_update_volume(struct args *args, libubi_t libubi) { int rc, ofd; FILE *ifp = NULL; @@ -263,7 +262,7 @@ ubi_update_volume(struct args *args) exit(EXIT_FAILURE); } - rc = ioctl(ofd, UBI_IOCVOLUP, &bytes); + rc = ubi_update_start(libubi, ofd, bytes); old_errno = errno; if (rc < 0) { perror("UBI volume update ioctl"); @@ -304,24 +303,35 @@ 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); + rc = ubi_truncate_volume(&myargs, 0LL, libubi); if (rc < 0) - exit(EXIT_FAILURE); - exit(EXIT_SUCCESS); + goto out_libubi; } - if (myargs.broken_update) { - rc = ubi_truncate_volume(&myargs, 1LL); + else if (myargs.broken_update) { + rc = ubi_truncate_volume(&myargs, 1LL, libubi); if (rc < 0) - exit(EXIT_FAILURE); - exit(EXIT_SUCCESS); + goto out_libubi; + } else { + rc = ubi_update_volume(&myargs, libubi); + if (rc < 0) + goto out_libubi; } - rc = ubi_update_volume(&myargs); - if (rc < 0) - exit(EXIT_FAILURE); - exit(EXIT_SUCCESS); + libubi_close(libubi); + return 0; + +out_libubi: + libubi_close(libubi); + return -1; } -- cgit v1.2.3 From 842f19e7a67e0dd9ca53d1760fb8b3f2c94ab826 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 21 Mar 2007 11:54:35 +0200 Subject: UBI-Utils: Add a run_all.sh script Signed-off-by: Adrian Hunter --- ubi-utils/scripts/Makefile | 2 +- ubi-utils/scripts/run_all.sh | 101 ++++++++++++++++++++++++++++++++++++ ubi-utils/scripts/ubi_jffs2_test.sh | 3 +- 3 files changed, 103 insertions(+), 3 deletions(-) create mode 100755 ubi-utils/scripts/run_all.sh mode change 100644 => 100755 ubi-utils/scripts/ubi_jffs2_test.sh diff --git a/ubi-utils/scripts/Makefile b/ubi-utils/scripts/Makefile index e8850fd..ebd9bc6 100644 --- a/ubi-utils/scripts/Makefile +++ b/ubi-utils/scripts/Makefile @@ -15,7 +15,7 @@ mkpfi_cfg = test.cfg # on those values. If you do it wrong your card has no valid PDD data. # -PATH := $(PATH):/opt/ppc/usr/bin +PATH := $(PATH):/opt/ppc/usr/bin:../perl:.. dd = dd sed = sed 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 " + 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/ubi_jffs2_test.sh b/ubi-utils/scripts/ubi_jffs2_test.sh old mode 100644 new mode 100755 index 4d97431..883903d --- a/ubi-utils/scripts/ubi_jffs2_test.sh +++ b/ubi-utils/scripts/ubi_jffs2_test.sh @@ -216,8 +216,7 @@ writevol_test () jffs2_torture () { - rm -f $TLOG - touch $TLOG + cat /dev/null > TLOG echo "*** Torture test ... " -- cgit v1.2.3 From 22f90673165489fd50c893a91051a79c1b143d2a Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 21 Mar 2007 11:55:30 +0200 Subject: UBI-Utils: Add Artem's test programs Signed-off-by: Adrian Hunter --- ubi-utils/tests/Makefile | 40 +++ ubi-utils/tests/common.c | 336 ++++++++++++++++++ ubi-utils/tests/common.h | 103 ++++++ ubi-utils/tests/integ.c | 783 ++++++++++++++++++++++++++++++++++++++++++ ubi-utils/tests/io_basic.c | 182 ++++++++++ ubi-utils/tests/io_paral.c | 251 ++++++++++++++ ubi-utils/tests/io_read.c | 398 +++++++++++++++++++++ ubi-utils/tests/io_update.c | 370 ++++++++++++++++++++ ubi-utils/tests/mkvol_bad.c | 311 +++++++++++++++++ ubi-utils/tests/mkvol_basic.c | 249 ++++++++++++++ ubi-utils/tests/mkvol_paral.c | 112 ++++++ ubi-utils/tests/rsvol.c | 310 +++++++++++++++++ ubi-utils/tests/runtests.pl | 30 ++ ubi-utils/tests/runtests.sh | 41 +++ 14 files changed, 3516 insertions(+) create mode 100644 ubi-utils/tests/Makefile create mode 100644 ubi-utils/tests/common.c create mode 100644 ubi-utils/tests/common.h create mode 100644 ubi-utils/tests/integ.c create mode 100644 ubi-utils/tests/io_basic.c create mode 100644 ubi-utils/tests/io_paral.c create mode 100644 ubi-utils/tests/io_read.c create mode 100644 ubi-utils/tests/io_update.c create mode 100644 ubi-utils/tests/mkvol_bad.c create mode 100644 ubi-utils/tests/mkvol_basic.c create mode 100644 ubi-utils/tests/mkvol_paral.c create mode 100644 ubi-utils/tests/rsvol.c create mode 100755 ubi-utils/tests/runtests.pl create mode 100755 ubi-utils/tests/runtests.sh 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 +#include +#include +#include +#include +#include +#include +#include +#include +#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 + +#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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#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 [] \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 +#include +#include +#include +#include +#include +#include +#include +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#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 +#include +#include +#include +#include +#include +#include +#include +#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 +#include +#include +#include +#include +#include +#include +#include +#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 +#include +#include +#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 +#include +#include +#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 +#include +#include +#include +#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 +#include +#include +#include +#include +#include +#include +#include +#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 ...\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 " + 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 -- cgit v1.2.3