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 (limited to 'ubi-utils') 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