summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ubi-utils/sort-me-out/bootenv.c1032
-rw-r--r--ubi-utils/sort-me-out/bootenv.h434
-rw-r--r--ubi-utils/sort-me-out/config.h28
-rw-r--r--ubi-utils/sort-me-out/crc32.c83
-rw-r--r--ubi-utils/sort-me-out/crc32.h36
-rw-r--r--ubi-utils/sort-me-out/eb_chain.c280
-rw-r--r--ubi-utils/sort-me-out/example_ubi.h28
-rw-r--r--ubi-utils/sort-me-out/hashmap.c412
-rw-r--r--ubi-utils/sort-me-out/hashmap.h49
-rw-r--r--ubi-utils/sort-me-out/pfi2bin.c681
10 files changed, 3063 insertions, 0 deletions
diff --git a/ubi-utils/sort-me-out/bootenv.c b/ubi-utils/sort-me-out/bootenv.c
new file mode 100644
index 0000000..a6dd4de
--- /dev/null
+++ b/ubi-utils/sort-me-out/bootenv.c
@@ -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
index 0000000..8fecdbf
--- /dev/null
+++ b/ubi-utils/sort-me-out/bootenv.h
@@ -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
index 0000000..55e60f3
--- /dev/null
+++ b/ubi-utils/sort-me-out/config.h
@@ -0,0 +1,28 @@
+#ifndef __CONFIG_H__
+#define __CONFIG_H__
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Frank Haverkamp
+ */
+
+#define PACKAGE_BUGREPORT \
+ "haver@vnet.ibm.com, dedekind@linutronix.de, or tglx@linutronix.de"
+
+#define 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
index 0000000..666e217
--- /dev/null
+++ b/ubi-utils/sort-me-out/crc32.c
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Thomas Gleixner
+ */
+
+/*
+ * CRC32 functions
+ *
+ * Can be compiled as seperate object, but is included into the ipl source
+ * so gcc can inline the functions. We optimize for size so the omission of
+ * the function frame is helpful.
+ *
+ */
+
+#include <stdint.h>
+#include <crc32.h>
+
+/* CRC polynomial */
+#define CRC_POLY 0xEDB88320
+
+/**
+ * init_crc32_table - Initialize crc table
+ *
+ * @table: pointer to the CRC table which must be initialized
+ *
+ * Create CRC32 table for given polynomial. The table is created with
+ * the lowest order term in the highest order bit. So the x^32 term
+ * has to implied in the crc calculation function.
+ */
+void init_crc32_table(uint32_t *table)
+{
+ uint32_t crc;
+ int i, j;
+
+ for (i = 0; i < 256; i++) {
+ crc = i;
+ for (j = 8; j > 0; j--) {
+ if (crc & 1)
+ crc = (crc >> 1) ^ CRC_POLY;
+ else
+ crc >>= 1;
+ }
+ table[i] = crc;
+ }
+}
+
+/**
+ * clc_crc32 - Calculate CRC32 over a buffer
+ *
+ * @table: pointer to the CRC table
+ * @crc: initial crc value
+ * @buf: pointer to the buffer
+ * @len: number of bytes to calc
+ *
+ * Returns the updated crc value.
+ *
+ * The algorithm resembles a hardware shift register, but calculates 8
+ * bit at once.
+ */
+uint32_t clc_crc32(uint32_t *table, uint32_t crc, void *buf,
+ int len)
+{
+ const unsigned char *p = buf;
+
+ while(--len >= 0)
+ crc = table[(crc ^ *p++) & 0xff] ^ (crc >> 8);
+ return crc;
+}
diff --git a/ubi-utils/sort-me-out/crc32.h b/ubi-utils/sort-me-out/crc32.h
new file mode 100644
index 0000000..31362b0
--- /dev/null
+++ b/ubi-utils/sort-me-out/crc32.h
@@ -0,0 +1,36 @@
+#ifndef __CRC32_H__
+#define __CRC32_H__
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * Author: Thomas Gleixner
+ *
+ * CRC32 functions
+ *
+ * Can be compiled as seperate object, but is included into the ipl source
+ * so gcc can inline the functions. We optimize for size so the omission of
+ * the function frame is helpful.
+ *
+ */
+#include <stdint.h>
+
+void init_crc32_table(uint32_t *table);
+uint32_t clc_crc32(uint32_t *table, uint32_t crc, void *buf, int len);
+
+#endif /* __CRC32_H__ */
diff --git a/ubi-utils/sort-me-out/eb_chain.c b/ubi-utils/sort-me-out/eb_chain.c
new file mode 100644
index 0000000..a018ae6
--- /dev/null
+++ b/ubi-utils/sort-me-out/eb_chain.c
@@ -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
index 0000000..23c7b54
--- /dev/null
+++ b/ubi-utils/sort-me-out/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/sort-me-out/hashmap.c b/ubi-utils/sort-me-out/hashmap.c
new file mode 100644
index 0000000..3511d56
--- /dev/null
+++ b/ubi-utils/sort-me-out/hashmap.c
@@ -0,0 +1,412 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Oliver Lohmann
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "error.h"
+#include "hashmap.h"
+#define DEFAULT_BUCKETS 4096
+
+#if 0
+#define INFO_MSG(fmt...) do { \
+ info_msg(fmt); \
+} while (0)
+#else
+#define INFO_MSG(fmt...)
+#endif
+
+struct hashentry {
+ char* key; /* key '0' term. str */
+ char* value; /* payload '0' term. str */
+
+ hashentry_t next;
+};
+
+struct hashmap {
+ size_t entries; /* current #entries */
+ size_t maxsize; /* no. of hash buckets */
+ hashentry_t* data; /* array of buckets */
+};
+
+static int
+is_empty(hashentry_t l)
+{
+ return l == NULL ? 1 : 0;
+}
+
+hashmap_t
+hashmap_new(void)
+{
+ hashmap_t res;
+ res = (hashmap_t) calloc(1, sizeof(struct hashmap));
+
+ if (res == NULL)
+ return NULL;
+
+ res->maxsize = DEFAULT_BUCKETS;
+ res->entries = 0;
+
+ res->data = (hashentry_t*)
+ calloc(1, res->maxsize * sizeof(struct hashentry));
+
+ if (res->data == NULL)
+ return NULL;
+
+ return res;
+}
+
+static hashentry_t
+new_entry(const char* key, const char* value)
+{
+ hashentry_t res;
+
+ res = (hashentry_t) calloc(1, sizeof(struct hashentry));
+
+ if (res == NULL)
+ return NULL;
+
+ /* allocate key and value and copy them */
+ res->key = strdup(key);
+ if (res->key == NULL) {
+ free(res);
+ return NULL;
+ }
+
+ res->value = strdup(value);
+ if (res->value == NULL) {
+ free(res->key);
+ free(res);
+ return NULL;
+ }
+
+ res->next = NULL;
+
+ return res;
+}
+
+static hashentry_t
+free_entry(hashentry_t e)
+{
+ if (!is_empty(e)) {
+ if(e->key != NULL) {
+ free(e->key);
+ }
+ if(e->value != NULL)
+ free(e->value);
+ free(e);
+ }
+
+ return NULL;
+}
+
+static hashentry_t
+remove_entry(hashentry_t l, const char* key, size_t* entries)
+{
+ hashentry_t lnext;
+ if (is_empty(l))
+ return NULL;
+
+ if(strcmp(l->key,key) == 0) {
+ lnext = l->next;
+ l = free_entry(l);
+ (*entries)--;
+ return lnext;
+ }
+
+ l->next = remove_entry(l->next, key, entries);
+
+ return l;
+}
+
+static hashentry_t
+insert_entry(hashentry_t l, hashentry_t e, size_t* entries)
+{
+ if (is_empty(l)) {
+ (*entries)++;
+ return e;
+ }
+
+ /* check for update */
+ if (strcmp(l->key, e->key) == 0) {
+ e->next = l->next;
+ l = free_entry(l);
+ return e;
+ }
+
+ l->next = insert_entry(l->next, e, entries);
+ return l;
+}
+
+static hashentry_t
+remove_all(hashentry_t l, size_t* entries)
+{
+ hashentry_t lnext;
+ if (is_empty(l))
+ return NULL;
+
+ lnext = l->next;
+ free_entry(l);
+ (*entries)--;
+
+ return remove_all(lnext, entries);
+}
+
+static const char*
+value_lookup(hashentry_t l, const char* key)
+{
+ if (is_empty(l))
+ return NULL;
+
+ if (strcmp(l->key, key) == 0)
+ return l->value;
+
+ return value_lookup(l->next, key);
+}
+
+static void
+print_all(hashentry_t l)
+{
+ if (is_empty(l)) {
+ printf("\n");
+ return;
+ }
+
+ printf("%s=%s", l->key, l->value);
+ if (!is_empty(l->next)) {
+ printf(",");
+ }
+
+ print_all(l->next);
+}
+
+static void
+keys_to_array(hashentry_t l, const char** a, size_t* i)
+{
+ if (is_empty(l))
+ return;
+
+ a[*i] = l->key;
+ (*i)++;
+
+ keys_to_array(l->next, a, i);
+}
+
+uint32_t
+hash_str(const char* str, uint32_t mapsize)
+{
+ uint32_t hash = 0;
+ uint32_t x = 0;
+ uint32_t i = 0;
+ size_t len = strlen(str);
+
+ for(i = 0; i < len; str++, i++) {
+ hash = (hash << 4) + (*str);
+ if((x = hash & 0xF0000000L) != 0) {
+ hash ^= (x >> 24);
+ hash &= ~x;
+ }
+ }
+
+ return (hash & 0x7FFFFFFF) % mapsize;
+}
+
+
+int
+hashmap_is_empty(hashmap_t map)
+{
+ if (map == NULL)
+ return -EINVAL;
+
+ return map->entries > 0 ? 1 : 0;
+}
+
+const char*
+hashmap_lookup(hashmap_t map, const char* key)
+{
+ uint32_t i;
+
+ if ((map == NULL) || (key == NULL))
+ return NULL;
+
+ i = hash_str(key, map->maxsize);
+
+ return value_lookup(map->data[i], key);
+}
+
+int
+hashmap_add(hashmap_t map, const char* key, const char* value)
+{
+ uint32_t i;
+ hashentry_t entry;
+
+ if ((map == NULL) || (key == NULL) || (value == NULL))
+ return -EINVAL;
+
+ i = hash_str(key, map->maxsize);
+ entry = new_entry(key, value);
+ if (entry == NULL)
+ return -ENOMEM;
+
+ map->data[i] = insert_entry(map->data[i],
+ entry, &map->entries);
+
+ INFO_MSG("HASH_ADD: chain[%d] key:%s val:%s",i, key, value);
+ return 0;
+}
+
+int
+hashmap_remove(hashmap_t map, const char* key)
+{
+ uint32_t i;
+
+ if ((map == NULL) || (key == NULL))
+ return -EINVAL;
+
+ i = hash_str(key, map->maxsize);
+ map->data[i] = remove_entry(map->data[i], key, &map->entries);
+
+ return 0;
+}
+
+size_t
+hashmap_size(hashmap_t map)
+{
+ if (map != NULL)
+ return map->entries;
+ else
+ return 0;
+}
+
+int
+hashmap_free(hashmap_t map)
+{
+ size_t i;
+
+ if (map == NULL)
+ return -EINVAL;
+
+ /* "children" first */
+ for(i = 0; i < map->maxsize; i++) {
+ map->data[i] = remove_all(map->data[i], &map->entries);
+ }
+ free(map->data);
+ free(map);
+
+ return 0;
+}
+
+int
+hashmap_dump(hashmap_t map)
+{
+ size_t i;
+ if (map == NULL)
+ return -EINVAL;
+
+ for(i = 0; i < map->maxsize; i++) {
+ if (map->data[i] != NULL) {
+ printf("[%zd]: ", i);
+ print_all(map->data[i]);
+ }
+ }
+
+ return 0;
+}
+
+static const char**
+sort_key_vector(const char** a, size_t size)
+{
+ /* uses bubblesort */
+ size_t i, j;
+ const char* tmp;
+
+ if (size <= 0)
+ return a;
+
+ for (i = size - 1; i > 0; i--) {
+ for (j = 0; j < i; j++) {
+ if (strcmp(a[j], a[j+1]) > 0) {
+ tmp = a[j];
+ a[j] = a[j+1];
+ a[j+1] = tmp;
+ }
+ }
+ }
+ return a;
+}
+
+const char**
+hashmap_get_key_vector(hashmap_t map, size_t* size, int sort)
+{
+ const char** res;
+ size_t i, j;
+ *size = map->entries;
+
+ res = (const char**) malloc(*size * sizeof(char*));
+ if (res == NULL)
+ return NULL;
+
+ j = 0;
+ for(i=0; i < map->maxsize; i++) {
+ keys_to_array(map->data[i], res, &j);
+ }
+
+ if (sort)
+ res = sort_key_vector(res, *size);
+
+ return res;
+}
+
+int
+hashmap_key_is_in_vector(const char** vec, size_t size, const char* key)
+{
+ size_t i;
+ for (i = 0; i < size; i++) {
+ if (strcmp(vec[i], key) == 0) /* found */
+ return 1;
+ }
+
+ return 0;
+}
+
+const char**
+hashmap_get_update_key_vector(const char** vec1, size_t vec1_size,
+ const char** vec2, size_t vec2_size, size_t* res_size)
+{
+ const char** res;
+ size_t i, j;
+
+ *res_size = vec2_size;
+
+ res = (const char**) malloc(*res_size * sizeof(char*));
+ if (res == NULL)
+ return NULL;
+
+ /* get all keys from vec2 which are not set in vec1 */
+ j = 0;
+ for (i = 0; i < vec2_size; i++) {
+ if (!hashmap_key_is_in_vector(vec1, vec1_size, vec2[i]))
+ res[j++] = vec2[i];
+ }
+
+ *res_size = j;
+ return res;
+}
diff --git a/ubi-utils/sort-me-out/hashmap.h b/ubi-utils/sort-me-out/hashmap.h
new file mode 100644
index 0000000..1b13e95
--- /dev/null
+++ b/ubi-utils/sort-me-out/hashmap.h
@@ -0,0 +1,49 @@
+#ifndef __HASHMAP_H__
+#define __HASHMAP_H__
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Oliver Lohmann
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+
+typedef struct hashentry *hashentry_t;
+typedef struct hashmap *hashmap_t;
+
+hashmap_t hashmap_new(void);
+int hashmap_free(hashmap_t map);
+
+int hashmap_add(hashmap_t map, const char* key, const char* value);
+int hashmap_update(hashmap_t map, const char* key, const char* value);
+int hashmap_remove(hashmap_t map, const char* key);
+const char* hashmap_lookup(hashmap_t map, const char* key);
+
+const char** hashmap_get_key_vector(hashmap_t map, size_t* size, int sort);
+int hashmap_key_is_in_vector(const char** vec, size_t size, const char* key);
+const char** hashmap_get_update_key_vector(const char** vec1, size_t vec1_size,
+ const char** vec2, size_t vec2_size, size_t* res_size);
+
+int hashmap_dump(hashmap_t map);
+
+int hashmap_is_empty(hashmap_t map);
+size_t hashmap_size(hashmap_t map);
+
+uint32_t hash_str(const char* str, uint32_t mapsize);
+
+#endif /* __HASHMAP_H__ */
diff --git a/ubi-utils/sort-me-out/pfi2bin.c b/ubi-utils/sort-me-out/pfi2bin.c
new file mode 100644
index 0000000..7f31938
--- /dev/null
+++ b/ubi-utils/sort-me-out/pfi2bin.c
@@ -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;
+}