diff options
Diffstat (limited to 'ubi-utils')
| -rw-r--r-- | ubi-utils/sort-me-out/bootenv.c | 1032 | ||||
| -rw-r--r-- | ubi-utils/sort-me-out/bootenv.h | 434 | ||||
| -rw-r--r-- | ubi-utils/sort-me-out/config.h | 28 | ||||
| -rw-r--r-- | ubi-utils/sort-me-out/crc32.c | 83 | ||||
| -rw-r--r-- | ubi-utils/sort-me-out/crc32.h | 36 | ||||
| -rw-r--r-- | ubi-utils/sort-me-out/eb_chain.c | 280 | ||||
| -rw-r--r-- | ubi-utils/sort-me-out/example_ubi.h | 28 | ||||
| -rw-r--r-- | ubi-utils/sort-me-out/hashmap.c | 412 | ||||
| -rw-r--r-- | ubi-utils/sort-me-out/hashmap.h | 49 | ||||
| -rw-r--r-- | ubi-utils/sort-me-out/pfi2bin.c | 681 | 
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; +} | 
