]> www.infradead.org Git - mtd-utils.git/commitdiff
ubi-utils: save more files
authorArtem Bityutskiy <Artem.Bityutskiy@nokia.com>
Tue, 22 Jan 2008 14:00:54 +0000 (16:00 +0200)
committerArtem Bityutskiy <Artem.Bityutskiy@nokia.com>
Tue, 22 Jan 2008 14:04:03 +0000 (16:04 +0200)
Signed-off-by: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
ubi-utils/sort-me-out/bootenv.c [new file with mode: 0644]
ubi-utils/sort-me-out/bootenv.h [new file with mode: 0644]
ubi-utils/sort-me-out/config.h [new file with mode: 0644]
ubi-utils/sort-me-out/crc32.c [new file with mode: 0644]
ubi-utils/sort-me-out/crc32.h [new file with mode: 0644]
ubi-utils/sort-me-out/eb_chain.c [new file with mode: 0644]
ubi-utils/sort-me-out/example_ubi.h [new file with mode: 0644]
ubi-utils/sort-me-out/hashmap.c [new file with mode: 0644]
ubi-utils/sort-me-out/hashmap.h [new file with mode: 0644]
ubi-utils/sort-me-out/pfi2bin.c [new file with mode: 0644]

diff --git a/ubi-utils/sort-me-out/bootenv.c b/ubi-utils/sort-me-out/bootenv.c
new file mode 100644 (file)
index 0000000..a6dd4de
--- /dev/null
@@ -0,0 +1,1032 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Oliver Lohmann
+ */
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <sys/stat.h>
+#include <bootenv.h>
+
+#include "hashmap.h"
+#include "error.h"
+
+#include <mtd/ubi-header.h>
+#include "crc32.h"
+
+#define ubi_unused __attribute__((unused))
+
+#define BOOTENV_MAXLINE 512 /* max line size of a bootenv.txt file */
+
+/* Structures */
+struct bootenv {
+       hashmap_t map;   ///< Pointer to hashmap which holds data structure.
+};
+
+struct bootenv_list {
+       hashmap_t head; ///< Pointer to list which holds the data structure.
+};
+
+/**
+ * @brief Remove the '\n' from a given line.
+ * @param line Input/Output line.
+ * @param size Size of the line.
+ * @param fp   File Pointer.
+ * @return 0
+ * @return or error
+ */
+static int
+remove_lf(char *line, size_t size, FILE* fp)
+{
+       size_t i;
+
+       for (i = 0; i < size; i++) {
+               if (line[i] == '\n') {
+                       line[i] = '\0';
+                       return 0;
+               }
+       }
+
+       if (!feof(fp)) {
+               return BOOTENV_EINVAL;
+       }
+
+       return 0;
+}
+
+/**
+ * @brief Determine if a line contains only WS.
+ * @param line The line to process.
+ * @param size Size of input line.
+ * @return 1   Yes, only WS.
+ * @return 0   No, contains data.
+ */
+static int
+is_ws(const char *line, size_t size)
+{
+       size_t i = 0;
+
+       while (i < size) {
+               switch (line[i]) {
+                       case '\n':
+                               return 1;
+                       case '#':
+                               return 1;
+                       case ' ':
+                               i++;
+                               continue;
+                       case '\t':
+                               i++;
+                               continue;
+                       default: /* any other char -> no cmnt */
+                               return 0;
+               }
+       }
+
+       return 0;
+}
+
+
+/* ------------------------------------------------------------------------- */
+
+/**
+ * @brief Build a list from a comma seperated value string.
+ * @param list Pointer to hashmap structure which shall store
+ *             the list.
+ * @param value        Comma seperated value string.
+ * @return 0
+ * @return or error.
+ */
+static int
+build_list_definition(hashmap_t list, const char *value)
+{
+       int rc = 0;
+       char *str = NULL;
+       char *ptr = NULL;
+       size_t len, i, j;
+
+       /* str: val1,val2 , val4,...,valN     */
+       len = strlen(value);
+       str = (char*) malloc((len+1) * sizeof(char));
+
+       /* 1. reformat string: remove spaces */
+       for (i = 0, j = 0; i < len; i++) {
+               if (value[i] == ' ')
+                       continue;
+
+               str[j] = value[i];
+               j++;
+       }
+       str[j] = '\0';
+
+       /* str: val1,val2,val4,...,valN\0*/
+       /* 2. replace ',' seperator with '\0' */
+       len = strlen(str);
+       for (i = 0; i < len; i++) {
+               if (str[i] == ',') {
+                       str[i] = '\0';
+               }
+       }
+
+       /* str: val1\0val2\0val4\0...\0valN\0*/
+       /* 3. insert definitions into a hash map, using it like a list */
+       i = j = 0;
+       ptr = str;
+       while (((i = strlen(ptr)) > 0) && (j < len)) {
+               rc = hashmap_add(list, ptr, "");
+               if (rc != 0) {
+                       free(str);
+                       return rc;
+               }
+               j += i+1;
+               if (j < len)
+                       ptr += i+1;
+       }
+
+       free(str);
+       return rc;
+}
+
+/**
+ * @brief Extract a key value pair and add it to a hashmap
+ * @param str  Input string which contains a key value pair.
+ * @param env  The updated handle which contains the new pair.
+ * @return 0
+ * @return or error
+ * @note The input string format is: "key=value"
+ */
+static int
+extract_pair(const char *str, bootenv_t env)
+{
+       int rc = 0;
+       char *key = NULL;
+       char *val = NULL;
+
+       key = strdup(str);
+       if (key == NULL)
+               return -ENOMEM;
+
+       val = strstr(key, "=");
+       if (val == NULL) {
+               rc = BOOTENV_EBADENTRY;
+               goto err;
+       }
+
+       *val = '\0'; /* split strings */
+       val++;
+
+       rc = bootenv_set(env, key, val);
+
+ err:
+       free(key);
+       return rc;
+}
+
+int
+bootenv_destroy(bootenv_t* env)
+{
+       int rc = 0;
+
+       if (env == NULL || *env == NULL)
+               return -EINVAL;
+
+       bootenv_t tmp = *env;
+
+       rc = hashmap_free(tmp->map);
+       if (rc != 0)
+               return rc;
+
+       free(tmp);
+       return rc;
+}
+
+int
+bootenv_create(bootenv_t* env)
+{
+       bootenv_t res;
+       res = (bootenv_t) calloc(1, sizeof(struct bootenv));
+
+       if (res == NULL)
+               return -ENOMEM;
+
+       res->map = hashmap_new();
+
+       if (res->map == NULL) {
+               free(res);
+               return -ENOMEM;
+       }
+
+       *env = res;
+
+       return 0;
+}
+
+
+/**
+ * @brief Read a formatted buffer and scan it for valid bootenv
+ *       key/value pairs. Add those pairs into a hashmap.
+ * @param env  Hashmap which shall be used to hold the data.
+ * @param buf  Formatted buffer.
+ * @param size Size of the buffer.
+ * @return 0
+ * @return or error
+ */
+static int
+rd_buffer(bootenv_t env, const char *buf, size_t size)
+{
+       const char *curr = buf;         /* ptr to current key/value pair */
+       uint32_t i, j;                  /* current length, chars processed */
+
+       if (buf[size - 1] != '\0')      /* must end in '\0' */
+               return BOOTENV_EFMT;
+
+       for (j = 0; j < size; j += i, curr += i) {
+               /* strlen returns the size of the string upto
+                  but not including the null terminator;
+                  adding 1 to account for '\0' */
+               i = strlen(curr) + 1;
+
+               if (i == 1)
+                       return 0;       /* no string found */
+
+               if (extract_pair(curr, env) != 0)
+                       return BOOTENV_EINVAL;
+       }
+
+       return 0;
+}
+
+
+int
+bootenv_read_crc(FILE* fp, bootenv_t env, size_t size, uint32_t* ret_crc)
+{
+       int rc;
+       char *buf = NULL;
+       size_t i = 0;
+       uint32_t crc32_table[256];
+
+       if ((fp == NULL) || (env == NULL))
+               return -EINVAL;
+
+       /* allocate temp buffer */
+       buf = (char*) calloc(1, size * sizeof(char));
+       if (buf == NULL)
+               return -ENOMEM;
+
+       /* FIXME Andreas, please review this I removed size-1 and
+        * replaced it by just size, I saw the kernel image starting
+        * with a 0x0060.... and not with the 0x60.... what it should
+        * be. Is this a tools problem or is it a problem here where
+        * fp is moved not to the right place due to the former size-1
+        * here.
+        */
+       while((i < size) && (!feof(fp))) {
+               int c = fgetc(fp);
+               if (c == EOF) {
+                       /* FIXME isn't this dangerous, to update
+                          the boot envs with incomplete data? */
+                       buf[i++] = '\0';
+                       break;  /* we have enough */
+               }
+               if (ferror(fp)) {
+                       rc = -EIO;
+                       goto err;
+               }
+
+               buf[i++] = (char)c;
+       }
+
+       /* calculate crc to return */
+       if (ret_crc != NULL) {
+               init_crc32_table(crc32_table);
+               *ret_crc = clc_crc32(crc32_table, UBI_CRC32_INIT, buf, size);
+       }
+
+       /* transfer to hashmap */
+       rc = rd_buffer(env, buf, size);
+
+err:
+       free(buf);
+       return rc;
+}
+
+
+/**
+ * If we have a single file containing the boot-parameter size should
+ * be specified either as the size of the file or as BOOTENV_MAXSIZE.
+ * If the bootparameter are in the middle of a file we need the exact
+ * length of the data.
+ */
+int
+bootenv_read(FILE* fp, bootenv_t env, size_t size)
+{
+       return bootenv_read_crc(fp, env, size, NULL);
+}
+
+
+int
+bootenv_read_txt(FILE* fp, bootenv_t env)
+{
+       int rc = 0;
+       char *buf = NULL;
+       char *line = NULL;
+       char *lstart = NULL;
+       char *curr = NULL;
+       size_t len;
+       size_t size;
+
+       if ((fp == NULL) || (env == NULL))
+               return -EINVAL;
+
+       size = BOOTENV_MAXSIZE;
+
+       /* allocate temp buffers */
+       buf = (char*) calloc(1, size * sizeof(char));
+       lstart = line = (char*) calloc(1, size * sizeof(char));
+       if ((buf == NULL)  || (line == NULL)) {
+               rc = -ENOMEM;
+               goto err;
+       }
+
+       curr = buf;
+       while ((line = fgets(line, size, fp)) != NULL) {
+               if (is_ws(line, size)) {
+                       continue;
+               }
+               rc = remove_lf(line, BOOTENV_MAXSIZE, fp);
+               if (rc != 0) {
+                       goto err;
+               }
+
+               /* copy new line to binary buffer */
+               len = strlen(line);
+               if (len > size) {
+                       rc = -EFBIG;
+                       goto err;
+               }
+               size -= len; /* track remaining space */
+
+               memcpy(curr, line, len);
+               curr += len + 1; /* for \0 seperator */
+       }
+
+       rc = rd_buffer(env, buf, BOOTENV_MAXSIZE);
+err:
+       if (buf != NULL)
+               free(buf);
+       if (lstart != NULL)
+               free(lstart);
+       return rc;
+}
+
+static int
+fill_output_buffer(bootenv_t env, char *buf, size_t buf_size_max ubi_unused,
+               size_t *written)
+{
+       int rc = 0;
+       size_t keys_size, i;
+       size_t wr = 0;
+       const char **keys = NULL;
+       const char *val = NULL;
+
+       rc = bootenv_get_key_vector(env, &keys_size, 1, &keys);
+       if (rc != 0)
+               goto err;
+
+       for (i = 0; i < keys_size; i++) {
+               if (wr > BOOTENV_MAXSIZE) {
+                       rc = -ENOSPC;
+                       goto err;
+               }
+
+               rc = bootenv_get(env, keys[i], &val);
+               if (rc != 0)
+                       goto err;
+
+               wr += snprintf(buf + wr, BOOTENV_MAXSIZE - wr,
+                               "%s=%s", keys[i], val);
+               wr++; /* for \0 */
+       }
+
+       *written = wr;
+
+err:
+       if (keys != NULL)
+               free(keys);
+
+       return rc;
+}
+
+int
+bootenv_write_crc(FILE* fp, bootenv_t env, uint32_t* ret_crc)
+{
+       int rc = 0;
+       size_t size = 0;
+       char *buf = NULL;
+       uint32_t crc32_table[256];
+
+       if ((fp == NULL) || (env == NULL))
+               return -EINVAL;
+
+       buf = (char*) calloc(1, BOOTENV_MAXSIZE * sizeof(char));
+       if (buf == NULL)
+               return -ENOMEM;
+
+
+       rc = fill_output_buffer(env, buf, BOOTENV_MAXSIZE, &size);
+       if (rc != 0)
+               goto err;
+
+       /* calculate crc to return */
+       if (ret_crc != NULL) {
+               init_crc32_table(crc32_table);
+               *ret_crc = clc_crc32(crc32_table, UBI_CRC32_INIT, buf, size);
+       }
+
+       if (fwrite(buf, size, 1, fp) != 1) {
+               rc = -EIO;
+               goto err;
+       }
+
+err:
+       if (buf != NULL)
+               free(buf);
+       return rc;
+}
+
+int
+bootenv_write(FILE* fp, bootenv_t env)
+{
+       return bootenv_write_crc(fp, env, NULL);
+}
+
+int
+bootenv_compare(bootenv_t first, bootenv_t second)
+{
+       int rc;
+       size_t written_first, written_second;
+       char *buf_first, *buf_second;
+
+       if (first == NULL || second == NULL)
+               return -EINVAL;
+
+       buf_first = malloc(BOOTENV_MAXSIZE);
+       if (!buf_first)
+               return -ENOMEM;
+       buf_second = malloc(BOOTENV_MAXSIZE);
+       if (!buf_second) {
+               rc = -ENOMEM;
+               goto err;
+       }
+
+       rc = fill_output_buffer(first, buf_first, BOOTENV_MAXSIZE,
+                       &written_first);
+       if (rc < 0)
+               goto err;
+       rc = fill_output_buffer(second, buf_second, BOOTENV_MAXSIZE,
+                       &written_second);
+       if (rc < 0)
+               goto err;
+
+       if (written_first != written_second) {
+               rc = 1;
+               goto err;
+       }
+
+       rc = memcmp(buf_first, buf_second, written_first);
+       if (rc != 0) {
+               rc = 2;
+               goto err;
+       }
+
+err:
+       if (buf_first)
+               free(buf_first);
+       if (buf_second)
+               free(buf_second);
+
+       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 ubi_unused)
+{
+       /* @FIXME No sanity check implemented. */
+       return 0;
+}
+
+int
+bootenv_copy_bootenv(bootenv_t in, bootenv_t *out)
+{
+       int rc = 0;
+       const char *tmp = NULL;
+       const char **keys = NULL;
+       size_t vec_size, i;
+
+       if ((in == NULL) || (out == NULL))
+               return -EINVAL;
+
+       /* purge output var for sure... */
+       rc = bootenv_destroy(out);
+       if (rc != 0)
+               return rc;
+
+       /* create the new map  */
+       rc = bootenv_create(out);
+       if (rc != 0)
+               goto err;
+
+       /* get the key list from the input map */
+       rc = bootenv_get_key_vector(in, &vec_size, 0, &keys);
+       if (rc != 0)
+               goto err;
+
+       if (vec_size != hashmap_size(in->map)) {
+               rc = BOOTENV_ECOPY;
+               goto err;
+       }
+
+       /* make a deep copy of the hashmap */
+       for (i = 0; i < vec_size; i++) {
+               rc = bootenv_get(in, keys[i], &tmp);
+               if (rc != 0)
+                       goto err;
+
+               rc = bootenv_set(*out, keys[i], tmp);
+               if (rc != 0)
+                       goto err;
+       }
+
+err:
+       if (keys != NULL)
+               free(keys);
+
+       return rc;
+}
+
+/* ------------------------------------------------------------------------- */
+
+
+int
+bootenv_pdd_keep(bootenv_t env_old, bootenv_t env_new, bootenv_t *env_res,
+                int *warnings, char *err_buf ubi_unused,
+                size_t err_buf_size ubi_unused)
+{
+       bootenv_list_t l_old = NULL;
+       bootenv_list_t l_new = NULL;
+       const char *pdd_old = NULL;
+       const char *pdd_new = NULL;
+       const char *tmp = NULL;
+       const char **vec_old = NULL;
+       const char **vec_new = NULL;
+       const char **pdd_up_vec = NULL;
+       size_t vec_old_size, vec_new_size, pdd_up_vec_size, i;
+       int rc = 0;
+
+       if ((env_old == NULL) || (env_new == NULL) || (env_res == NULL))
+               return -EINVAL;
+
+       /* get the pdd strings, e.g.:
+        * pdd_old=a,b,c
+        * pdd_new=a,c,d,e */
+       rc = bootenv_get(env_old, "pdd", &pdd_old);
+       if (rc != 0)
+               goto err;
+       rc = bootenv_get(env_new, "pdd", &pdd_new);
+       if (rc != 0)
+               goto err;
+
+       /* put it into a list and then convert it to an vector */
+       rc = bootenv_list_create(&l_old);
+       if (rc != 0)
+               goto err;
+       rc  = bootenv_list_create(&l_new);
+       if (rc != 0)
+               goto err;
+
+       rc = bootenv_list_import(l_old, pdd_old);
+       if (rc != 0)
+               goto err;
+
+       rc = bootenv_list_import(l_new, pdd_new);
+       if (rc != 0)
+               goto err;
+
+       rc = bootenv_list_to_vector(l_old, &vec_old_size, &vec_old);
+       if (rc != 0)
+               goto err;
+
+       rc = bootenv_list_to_vector(l_new, &vec_new_size, &vec_new);
+       if (rc != 0)
+               goto err;
+
+       rc = bootenv_copy_bootenv(env_new, env_res);
+       if (rc != 0)
+               goto err;
+
+       /* calculate the update vector between the old and new pdd */
+       pdd_up_vec = hashmap_get_update_key_vector(vec_old, vec_old_size,
+                       vec_new, vec_new_size, &pdd_up_vec_size);
+
+       if (pdd_up_vec == NULL) {
+               rc = -ENOMEM;
+               goto err;
+       }
+
+       if (pdd_up_vec_size != 0) {
+               /* need to warn the user about the unset of
+                * some pdd/bootenv values */
+               *warnings = BOOTENV_WPDD_STRING_DIFFERS;
+
+               /* remove all entries in the new bootenv load */
+               for (i = 0; i < pdd_up_vec_size; i++) {
+                       bootenv_unset(*env_res, pdd_up_vec[i]);
+               }
+       }
+
+       /* generate the keep array and copy old pdd values to new bootenv */
+       for (i = 0; i < vec_old_size; i++) {
+               rc = bootenv_get(env_old, vec_old[i], &tmp);
+               if (rc != 0) {
+                       rc = BOOTENV_EPDDINVAL;
+                       goto err;
+               }
+               rc = bootenv_set(*env_res, vec_old[i], tmp);
+               if (rc != 0) {
+                       goto err;
+               }
+       }
+       /* put the old pdd string into the result map */
+       rc = bootenv_set(*env_res, "pdd", pdd_old);
+       if (rc != 0) {
+               goto err;
+       }
+
+
+err:
+       if (vec_old != NULL)
+               free(vec_old);
+       if (vec_new != NULL)
+               free(vec_new);
+       if (pdd_up_vec != NULL)
+               free(pdd_up_vec);
+
+       bootenv_list_destroy(&l_old);
+       bootenv_list_destroy(&l_new);
+       return rc;
+}
+
+
+int
+bootenv_pdd_overwrite(bootenv_t env_old, bootenv_t env_new,
+                     bootenv_t *env_res, int *warnings ubi_unused,
+                     char *err_buf ubi_unused, size_t err_buf_size ubi_unused)
+{
+       if ((env_old == NULL) || (env_new == NULL) || (env_res == NULL))
+               return -EINVAL;
+
+       return bootenv_copy_bootenv(env_new, env_res);
+}
+
+int
+bootenv_pdd_merge(bootenv_t env_old, bootenv_t env_new, bootenv_t *env_res,
+                 int *warnings ubi_unused, char *err_buf, size_t err_buf_size)
+{
+       if ((env_old == NULL) || (env_new == NULL) || (env_res == NULL))
+               return -EINVAL;
+
+       snprintf(err_buf, err_buf_size, "The PDD merge operation is not "
+                       "implemented. Contact: <oliloh@de.ibm.com>");
+
+       return BOOTENV_ENOTIMPL;
+}
+
+/* ------------------------------------------------------------------------- */
+
+int
+bootenv_get(bootenv_t env, const char *key, const char **value)
+{
+       if (env == NULL)
+               return -EINVAL;
+
+       *value = hashmap_lookup(env->map, key);
+       if (*value == NULL)
+               return BOOTENV_ENOTFOUND;
+
+       return 0;
+}
+
+int
+bootenv_get_num(bootenv_t env, const char *key, uint32_t *value)
+{
+       char *endptr = NULL;
+       const char *str;
+
+       if (env == NULL)
+               return 0;
+
+       str = hashmap_lookup(env->map, key);
+       if (!str)
+               return -EINVAL;
+
+       *value = strtoul(str, &endptr, 0);
+
+       if (*endptr == '\0') {
+               return 0;
+       }
+
+       return -EINVAL;
+}
+
+int
+bootenv_set(bootenv_t env, const char *key, const char *value)
+{
+       if (env == NULL)
+               return -EINVAL;
+
+       return hashmap_add(env->map, key, value);
+}
+
+int
+bootenv_unset(bootenv_t env, const char *key)
+{
+       if (env == NULL)
+               return -EINVAL;
+
+       return hashmap_remove(env->map, key);
+}
+
+int
+bootenv_get_key_vector(bootenv_t env, size_t* size, int sort,
+                      const char ***vector)
+{
+       if ((env == NULL) || (size == NULL))
+               return -EINVAL;
+
+       *vector = hashmap_get_key_vector(env->map, size, sort);
+
+       if (*vector == NULL)
+               return -EINVAL;
+
+       return 0;
+}
+
+int
+bootenv_dump(bootenv_t env)
+{
+       if (env == NULL)
+               return -EINVAL;
+
+       return hashmap_dump(env->map);
+}
+
+int
+bootenv_list_create(bootenv_list_t *list)
+{
+       bootenv_list_t res;
+       res = (bootenv_list_t) calloc(1, sizeof(struct bootenv_list));
+
+       if (res == NULL)
+               return -ENOMEM;
+
+       res->head = hashmap_new();
+
+       if (res->head == NULL) {
+               free(res);
+               return -ENOMEM;
+       }
+
+       *list = res;
+       return 0;
+}
+
+int
+bootenv_list_destroy(bootenv_list_t *list)
+{
+       int rc = 0;
+
+       if (list == NULL)
+               return -EINVAL;
+
+       bootenv_list_t tmp = *list;
+       if (tmp == 0)
+               return 0;
+
+       rc = hashmap_free(tmp->head);
+       if (rc != 0)
+               return rc;
+
+       free(tmp);
+       *list = NULL;
+       return 0;
+}
+
+int
+bootenv_list_import(bootenv_list_t list, const char *str)
+{
+       if (list == NULL)
+               return -EINVAL;
+
+       return build_list_definition(list->head, str);
+}
+
+int
+bootenv_list_export(bootenv_list_t list, char **string)
+{
+       size_t size, i, j, bufsize, tmp, rc = 0;
+       const char **items;
+
+       if (list == NULL)
+               return -EINVAL;
+
+       bufsize = BOOTENV_MAXLINE;
+       char *res = (char*) malloc(bufsize * sizeof(char));
+       if (res == NULL)
+               return -ENOMEM;
+
+       rc = bootenv_list_to_vector(list, &size, &items);
+       if (rc != 0) {
+               goto err;
+       }
+
+       j = 0;
+       for (i = 0; i < size; i++) {
+               tmp = strlen(items[i]);
+               if (j >= bufsize) {
+                       bufsize += BOOTENV_MAXLINE;
+                       res = (char*) realloc(res, bufsize * sizeof(char));
+                       if (res == NULL)  {
+                               rc = -ENOMEM;
+                               goto err;
+                       }
+               }
+               memcpy(res + j, items[i], tmp);
+               j += tmp;
+               if (i < (size - 1)) {
+                       res[j] = ',';
+                       j++;
+               }
+       }
+       j++;
+       res[j] = '\0';
+       free(items);
+       *string = res;
+       return 0;
+err:
+       free(items);
+       return rc;
+}
+
+int
+bootenv_list_add(bootenv_list_t list, const char *item)
+{
+       if ((list == NULL) || (item == NULL))
+               return -EINVAL;
+
+       return hashmap_add(list->head, item, "");
+}
+
+int
+bootenv_list_remove(bootenv_list_t list, const char *item)
+{
+       if ((list == NULL) || (item == NULL))
+               return -EINVAL;
+
+       return hashmap_remove(list->head, item);
+}
+
+int
+bootenv_list_is_in(bootenv_list_t list, const char *item)
+{
+       if ((list == NULL) || (item == NULL))
+               return -EINVAL;
+
+       return hashmap_lookup(list->head, item) != NULL ? 1 : 0;
+}
+
+int
+bootenv_list_to_vector(bootenv_list_t list, size_t *size, const char ***vector)
+{
+       if ((list == NULL) || (size == NULL))
+               return -EINVAL;
+
+       *vector = hashmap_get_key_vector(list->head, size, 1);
+       if (*vector == NULL)
+               return -ENOMEM;
+
+       return 0;
+}
+
+int
+bootenv_list_to_num_vector(bootenv_list_t list, size_t *size,
+               uint32_t **vector)
+{
+       int rc = 0;
+       size_t i;
+       uint32_t* res = NULL;
+       char *endptr = NULL;
+       const char **a = NULL;
+
+       rc = bootenv_list_to_vector(list, size, &a);
+       if (rc != 0)
+               goto err;
+
+       res = (uint32_t*) malloc (*size * sizeof(uint32_t));
+       if (!res)
+               goto err;
+
+       for (i = 0; i < *size; i++) {
+               res[i] = strtoul(a[i], &endptr, 0);
+               if (*endptr != '\0')
+                       goto err;
+       }
+
+       if (a)
+               free(a);
+       *vector = res;
+       return 0;
+
+err:
+       if (a)
+               free(a);
+       if (res)
+               free(res);
+       return rc;
+}
diff --git a/ubi-utils/sort-me-out/bootenv.h b/ubi-utils/sort-me-out/bootenv.h
new file mode 100644 (file)
index 0000000..8fecdbf
--- /dev/null
@@ -0,0 +1,434 @@
+#ifndef __BOOTENV_H__
+#define __BOOTENV_H__
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdio.h> /* FILE */
+#include <stdint.h>
+#include <pfiflash.h>
+
+/* DOXYGEN DOCUMENTATION */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @file bootenv.h
+ * @author oliloh@de.ibm.com
+ * @version 1.3
+ *
+ * 1.3 Some renaming
+ */
+
+/**
+ * @mainpage Usage
+ *
+ * @section intro Introduction
+ * This library provides all functionality to handle with the so-called
+ * platform description data (PDD) and the bootparameters defined in
+ * U-Boot. It is able to apply the defined PDD operations in PDD update
+ * scenarios. For more information about the PDD and bootparameter
+ * environment "bootenv" confer the PDD documentation.
+ *
+ * @section ret Return codes
+ * This library defines some return codes which will be delivered classified
+ * as warnings or errors. See the "Defines" section for details and numeric
+ * values.
+ *
+ * @section benv Bootenv format description
+ * There are two different input formats:
+ *     - text files
+ *     - binary files
+ *
+ * @subsection txt Text Files
+ * Text files have to be specified like:
+ * @verbatim key1=value1,value2,value7\n key2=value55,value1\n key4=value1\n@endverbatim
+ *
+ * @subsection bin Binary files
+ * Binary files have to be specified like:
+ * @verbatim<CRC32-bit>key1=value1,value2,value7\0key2=value55,value1\0... @endverbatim
+ * You can confer the U-Boot documentation for more details.
+ *
+ * @section benvlists Bootenv lists format description.
+ * Values referenced in the preceeding subsection can be
+ * defined like lists:
+ * @verbatim value1,value2,value3 @endverbatim
+ * There are some situation where a conversion of a comma
+ * seperated list can be useful, e.g. to get a list
+ * of defined PDD entries.
+ */
+
+#define BOOTENV_MAXSIZE (1024 * 100) /* max 100kiB space for bootenv */
+
+/**
+ * @def BOOTENV_ECRC
+ *     @brief Given binary file is to large.
+ * @def BOOTENV_EFMT
+ *     @brief Given bootenv section has an invalid format
+ * @def BOOTENV_EBADENTRY
+ *     @brief Bad entry in the bootenv section.
+ * @def BOOTENV_EINVAL
+ *     @brief Invalid bootenv defintion.
+ * @def BOOTENV_ENOPDD
+ *     @brief Given bootenv sectoin has no PDD defintion string (pdd=...).
+ * @def BOOTENV_EPDDINVAL
+ *     @brief Given bootenv section has an invalid PDD defintion.
+ * @def BOOTENV_ENOTIMPL
+ *     @brief Functionality not implemented.
+ * @def BOOTENV_ECOPY
+ *     @brief Bootenv memory copy error
+ * @def BOOTENV_ENOTFOUND
+ *     @brief Given key has has no value.
+ * @def BOOTENV_EMAX
+ *     @brief Highest error value.
+ */
+#define BOOTENV_ETOOBIG                1
+#define BOOTENV_EFMT           2
+#define BOOTENV_EBADENTRY      3
+#define BOOTENV_EINVAL         4
+#define BOOTENV_ENOPDD         5
+#define BOOTENV_EPDDINVAL      6
+#define BOOTENV_ENOTIMPL       7
+#define BOOTENV_ECOPY          8
+#define BOOTENV_ENOTFOUND      9
+#define BOOTENV_EMAX           10
+
+/**
+ * @def BOOTENV_W
+ *     @brief A warning which is handled internally as an error
+ *      but can be recovered by manual effort.
+ * @def BOOTENV_WPDD_STRING_DIFFERS
+ *     @brief The PDD strings of old and new PDD differ and
+ *     can cause update problems, because new PDD values
+ *     are removed from the bootenv section completely.
+ */
+#define BOOTENV_W                   20
+#define BOOTENV_WPDD_STRING_DIFFERS  21
+#define BOOTENV_WMAX 22 /* highest warning value */
+
+
+typedef struct bootenv *bootenv_t;
+       /**< A bootenv library handle. */
+
+typedef struct bootenv_list *bootenv_list_t;
+       /**< A handle for a value list. */
+
+typedef int(*pdd_func_t)(bootenv_t, bootenv_t, bootenv_t*,
+               int*, char*, size_t);
+
+
+/**
+ * @brief Get a new handle.
+ * @return 0
+ * @return or error
+ * */
+int bootenv_create(bootenv_t *env);
+
+/**
+ * @brief      Cleanup structure.
+ * @param env  Bootenv structure which shall be destroyed.
+ * @return 0
+ * @return or error
+ */
+int bootenv_destroy(bootenv_t *env);
+
+/**
+ * @brief Copy a bootenv handle.
+ * @param in   The input bootenv.
+ * @param out  The copied output bootenv. Discards old data.
+ * @return 0
+ * @return or error
+ */
+int bootenv_copy_bootenv(bootenv_t in, bootenv_t *out);
+
+/**
+ * @brief Looks for a value inside the bootenv data.
+ * @param env Handle to a bootenv structure.
+ * @param key The key.
+ * @return NULL         key not found
+ * @return !NULL ptr to value
+ */
+int bootenv_get(bootenv_t env, const char *key, const char **value);
+
+
+/**
+ * @brief Looks for a value inside the bootenv data and converts it to num.
+ * @param env Handle to a bootenv structure.
+ * @param key The key.
+ * @param value A pointer to the resulting numerical value
+ * @return NULL         key not found
+ * @return !NULL ptr to value
+ */
+int bootenv_get_num(bootenv_t env, const char *key, uint32_t *value);
+
+/**
+ * @brief Set a bootenv value by key.
+ * @param env   Handle to a bootenv structure.
+ * @param key  Key.
+ * @param value        Value to set.
+ * @return 0
+ * @return or error
+ */
+int bootenv_set(bootenv_t env, const char *key, const char *value);
+
+/**
+ * @brief Remove the given key (and its value) from a bootenv structure.
+ * @param env  Handle to a bootenv structure.
+ * @param key  Key.
+ * @return 0
+ * @return or error
+ */
+int bootenv_unset(bootenv_t env, const char *key);
+
+
+/**
+ * @brief Get a vector of all keys which are currently set
+ *        within a bootenv handle.
+ * @param env  Handle to a bootenv structure.
+ * @param size The size of the allocated array structure.
+ * @param sort Flag, if set the vector is sorted ascending.
+ * @return NULL on error.
+ * @return !NULL a pointer to the first element the allocated vector.
+ * @warning Free the allocate memory yourself!
+ */
+int bootenv_get_key_vector(bootenv_t env, size_t *size, int sort,
+                               const char ***vector);
+
+/**
+ * @brief Calculate the size in bytes which are necessary to write the
+ *        current bootenv section in a *binary file.
+ * @param env  bootenv handle.
+ * @param size  The size in bytes of the bootenv handle.
+ * @return 0
+ * @return or ERROR.
+ */
+int bootenv_size(bootenv_t env, size_t *size);
+
+/**
+ * @brief Read a binary bootenv file.
+ * @param fp   File pointer to input stream.
+ * @param env  bootenv handle.
+ * @param size  maximum data size.
+ * @return 0
+ * @return or ERROR.
+ */
+int bootenv_read(FILE* fp, bootenv_t env, size_t size);
+
+/**
+ * @param ret_crc  return value of crc of read data
+ */
+int bootenv_read_crc(FILE* fp, bootenv_t env, size_t size, uint32_t *ret_crc);
+
+/**
+ * @brief Read bootenv data from an text/ascii file.
+ * @param fp   File pointer to ascii PDD file.
+ * @param env  bootenv handle
+ * @return 0
+ * @return or ERROR.
+ */
+int bootenv_read_txt(FILE* fp, bootenv_t env);
+
+/**
+ * @brief Write a bootenv structure to the given location (binary).
+ * @param fp   Filepointer to binary file.
+ * @param env  Bootenv structure which shall be written.
+ * @return 0
+ * @return or error
+ */
+int bootenv_write(FILE* fp, bootenv_t env);
+
+/**
+ * @param ret_crc  return value of crc of read data
+ */
+int bootenv_write_crc(FILE* fp, bootenv_t env, uint32_t* ret_crc);
+
+/**
+ * @brief Write a bootenv structure to the given location (text).
+ * @param fp   Filepointer to text file.
+ * @param env  Bootenv structure which shall be written.
+ * @return 0
+ * @return or error
+ */
+int bootenv_write_txt(FILE* fp, bootenv_t env);
+
+/**
+ * @brief Compare bootenvs using memcmp().
+ * @param first        First bootenv.
+ * @param second       Second bootenv.
+ * @return 0 if bootenvs are equal
+ * @return < 0 if error
+ * @return > 0 if unequal
+ */
+int bootenv_compare(bootenv_t first, bootenv_t second);
+
+/**
+ * @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/sort-me-out/config.h b/ubi-utils/sort-me-out/config.h
new file mode 100644 (file)
index 0000000..55e60f3
--- /dev/null
@@ -0,0 +1,28 @@
+#ifndef __CONFIG_H__
+#define __CONFIG_H__
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Frank Haverkamp
+ */
+
+#define PACKAGE_BUGREPORT                                              \
+       "haver@vnet.ibm.com, dedekind@linutronix.de, or tglx@linutronix.de"
+
+#define ubi_unused __attribute__((unused))
+
+#endif
diff --git a/ubi-utils/sort-me-out/crc32.c b/ubi-utils/sort-me-out/crc32.c
new file mode 100644 (file)
index 0000000..666e217
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Thomas Gleixner
+ */
+
+/*
+ * CRC32 functions
+ *
+ * Can be compiled as seperate object, but is included into the ipl source
+ * so gcc can inline the functions. We optimize for size so the omission of
+ * the function frame is helpful.
+ *
+ */
+
+#include <stdint.h>
+#include <crc32.h>
+
+/* CRC polynomial */
+#define CRC_POLY       0xEDB88320
+
+/**
+ * init_crc32_table - Initialize crc table
+ *
+ * @table:     pointer to the CRC table which must be initialized
+ *
+ * Create CRC32 table for given polynomial. The table is created with
+ * the lowest order term in the highest order bit. So the x^32 term
+ * has to implied in the crc calculation function.
+ */
+void init_crc32_table(uint32_t *table)
+{
+       uint32_t crc;
+       int i, j;
+
+       for (i = 0; i < 256; i++) {
+               crc = i;
+               for (j = 8; j > 0; j--) {
+                       if (crc & 1)
+                               crc = (crc >> 1) ^ CRC_POLY;
+                       else
+                               crc >>= 1;
+               }
+               table[i] = crc;
+       }
+}
+
+/**
+ * clc_crc32 - Calculate CRC32 over a buffer
+ *
+ * @table:     pointer to the CRC table
+ * @crc:       initial crc value
+ * @buf:       pointer to the buffer
+ * @len:       number of bytes to calc
+ *
+ * Returns the updated crc value.
+ *
+ * The algorithm resembles a hardware shift register, but calculates 8
+ * bit at once.
+ */
+uint32_t clc_crc32(uint32_t *table, uint32_t crc, void *buf,
+                  int len)
+{
+       const unsigned char *p = buf;
+
+       while(--len >= 0)
+               crc = table[(crc ^ *p++) & 0xff] ^ (crc >> 8);
+       return crc;
+}
diff --git a/ubi-utils/sort-me-out/crc32.h b/ubi-utils/sort-me-out/crc32.h
new file mode 100644 (file)
index 0000000..31362b0
--- /dev/null
@@ -0,0 +1,36 @@
+#ifndef __CRC32_H__
+#define __CRC32_H__
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * Author: Thomas Gleixner
+ *
+ * CRC32 functions
+ *
+ * Can be compiled as seperate object, but is included into the ipl source
+ * so gcc can inline the functions. We optimize for size so the omission of
+ * the function frame is helpful.
+ *
+ */
+#include <stdint.h>
+
+void init_crc32_table(uint32_t *table);
+uint32_t clc_crc32(uint32_t *table, uint32_t crc, void *buf, int len);
+
+#endif /* __CRC32_H__ */
diff --git a/ubi-utils/sort-me-out/eb_chain.c b/ubi-utils/sort-me-out/eb_chain.c
new file mode 100644 (file)
index 0000000..a018ae6
--- /dev/null
@@ -0,0 +1,280 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006, 2007
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * Author:  Drake Dowsett, dowsett@de.ibm.com
+ * Contact: Andreas Arnez, arnez@de.ibm.com
+ */
+
+/* see eb_chain.h */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include "unubi_analyze.h"
+#include "crc32.h"
+
+#define COPY(dst, src)                                                 \
+       do {                                                            \
+               dst = malloc(sizeof(*dst));                             \
+               if (dst == NULL)                                        \
+                       return -ENOMEM;                                 \
+               memcpy(dst, src, sizeof(*dst));                         \
+       } while (0)
+
+
+/**
+ * inserts an eb_info into the chain starting at head, then searching
+ * linearly for the correct position;
+ * new should contain valid vid and ec headers and the data_crc should
+ * already have been checked before insertion, otherwise the chain
+ * could be have un an undesired manner;
+ * returns -ENOMEM if alloc fails, otherwise SHOULD always return 0,
+ * if not, the code reached the last line and returned -EAGAIN,
+ * meaning there is a bug or a case not being handled here;
+ **/
+int
+eb_chain_insert(struct eb_info **head, struct eb_info *new)
+{
+       uint32_t vol, num, ver;
+       uint32_t new_vol, new_num, new_ver;
+       struct eb_info *prev, *cur, *hist, *ins;
+       struct eb_info **prev_ptr;
+
+       if ((head == NULL) || (new == NULL))
+               return 0;
+
+       if (*head == NULL) {
+               COPY(*head, new);
+               (*head)->next = NULL;
+               return 0;
+       }
+
+       new_vol = __be32_to_cpu(new->vid.vol_id);
+       new_num = __be32_to_cpu(new->vid.lnum);
+       new_ver = __be32_to_cpu(new->vid.leb_ver);
+
+       /** TRAVERSE HORIZONTALY **/
+
+       cur = *head;
+       prev = NULL;
+
+       /* traverse until vol_id/lnum align */
+       vol = __be32_to_cpu(cur->vid.vol_id);
+       num = __be32_to_cpu(cur->vid.lnum);
+       while ((new_vol > vol) || ((new_vol == vol) && (new_num > num))) {
+               /* insert new at end of chain */
+               if (cur->next == NULL) {
+                       COPY(ins, new);
+                       ins->next = NULL;
+                       cur->next = ins;
+                       return 0;
+               }
+
+               prev = cur;
+               cur = cur->next;
+               vol = __be32_to_cpu(cur->vid.vol_id);
+               num = __be32_to_cpu(cur->vid.lnum);
+       }
+
+       if (prev == NULL)
+               prev_ptr = head;
+       else
+               prev_ptr = &(prev->next);
+
+       /* insert new into the middle of chain */
+       if ((new_vol != vol) || (new_num != num)) {
+               COPY(ins, new);
+               ins->next = cur;
+               *prev_ptr = ins;
+               return 0;
+       }
+
+       /** TRAVERSE VERTICALY **/
+
+       hist = cur;
+       prev = NULL;
+
+       /* traverse until versions align */
+       ver = __be32_to_cpu(cur->vid.leb_ver);
+       while (new_ver < ver) {
+               /* insert new at bottom of history */
+               if (hist->older == NULL) {
+                       COPY(ins, new);
+                       ins->next = NULL;
+                       ins->older = NULL;
+                       hist->older = ins;
+                       return 0;
+               }
+
+               prev = hist;
+               hist = hist->older;
+               ver = __be32_to_cpu(hist->vid.leb_ver);
+       }
+
+       if (prev == NULL) {
+               /* replace active version */
+               COPY(ins, new);
+               ins->next = hist->next;
+               *prev_ptr = ins;
+
+               /* place cur in vertical histroy */
+               ins->older = hist;
+               hist->next = NULL;
+               return 0;
+       }
+
+       /* insert between versions, beneath active version */
+       COPY(ins, new);
+       ins->next = NULL;
+       ins->older = prev->older;
+       prev->older = ins;
+       return 0;
+}
+
+
+/**
+ * sets the pointer at pos to the position of the first entry in the chain
+ * with of vol_id and, if given, with the same lnum as *lnum;
+ * if there is no entry in the chain, then *pos is NULL on return;
+ * always returns 0;
+ **/
+int
+eb_chain_position(struct eb_info **head, uint32_t vol_id, uint32_t *lnum,
+                 struct eb_info **pos)
+{
+       uint32_t vol, num;
+       struct eb_info *cur;
+
+       if ((head == NULL) || (*head == NULL) || (pos == NULL))
+               return 0;
+
+       *pos = NULL;
+
+       cur = *head;
+       while (cur != NULL) {
+               vol = __be32_to_cpu(cur->vid.vol_id);
+               num = __be32_to_cpu(cur->vid.lnum);
+
+               if ((vol_id == vol) && ((lnum == NULL) || (*lnum == num))) {
+                       *pos = cur;
+                       return 0;
+               }
+
+               cur = cur->next;
+       }
+
+       return 0;
+}
+
+
+/**
+ * prints to stream, the vol_id, lnum and leb_ver for each entry in the
+ * chain, starting at head;
+ * this is intended for debuging purposes;
+ * always returns 0;
+ *
+ * FIXME I do not like the double list traversion ...
+ **/
+int
+eb_chain_print(FILE* stream, struct eb_info *head)
+{
+       struct eb_info *cur;
+
+       if (stream == NULL)
+               stream = stdout;
+
+       if (head == NULL) {
+               fprintf(stream, "EMPTY\n");
+               return 0;
+       }
+       /*               012345678012345678012345678012301230123 0123 01234567 0123457 01234567*/
+       fprintf(stream, "VOL_ID   LNUM     LEB_VER  EC  VID DAT  PBLK PADDR    DSIZE   EC\n");
+       cur = head;
+       while (cur != NULL) {
+               struct eb_info *hist;
+
+               fprintf(stream, "%08x %-8u %08x %-4s%-4s",
+                       __be32_to_cpu(cur->vid.vol_id),
+                       __be32_to_cpu(cur->vid.lnum),
+                       __be32_to_cpu(cur->vid.leb_ver),
+                       cur->ec_crc_ok   ? "ok":"bad",
+                       cur->vid_crc_ok  ? "ok":"bad");
+               if (cur->vid.vol_type == UBI_VID_STATIC)
+                       fprintf(stream, "%-4s", cur->data_crc_ok ? "ok":"bad");
+               else    fprintf(stream, "%-4s", cur->data_crc_ok ? "ok":"ign");
+               fprintf(stream, " %-4d %08x %-8u %-8llu\n", cur->phys_block,
+                       cur->phys_addr, __be32_to_cpu(cur->vid.data_size),
+                       __be64_to_cpu(cur->ec.ec));
+
+               hist = cur->older;
+               while (hist != NULL) {
+                       fprintf(stream, "%08x %-8u %08x %-4s%-4s",
+                               __be32_to_cpu(hist->vid.vol_id),
+                               __be32_to_cpu(hist->vid.lnum),
+                               __be32_to_cpu(hist->vid.leb_ver),
+                               hist->ec_crc_ok   ? "ok":"bad",
+                               hist->vid_crc_ok  ? "ok":"bad");
+                       if (hist->vid.vol_type == UBI_VID_STATIC)
+                               fprintf(stream, "%-4s", hist->data_crc_ok ? "ok":"bad");
+                       else    fprintf(stream, "%-4s", hist->data_crc_ok ? "ok":"ign");
+                       fprintf(stream, " %-4d %08x %-8u %-8llu (*)\n",
+                               hist->phys_block, hist->phys_addr,
+                               __be32_to_cpu(hist->vid.data_size),
+                               __be64_to_cpu(hist->ec.ec));
+
+                       hist = hist->older;
+               }
+               cur = cur->next;
+       }
+
+       return 0;
+}
+
+
+/**
+ * frees the memory of the entire chain, starting at head;
+ * head will be NULL on return;
+ * always returns 0;
+ **/
+int
+eb_chain_destroy(struct eb_info **head)
+{
+       if (head == NULL)
+               return 0;
+
+       while (*head != NULL) {
+               struct eb_info *cur;
+               struct eb_info *hist;
+
+               cur = *head;
+               *head = (*head)->next;
+
+               hist = cur->older;
+               while (hist != NULL) {
+                       struct eb_info *temp;
+
+                       temp = hist;
+                       hist = hist->older;
+                       free(temp);
+               }
+               free(cur);
+       }
+       return 0;
+}
+
diff --git a/ubi-utils/sort-me-out/example_ubi.h b/ubi-utils/sort-me-out/example_ubi.h
new file mode 100644 (file)
index 0000000..23c7b54
--- /dev/null
@@ -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/sort-me-out/hashmap.c b/ubi-utils/sort-me-out/hashmap.c
new file mode 100644 (file)
index 0000000..3511d56
--- /dev/null
@@ -0,0 +1,412 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Oliver Lohmann
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "error.h"
+#include "hashmap.h"
+#define DEFAULT_BUCKETS 4096
+
+#if 0
+#define INFO_MSG(fmt...) do {  \
+       info_msg(fmt);          \
+} while (0)
+#else
+#define INFO_MSG(fmt...)
+#endif
+
+struct hashentry {
+       char* key;      /* key '0' term. str */
+       char* value;    /* payload '0' term. str */
+
+       hashentry_t next;
+};
+
+struct hashmap {
+       size_t entries;     /* current #entries */
+       size_t maxsize;     /* no. of hash buckets */
+       hashentry_t* data;  /* array of buckets */
+};
+
+static int
+is_empty(hashentry_t l)
+{
+       return l == NULL ? 1 : 0;
+}
+
+hashmap_t
+hashmap_new(void)
+{
+       hashmap_t res;
+       res = (hashmap_t) calloc(1, sizeof(struct hashmap));
+
+       if (res == NULL)
+               return NULL;
+
+       res->maxsize = DEFAULT_BUCKETS;
+       res->entries = 0;
+
+       res->data   = (hashentry_t*)
+               calloc(1, res->maxsize * sizeof(struct hashentry));
+
+       if (res->data == NULL)
+               return NULL;
+
+       return res;
+}
+
+static hashentry_t
+new_entry(const char* key, const char* value)
+{
+       hashentry_t res;
+
+       res = (hashentry_t) calloc(1, sizeof(struct hashentry));
+
+       if (res == NULL)
+               return NULL;
+
+       /* allocate key and value and copy them */
+       res->key = strdup(key);
+       if (res->key == NULL) {
+               free(res);
+               return NULL;
+       }
+
+       res->value = strdup(value);
+       if (res->value == NULL) {
+               free(res->key);
+               free(res);
+               return NULL;
+       }
+
+       res->next = NULL;
+
+       return res;
+}
+
+static hashentry_t
+free_entry(hashentry_t e)
+{
+       if (!is_empty(e)) {
+               if(e->key != NULL) {
+                       free(e->key);
+               }
+               if(e->value != NULL)
+                       free(e->value);
+               free(e);
+       }
+
+       return NULL;
+}
+
+static hashentry_t
+remove_entry(hashentry_t l, const char* key, size_t* entries)
+{
+       hashentry_t lnext;
+       if (is_empty(l))
+               return NULL;
+
+       if(strcmp(l->key,key) == 0) {
+               lnext = l->next;
+               l = free_entry(l);
+               (*entries)--;
+               return lnext;
+       }
+
+       l->next = remove_entry(l->next, key, entries);
+
+       return l;
+}
+
+static hashentry_t
+insert_entry(hashentry_t l, hashentry_t e, size_t* entries)
+{
+       if (is_empty(l)) {
+               (*entries)++;
+               return e;
+       }
+
+       /* check for update */
+       if (strcmp(l->key, e->key) == 0) {
+               e->next = l->next;
+               l = free_entry(l);
+               return e;
+       }
+
+       l->next = insert_entry(l->next, e, entries);
+       return l;
+}
+
+static hashentry_t
+remove_all(hashentry_t l, size_t* entries)
+{
+       hashentry_t lnext;
+       if (is_empty(l))
+               return NULL;
+
+       lnext = l->next;
+       free_entry(l);
+       (*entries)--;
+
+       return remove_all(lnext, entries);
+}
+
+static const char*
+value_lookup(hashentry_t l, const char* key)
+{
+       if (is_empty(l))
+               return NULL;
+
+       if (strcmp(l->key, key) == 0)
+               return l->value;
+
+       return value_lookup(l->next, key);
+}
+
+static void
+print_all(hashentry_t l)
+{
+       if (is_empty(l)) {
+               printf("\n");
+               return;
+       }
+
+       printf("%s=%s", l->key, l->value);
+       if (!is_empty(l->next)) {
+               printf(",");
+       }
+
+       print_all(l->next);
+}
+
+static void
+keys_to_array(hashentry_t l, const char** a, size_t* i)
+{
+       if (is_empty(l))
+               return;
+
+       a[*i] = l->key;
+       (*i)++;
+
+       keys_to_array(l->next, a, i);
+}
+
+uint32_t
+hash_str(const char* str, uint32_t mapsize)
+{
+       uint32_t hash = 0;
+       uint32_t x    = 0;
+       uint32_t i    = 0;
+       size_t   len  = strlen(str);
+
+       for(i = 0; i < len; str++, i++) {
+               hash = (hash << 4) + (*str);
+               if((x = hash & 0xF0000000L) != 0) {
+                       hash ^= (x >> 24);
+                       hash &= ~x;
+               }
+       }
+
+       return (hash & 0x7FFFFFFF) % mapsize;
+}
+
+
+int
+hashmap_is_empty(hashmap_t map)
+{
+       if (map == NULL)
+               return -EINVAL;
+
+       return map->entries > 0 ? 1 : 0;
+}
+
+const char*
+hashmap_lookup(hashmap_t map, const char* key)
+{
+       uint32_t i;
+
+       if ((map == NULL) || (key == NULL))
+               return NULL;
+
+       i = hash_str(key, map->maxsize);
+
+       return value_lookup(map->data[i], key);
+}
+
+int
+hashmap_add(hashmap_t map, const char* key, const char* value)
+{
+       uint32_t i;
+       hashentry_t entry;
+
+       if ((map == NULL) || (key == NULL) || (value == NULL))
+               return -EINVAL;
+
+       i = hash_str(key, map->maxsize);
+       entry = new_entry(key, value);
+       if (entry == NULL)
+               return -ENOMEM;
+
+       map->data[i] = insert_entry(map->data[i],
+                       entry, &map->entries);
+
+       INFO_MSG("HASH_ADD: chain[%d] key:%s val:%s",i,  key, value);
+       return 0;
+}
+
+int
+hashmap_remove(hashmap_t map, const char* key)
+{
+       uint32_t i;
+
+       if ((map == NULL) || (key == NULL))
+               return -EINVAL;
+
+       i = hash_str(key, map->maxsize);
+       map->data[i] = remove_entry(map->data[i], key, &map->entries);
+
+       return 0;
+}
+
+size_t
+hashmap_size(hashmap_t map)
+{
+       if (map != NULL)
+               return map->entries;
+       else
+               return 0;
+}
+
+int
+hashmap_free(hashmap_t map)
+{
+       size_t i;
+
+       if (map == NULL)
+               return -EINVAL;
+
+       /* "children" first */
+       for(i = 0; i < map->maxsize; i++) {
+               map->data[i] = remove_all(map->data[i], &map->entries);
+       }
+       free(map->data);
+       free(map);
+
+       return 0;
+}
+
+int
+hashmap_dump(hashmap_t map)
+{
+       size_t i;
+       if (map == NULL)
+               return -EINVAL;
+
+       for(i = 0; i < map->maxsize; i++) {
+               if (map->data[i] != NULL) {
+                       printf("[%zd]: ", i);
+                       print_all(map->data[i]);
+               }
+       }
+
+       return 0;
+}
+
+static const char**
+sort_key_vector(const char** a, size_t size)
+{
+       /* uses bubblesort */
+       size_t i, j;
+       const char* tmp;
+
+       if (size <= 0)
+               return a;
+
+       for (i = size - 1; i > 0; i--) {
+               for (j = 0; j < i; j++) {
+                       if (strcmp(a[j], a[j+1]) > 0) {
+                               tmp  = a[j];
+                               a[j] = a[j+1];
+                               a[j+1] = tmp;
+                       }
+               }
+       }
+       return a;
+}
+
+const char**
+hashmap_get_key_vector(hashmap_t map, size_t* size, int sort)
+{
+       const char** res;
+       size_t i, j;
+       *size = map->entries;
+
+       res = (const char**) malloc(*size * sizeof(char*));
+       if (res == NULL)
+               return NULL;
+
+       j = 0;
+       for(i=0; i < map->maxsize; i++) {
+               keys_to_array(map->data[i], res, &j);
+       }
+
+       if (sort)
+               res = sort_key_vector(res, *size);
+
+       return res;
+}
+
+int
+hashmap_key_is_in_vector(const char** vec, size_t size, const char* key)
+{
+       size_t i;
+       for (i = 0; i < size; i++) {
+               if (strcmp(vec[i], key) == 0) /* found */
+                       return 1;
+       }
+
+       return 0;
+}
+
+const char**
+hashmap_get_update_key_vector(const char** vec1, size_t vec1_size,
+               const char** vec2, size_t vec2_size, size_t* res_size)
+{
+       const char** res;
+       size_t i, j;
+
+       *res_size = vec2_size;
+
+       res = (const char**) malloc(*res_size * sizeof(char*));
+       if (res == NULL)
+               return NULL;
+
+       /* get all keys from vec2 which are not set in vec1 */
+       j = 0;
+       for (i = 0; i < vec2_size; i++) {
+               if (!hashmap_key_is_in_vector(vec1, vec1_size, vec2[i]))
+                       res[j++] = vec2[i];
+       }
+
+       *res_size = j;
+       return res;
+}
diff --git a/ubi-utils/sort-me-out/hashmap.h b/ubi-utils/sort-me-out/hashmap.h
new file mode 100644 (file)
index 0000000..1b13e95
--- /dev/null
@@ -0,0 +1,49 @@
+#ifndef __HASHMAP_H__
+#define __HASHMAP_H__
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Oliver Lohmann
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+
+typedef struct hashentry *hashentry_t;
+typedef struct hashmap *hashmap_t;
+
+hashmap_t hashmap_new(void);
+int hashmap_free(hashmap_t map);
+
+int hashmap_add(hashmap_t map, const char* key, const char* value);
+int hashmap_update(hashmap_t map, const char* key, const char* value);
+int hashmap_remove(hashmap_t map, const char* key);
+const char* hashmap_lookup(hashmap_t map, const char* key);
+
+const char** hashmap_get_key_vector(hashmap_t map, size_t* size, int sort);
+int hashmap_key_is_in_vector(const char** vec, size_t size, const char* key);
+const char** hashmap_get_update_key_vector(const char** vec1, size_t vec1_size,
+               const char** vec2, size_t vec2_size, size_t* res_size);
+
+int hashmap_dump(hashmap_t map);
+
+int hashmap_is_empty(hashmap_t map);
+size_t hashmap_size(hashmap_t map);
+
+uint32_t hash_str(const char* str, uint32_t mapsize);
+
+#endif /* __HASHMAP_H__ */
diff --git a/ubi-utils/sort-me-out/pfi2bin.c b/ubi-utils/sort-me-out/pfi2bin.c
new file mode 100644 (file)
index 0000000..7f31938
--- /dev/null
@@ -0,0 +1,681 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Oliver Lohmann
+ *
+ * Convert a PFI file (partial flash image) into a plain binary file.
+ * This tool can be used to prepare the data to be burned into flash
+ * chips in a manufacturing step where the flashes are written before
+ * being soldered onto the hardware. For NAND images another step is
+ * required to add the right OOB data to the binary image.
+ *
+ * 1.3 Removed argp because we want to use uClibc.
+ * 1.4 Minor cleanups
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+
+#include <ubigen.h>
+#include <mtd/ubi-header.h>
+
+#include "config.h"
+#include "list.h"
+#include "error.h"
+#include "reader.h"
+#include "peb.h"
+#include "crc32.h"
+
+#define PROGRAM_VERSION "1.4"
+
+#define MAX_FNAME 255
+#define DEFAULT_ERASE_COUNT  0 /* Hmmm.... Perhaps */
+#define ERR_BUF_SIZE 1024
+
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+
+static uint32_t crc32_table[256];
+static char err_buf[ERR_BUF_SIZE];
+
+/*
+ * Data used to buffer raw blocks which have to be
+ * located at a specific point inside the generated RAW file
+ */
+
+typedef enum action_t {
+       ACT_NOTHING   = 0x00000000,
+       ACT_RAW    = 0x00000001,
+} action_t;
+
+static const char copyright [] __attribute__((unused)) =
+       "(c) Copyright IBM Corp 2006\n";
+
+static char doc[] = "\nVersion: " PROGRAM_VERSION "\n"
+       "pfi2bin - a tool to convert PFI files into binary images.\n";
+
+static const char *optionsstr =
+" Common settings:\n"
+"  -c, --copyright\n"
+"  -v, --verbose              Print more information.\n"
+"\n"
+" Input:\n"
+"  -j, --platform=pdd-file    PDD information which contains the card settings.\n"
+"\n"
+" Output:\n"
+"  -o, --output=filename      Outputfile, default: stdout.\n"
+"\n"
+"  -?, --help                 Give this help list\n"
+"      --usage                Give a short usage message\n"
+"  -V, --version              Print program version\n";
+
+static const char *usage =
+"Usage: pfi2bin [-cv?V] [-j pdd-file] [-o filename] [--copyright]\n"
+"            [--verbose] [--platform=pdd-file] [--output=filename] [--help]\n"
+"            [--usage] [--version] pfifile\n";
+
+struct option long_options[] = {
+       { .name = "copyright", .has_arg = 0, .flag = NULL, .val = 'c' },
+       { .name = "verbose", .has_arg = 0, .flag = NULL, .val = 'v' },
+       { .name = "platform", .has_arg = 1, .flag = NULL, .val = 'j' },
+       { .name = "output", .has_arg = 1, .flag = NULL, .val = 'o' },
+       { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' },
+       { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 },
+       { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' },
+       { NULL, 0, NULL, 0}
+};
+
+typedef struct io {
+       FILE* fp_pdd;   /* a FilePointer to the PDD data */
+       FILE* fp_pfi;   /* a FilePointer to the PFI input stream */
+       FILE* fp_out;   /* a FilePointer to the output stream */
+} *io_t;
+
+typedef struct myargs {
+       /* common settings */
+       action_t action;
+       int verbose;
+       const char *f_in_pfi;
+       const char *f_in_pdd;
+       const char *f_out;
+
+       /* special stuff needed to get additional arguments */
+       char *arg1;
+       char **options;                 /* [STRING...] */
+} myargs;
+
+static int
+parse_opt(int argc, char **argv, myargs *args)
+{
+       while (1) {
+               int key;
+
+               key = getopt_long(argc, argv, "cvj:o:?V", long_options, NULL);
+               if (key == -1)
+                       break;
+
+               switch (key) {
+                       /* common settings */
+                       case 'v': /* --verbose=<level> */
+                               args->verbose = 1;
+                               break;
+
+                       case 'c': /* --copyright */
+                               fprintf(stderr, "%s\n", copyright);
+                               exit(0);
+                               break;
+
+                       case 'j': /* --platform */
+                               args->f_in_pdd = optarg;
+                               break;
+
+                       case 'o': /* --output */
+                               args->f_out = optarg;
+                               break;
+
+                       case '?': /* help */
+                               printf("pfi2bin [OPTION...] pfifile\n");
+                               printf("%s", doc);
+                               printf("%s", optionsstr);
+                               printf("\nReport bugs to %s\n",
+                                      PACKAGE_BUGREPORT);
+                               exit(0);
+                               break;
+
+                       case 'V':
+                               printf("%s\n", PROGRAM_VERSION);
+                               exit(0);
+                               break;
+
+                       default:
+                               printf("%s", usage);
+                               exit(-1);
+               }
+       }
+
+       if (optind < argc)
+               args->f_in_pfi = argv[optind++];
+
+       return 0;
+}
+
+
+static size_t
+byte_to_blk(size_t byte, size_t blk_size)
+{
+       return  (byte % blk_size) == 0
+               ? byte / blk_size
+               : byte / blk_size + 1;
+}
+
+
+
+
+/**
+ * @precondition  IO: File stream points to first byte of RAW data.
+ * @postcondition IO: File stream points to first byte of next
+ *                   or EOF.
+ */
+static int
+memorize_raw_eb(pfi_raw_t pfi_raw, pdd_data_t pdd, list_t *raw_pebs,
+               io_t io)
+{
+       int rc = 0;
+       uint32_t i;
+
+       size_t read, to_read, eb_num;
+       size_t bytes_left;
+       list_t pebs = *raw_pebs;
+       peb_t   peb  = NULL;
+
+       long old_file_pos = ftell(io->fp_pfi);
+       for (i = 0; i < pfi_raw->starts_size; i++) {
+               bytes_left = pfi_raw->data_size;
+               rc = fseek(io->fp_pfi, old_file_pos, SEEK_SET);
+               if (rc != 0)
+                       goto err;
+
+               eb_num = byte_to_blk(pfi_raw->starts[i], pdd->eb_size);
+               while (bytes_left) {
+                       to_read = MIN(bytes_left, pdd->eb_size);
+                       rc = peb_new(eb_num++, pdd->eb_size, &peb);
+                       if (rc != 0)
+                               goto err;
+                       read = fread(peb->data, 1, to_read, io->fp_pfi);
+                       if (read != to_read) {
+                               rc = -EIO;
+                               goto err;
+                       }
+                       pebs = append_elem(peb, pebs);
+                       bytes_left -= read;
+               }
+
+       }
+       *raw_pebs = pebs;
+       return 0;
+err:
+       pebs = remove_all((free_func_t)&peb_free, pebs);
+       return rc;
+}
+
+static int
+convert_ubi_volume(pfi_ubi_t ubi, pdd_data_t pdd, list_t raw_pebs,
+               struct ubi_vtbl_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;
+    size_t ret;
+
+    assert(strcmp(opentype, "r") == 0);
+
+    f = tmpfile();
+    ret = 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_vtbl_record *vol_tab, size_t vol_tab_size,
+               size_t *ebs_written, io_t io)
+{
+       int rc = 0;
+       ubi_info_t u;
+       peb_t raw_peb;
+       peb_t cmp_peb;
+       size_t leb_size, leb_total, j = 0;
+       uint8_t *ptr = NULL;
+       FILE* fp_leb = NULL;
+       int vt_slots;
+       size_t vol_tab_size_limit;
+
+       rc = peb_new(0, 0, &cmp_peb);
+       if (rc != 0)
+               goto err;
+
+       /* @FIXME: Artem creates one volume with 2 LEBs.
+        * IMO 2 volumes would be more convenient. In order
+        * to get 2 reserved LEBs from ubigen, I have to
+        * introduce this stupid mechanism. Until no final
+        * decision of the VTAB structure is made... Good enough.
+        */
+       rc = ubigen_create(&u, UBI_LAYOUT_VOL_ID, UBI_VID_DYNAMIC,
+                          pdd->eb_size, DEFAULT_ERASE_COUNT,
+                          1, UBI_VERSION,
+                          pdd->vid_hdr_offset, UBI_COMPAT_REJECT,
+                          vol_tab_size, stdin, io->fp_out);
+                          /* @FIXME stdin for fp_in is a hack */
+       if (rc != 0)
+               goto err;
+       rc = ubigen_get_leb_size(u, &leb_size);
+       if (rc != 0)
+               goto err;
+       ubigen_destroy(&u);
+
+       /*
+        * The number of supported volumes is restricted by the eraseblock size
+        * and by the UBI_MAX_VOLUMES constant.
+        */
+       vt_slots = leb_size / UBI_VTBL_RECORD_SIZE;
+       if (vt_slots > UBI_MAX_VOLUMES)
+               vt_slots = UBI_MAX_VOLUMES;
+       vol_tab_size_limit = vt_slots * UBI_VTBL_RECORD_SIZE;
+
+       ptr = (uint8_t*) malloc(leb_size * sizeof(uint8_t));
+       if (ptr == NULL)
+               goto err;
+
+       memset(ptr, 0xff, leb_size);
+       memcpy(ptr, vol_tab, vol_tab_size_limit);
+       fp_leb = my_fmemopen(ptr, leb_size, "r");
+
+       rc = ubigen_create(&u, UBI_LAYOUT_VOL_ID, UBI_VID_DYNAMIC,
+                          pdd->eb_size, DEFAULT_ERASE_COUNT,
+                          1, UBI_VERSION, pdd->vid_hdr_offset,
+                          UBI_COMPAT_REJECT, leb_size * UBI_LAYOUT_VOLUME_EBS,
+                          fp_leb, io->fp_out);
+       if (rc != 0)
+               goto err;
+       rc = ubigen_get_leb_total(u, &leb_total);
+       if (rc != 0)
+               goto err;
+
+       long old_file_pos = ftell(fp_leb);
+       while(j < leb_total) {
+               rc = fseek(fp_leb, old_file_pos, SEEK_SET);
+               if (rc != 0)
+                       goto err;
+
+               cmp_peb->num = *ebs_written;
+               raw_peb = is_in((cmp_func_t)peb_cmp, cmp_peb,
+                               raw_pebs);
+               if (raw_peb) {
+                       rc = peb_write(io->fp_out, raw_peb);
+               }
+               else {
+                       rc = ubigen_write_leb(u, NO_ERROR);
+                       j++;
+               }
+
+               if (rc != 0)
+                       goto err;
+               (*ebs_written)++;
+       }
+
+err:
+       free(ptr);
+       peb_free(&cmp_peb);
+       ubigen_destroy(&u);
+       fclose(fp_leb);
+       return rc;
+}
+
+static int
+write_remaining_raw_ebs(pdd_data_t pdd, list_t raw_blocks, size_t *ebs_written,
+                       FILE* fp_out)
+{
+       int rc = 0;
+       uint32_t j, delta;
+       list_t ptr;
+       peb_t empty_eb, peb;
+
+       /* create an empty 0xff EB (for padding) */
+       rc = peb_new(0, pdd->eb_size, &empty_eb);
+
+       foreach(peb, ptr, raw_blocks) {
+               if (peb->num < *ebs_written) {
+                       continue; /* omit blocks which
+                                    are already passed */
+               }
+
+               if (peb->num < *ebs_written) {
+                       err_msg("eb_num: %d\n", peb->num);
+                       err_msg("Bug: This should never happen. %d %s",
+                               __LINE__, __FILE__);
+                       goto err;
+               }
+
+               delta = peb->num - *ebs_written;
+               if (((delta + *ebs_written) * pdd->eb_size) > pdd->flash_size) {
+                       err_msg("RAW block outside of flash_size.");
+                       goto err;
+               }
+               for (j = 0; j < delta; j++) {
+                       rc = peb_write(fp_out, empty_eb);
+                       if (rc != 0)
+                               goto err;
+                       (*ebs_written)++;
+               }
+               rc = peb_write(fp_out, peb);
+               if (rc != 0)
+                       goto err;
+               (*ebs_written)++;
+       }
+
+err:
+       peb_free(&empty_eb);
+       return rc;
+}
+
+static int
+init_vol_tab(struct ubi_vtbl_record **vol_tab, size_t *vol_tab_size)
+{
+       uint32_t crc;
+       size_t i;
+       struct ubi_vtbl_record* res = NULL;
+
+       *vol_tab_size = UBI_MAX_VOLUMES * UBI_VTBL_RECORD_SIZE;
+
+       res = (struct ubi_vtbl_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_be32(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_vtbl_record *vol_tab = NULL;
+       pdd_data_t pdd = NULL;
+
+       rc = init_vol_tab (&vol_tab, &vol_tab_size);
+       if (rc != 0) {
+               err_msg("Cannot initialize volume table.");
+               goto err;
+       }
+
+       rc = read_pdd_data(io->fp_pdd, &pdd,
+                       err_buf, ERR_BUF_SIZE);
+       if (rc != 0) {
+               err_msg("Cannot read necessary pdd_data: %s rc: %d",
+                               err_buf, rc);
+               goto err;
+       }
+
+       rc = read_pfi_headers(&pfi_raws, &pfi_ubis, io->fp_pfi,
+                       err_buf, ERR_BUF_SIZE);
+       if (rc != 0) {
+               err_msg("Cannot read pfi header: %s rc: %d",
+                               err_buf, rc);
+               goto err;
+       }
+
+       pfi_raw_t pfi_raw;
+       foreach(pfi_raw, ptr, pfi_raws) {
+               rc = memorize_raw_eb(pfi_raw, pdd, &raw_pebs,
+                       io);
+               if (rc != 0) {
+                       err_msg("Cannot create raw_block in mem. rc: %d\n",
+                               rc);
+                       goto err;
+               }
+       }
+
+       pfi_ubi_t pfi_ubi;
+       foreach(pfi_ubi, ptr, pfi_ubis) {
+               rc = convert_ubi_volume(pfi_ubi, pdd, raw_pebs,
+                                       vol_tab, &ebs_written, io);
+               if (rc != 0) {
+                       err_msg("Cannot convert UBI volume. rc: %d\n", rc);
+                       goto err;
+               }
+       }
+
+       rc = write_ubi_volume_table(pdd, raw_pebs, vol_tab, vol_tab_size,
+                       &ebs_written, io);
+       if (rc != 0) {
+               err_msg("Cannot write UBI volume table. rc: %d\n", rc);
+               goto err;
+       }
+
+       rc  = write_remaining_raw_ebs(pdd, raw_pebs, &ebs_written, io->fp_out);
+       if (rc != 0)
+               goto err;
+
+       if (io->fp_out != stdout)
+               info_msg("Physical eraseblocks written: %8d\n", ebs_written);
+err:
+       free(vol_tab);
+       pfi_raws = remove_all((free_func_t)&free_pfi_raw, pfi_raws);
+       pfi_ubis = remove_all((free_func_t)&free_pfi_ubi, pfi_ubis);
+       raw_pebs = remove_all((free_func_t)&peb_free, raw_pebs);
+       free_pdd_data(&pdd);
+       return rc;
+}
+
+
+/* ------------------------------------------------------------------------- */
+static void
+open_io_handle(myargs *args, io_t io)
+{
+       /* set PDD input */
+       io->fp_pdd = fopen(args->f_in_pdd, "r");
+       if (io->fp_pdd == NULL) {
+               err_sys("Cannot open: %s", args->f_in_pdd);
+       }
+
+       /* set PFI input */
+       io->fp_pfi = fopen(args->f_in_pfi, "r");
+       if (io->fp_pfi == NULL) {
+               err_sys("Cannot open PFI input file: %s", args->f_in_pfi);
+       }
+
+       /* set output prefix */
+       if (strcmp(args->f_out,"") == 0)
+               io->fp_out = stdout;
+       else {
+               io->fp_out = fopen(args->f_out, "wb");
+               if (io->fp_out == NULL) {
+                       err_sys("Cannot open output file: %s", args->f_out);
+               }
+       }
+}
+
+static void
+close_io_handle(io_t io)
+{
+       if (fclose(io->fp_pdd) != 0) {
+               err_sys("Cannot close PDD file.");
+       }
+       if (fclose(io->fp_pfi) != 0) {
+               err_sys("Cannot close PFI file.");
+       }
+       if (io->fp_out != stdout) {
+               if (fclose(io->fp_out) != 0) {
+                       err_sys("Cannot close output file.");
+               }
+       }
+
+       io->fp_pdd = NULL;
+       io->fp_pfi = NULL;
+       io->fp_out = NULL;
+}
+
+int
+main(int argc, char *argv[])
+{
+       int rc = 0;
+
+       ubigen_init();
+       init_crc32_table(crc32_table);
+
+       struct io io = {NULL, NULL, NULL};
+       myargs args = {
+               .action = ACT_RAW,
+               .verbose = 0,
+
+               .f_in_pfi = "",
+               .f_in_pdd = "",
+               .f_out = "",
+
+               /* arguments */
+               .arg1 = NULL,
+               .options = NULL,
+       };
+
+       /* parse arguments */
+       parse_opt(argc, argv, &args);
+
+       if (strcmp(args.f_in_pfi, "") == 0) {
+               err_quit("No PFI input file specified!");
+       }
+
+       if (strcmp(args.f_in_pdd, "") == 0) {
+               err_quit("No PDD input file specified!");
+       }
+
+       open_io_handle(&args, &io);
+
+       info_msg("[ Creating RAW...");
+       rc = create_raw(&io);
+       if (rc != 0) {
+               err_msg("Creating RAW failed.");
+               goto err;
+       }
+
+err:
+       close_io_handle(&io);
+       if (rc != 0) {
+               remove(args.f_out);
+       }
+
+       return rc;
+}