diff options
Diffstat (limited to 'ubi-utils/src')
39 files changed, 11352 insertions, 0 deletions
diff --git a/ubi-utils/src/bin2nand/bin2nand.c b/ubi-utils/src/bin2nand/bin2nand.c new file mode 100644 index 0000000..5224e3b --- /dev/null +++ b/ubi-utils/src/bin2nand/bin2nand.c @@ -0,0 +1,343 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Oliver Lohmann + */ + +/* + * Create a flashable NAND image from a binary image + * + * History: + *	1.0:	Initial release (tglx) + * + *	1.1:	Understands hex and dec input parameters (tglx) + *	1.2:	Generates separated OOB data, if needed. (oloh) + *	1.3:	Padds data/oob to a given size. (oloh) + * + */ + +#include <unistd.h> +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> +#include <getopt.h> +#include <argp.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> + +#include "error.h" +#include "config.h" +#include "nandecc.h" + +#define CHECK_ENDP(option, endp) do {			\ +	if (*endp) {					\ +		fprintf(stderr,				\ +			"Parse error option \'%s\'. "	\ +			"No correct numeric value.\n"	\ +			, option);			\ +		exit(EXIT_FAILURE);			\ +	}						\ +} while(0) + +typedef enum action_t { +	ACT_NORMAL	    = 0x00000001, +} action_t; + +#define PAGESIZE	2048 +#define PADDING		   0 /* 0 means, do not adjust anything */ +#define BUFSIZE		4096 + +const char *argp_program_version = PACKAGE_VERSION; +const char *argp_program_bug_address = PACKAGE_BUGREPORT; +static char doc[] = "\nVersion: " PACKAGE_VERSION "\n\tBuilt on " +	BUILD_CPU" "BUILD_OS" at "__DATE__" "__TIME__"\n" +	"\n" +	"bin2nand - a tool for adding OOB information to a " +	"binary input file.\n"; + +static const char copyright [] __attribute__((unused)) = +	"FIXME: insert license type."; /* FIXME */ + +static struct argp_option options[] = { +	{ name: "copyright", key: 'c', arg: NULL, flags: 0, +	  doc: "Print copyright information.", +	  group: 1 }, + +	{ name: "pagesize", key: 'p', arg: "<num>", flags: 0, +	  doc: "Pagesize in Byte/Mi/ki. Default: 2048", +	  group: 1 }, + +	{ name: "padding", key: 'j', arg: "<num>", flags: 0, +	  doc: "Padding in Byte/Mi/ki. Default: no padding", +	  group: 1 }, + +	/* Output options */ +	{ name: NULL, key: 0, arg: NULL, flags: 0, +	  doc: "Output settings:", +	  group: 2 }, + +	{ name: "output", key: 'o', arg: "<fname>", flags: 0, +	  doc: "Output filename. Interleaved Data/OOB if output-oob not " +	       "specified.", +	  group: 2 }, + +	{ name: "output-oob", key: 'q', arg: "<fname>", flags: 0, +	  doc: "Write OOB data in separate file.", +	  group: 2 }, + +	{ name: NULL, key: 0, arg: NULL, flags: 0, doc: NULL, group: 0 }, +}; + +typedef struct myargs { +	action_t action; + +	size_t pagesize; +	size_t padding; + +	FILE* fp_in; +	char *file_out_data; /* Either: Data and OOB interleaved +				or plain data */ +	char *file_out_oob; /* OOB Data only. */ + +	/* special stuff needed to get additional arguments */ +	char *arg1; +	char **options;			/* [STRING...] */ +} myargs; + + +static int ustrtoull(const char *cp, char **endp, unsigned int base) +{ +	unsigned long long res = strtoull(cp, endp, base); + +	switch (**endp) { +	case 'G': +		res *= 1024; +	case 'M': +		res *= 1024; +	case 'k': +	case 'K': +		res *= 1024; +	/* "Ki", "ki", "Mi" or "Gi" are to be used. */ +		if ((*endp)[1] == 'i') +			(*endp) += 2; +	} +	return res; +} + +static error_t +parse_opt(int key, char *arg, struct argp_state *state) +{ +	int err = 0; +	char* endp; + +	myargs *args = state->input; + +	switch (key) { +	case 'p': /* pagesize */ +		args->pagesize = (size_t) ustrtoull(arg, &endp, 0); +		CHECK_ENDP("p", endp); +		break; +	case 'j': /* padding */ +		args->padding = (size_t) ustrtoull(arg, &endp, 0); +		CHECK_ENDP("j", endp); +		break; +	case 'o': /* output */ +		args->file_out_data = arg; +		break; +	case 'q': /* output oob */ +		args->file_out_oob = arg; +		break; +	case ARGP_KEY_ARG: /* input file */ +		args->fp_in = fopen(arg, "rb"); +		if ((args->fp_in) == NULL) { +			err_quit("Cannot open file %s for input\n", arg); +		} +		args->arg1 = arg; +		args->options = &state->argv[state->next]; +		state->next = state->argc; +		break; +	case ARGP_KEY_END: +		if (err) { +			err_msg("\n"); +			argp_usage(state); +			exit(EXIT_FAILURE); +		} +		break; +	default: +		return(ARGP_ERR_UNKNOWN); +	} + +	return 0; +} + +static struct argp argp = { +	options:     options, +	parser:	     parse_opt, +	args_doc:    0, +	doc:	     doc, +	children:    NULL, +	help_filter: NULL, +	argp_domain: NULL, +}; + +static int +process_page(uint8_t* buf, size_t pagesize, +	FILE *fp_data, FILE* fp_oob, size_t* written) +{ +	int eccpoi, oobsize; +	size_t i; +	uint8_t oobbuf[64]; + +	memset(oobbuf, 0xff, sizeof(oobbuf)); + +	switch(pagesize) { +	case 2048: oobsize = 64; eccpoi = 64 / 2; break; +	case 512:  oobsize = 16; eccpoi = 16 / 2; break; +	default: +		err_msg("Unsupported page size: %d\n", pagesize); +		return -EINVAL; +	} + +	for (i = 0; i < pagesize; i += 256, eccpoi += 3) { +		oobbuf[eccpoi++] = 0x0; +		/* Calculate ECC */ +		nand_calculate_ecc(&buf[i], &oobbuf[eccpoi]); +	} + +	/* write data */ +	*written += fwrite(buf, 1, pagesize, fp_data); + +	/* either separate oob or interleave with data */ +	if (fp_oob) { +		fwrite(oobbuf, 1, oobsize, fp_oob); +		if (ferror(fp_oob)) { +			err_msg("IO error\n"); +			return -EIO; +		} +	} +	else { +		fwrite(oobbuf, 1, oobsize, fp_data); +		if (ferror(fp_data)) { +			err_msg("IO error\n"); +			return -EIO; +		} +	} + +	return 0; +} + +int main (int argc, char** argv) +{ +	int rc = -1; +	int res = 0; +	size_t written, read; +	myargs args = { +		.action	  = ACT_NORMAL, +		.pagesize = PAGESIZE, +		.padding  = PADDING, +		.fp_in	  = NULL, +		.file_out_data = "", +		.file_out_oob = "", +	}; + +	FILE* fp_out_data = stdout; +	FILE* fp_out_oob = NULL; + +	argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &args); + +	uint8_t* buf = calloc(1, BUFSIZE); +	if (!buf) { +		err_quit("Cannot allocate page buffer.\n"); +	} + +	if (!args.fp_in) { +		err_msg("No input image specified!\n"); +		goto err; +	} + +	if (strcmp(args.file_out_data, "") != 0) { +		fp_out_data = fopen(args.file_out_data, "wb"); +		if (fp_out_data == NULL) { +			err_sys("Cannot open file %s for output\n", +					args.file_out_data); +			goto err; +		} +	} + +	if (strcmp(args.file_out_oob, "") != 0) { +		fp_out_oob = fopen(args.file_out_oob, "wb"); +		if (fp_out_oob == NULL) { +			err_sys("Cannot open file %s for output\n", +					args.file_out_oob); +			goto err; +		} +	} + + +	while(1) { +		read = fread(buf, 1, args.pagesize, args.fp_in); +		if (feof(args.fp_in) && read == 0) +			break; + +		if (read < args.pagesize) { +			err_msg("Image not page aligned\n"); +			goto err; +		} + +		if (ferror(args.fp_in)) { +			err_msg("Read error\n"); +			goto err; +		} + +		res = process_page(buf, args.pagesize, fp_out_data, +				fp_out_oob, &written); +		if (res != 0) +			goto err; +	} + +	while (written < args.padding) { +		memset(buf, 0xff, args.pagesize); +		res = process_page(buf, args.pagesize, fp_out_data, +				fp_out_oob, &written); +		if (res != 0) +			goto err; +	} + +	rc = 0; +err: +	free(buf); + +	if (args.fp_in) +		fclose(args.fp_in); + +	if (fp_out_oob) +		fclose(fp_out_oob); + +	if (fp_out_data && fp_out_data != stdout) +		fclose(fp_out_data); + +	if (rc != 0) { +		err_msg("Error during conversion. rc: %d\n", rc); +		remove(args.file_out_data); +		remove(args.file_out_oob); +	} +	return rc; +} diff --git a/ubi-utils/src/bin2nand/nandecc.c b/ubi-utils/src/bin2nand/nandecc.c new file mode 100644 index 0000000..71660ef --- /dev/null +++ b/ubi-utils/src/bin2nand/nandecc.c @@ -0,0 +1,159 @@ +/* + * This file contains an ECC algorithm from Toshiba that detects and + * corrects 1 bit errors in a 256 byte block of data. + * + * drivers/mtd/nand/nand_ecc.c + * + * Copyright (C) 2000-2004 Steven J. Hill (sjhill@realitydiluted.com) + *			   Toshiba America Electronics Components, Inc. + * + * This file is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 or (at your option) any + * later version. + * + * This file is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this file; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * As a special exception, if other files instantiate templates or use + * macros or inline functions from these files, or you compile these + * files and link them with other works to produce a work based on these + * files, these files do not by themselves cause the resulting work to be + * covered by the GNU General Public License. However the source code for + * these files must still be made available in accordance with section (3) + * of the GNU General Public License. + * + * This exception does not invalidate any other reasons why a work based on + * this file might be covered by the GNU General Public License. + */ + +#include "nandecc.h" + +/* + * Pre-calculated 256-way 1 byte column parity + */ +static const uint8_t nand_ecc_precalc_table[] = { +	0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, +	0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00, +	0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, +	0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65, +	0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, +	0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66, +	0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, +	0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03, +	0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, +	0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69, +	0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, +	0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c, +	0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, +	0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f, +	0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, +	0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a, +	0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, +	0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a, +	0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, +	0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f, +	0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, +	0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c, +	0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, +	0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69, +	0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, +	0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03, +	0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, +	0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66, +	0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, +	0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65, +	0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, +	0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00 +}; + +/** + * nand_trans_result - [GENERIC] create non-inverted ECC + * @reg2:	line parity reg 2 + * @reg3:	line parity reg 3 + * @ecc_code:	ecc + * + * Creates non-inverted ECC code from line parity + */ +static void nand_trans_result(uint8_t reg2, uint8_t reg3, +	uint8_t *ecc_code) +{ +	uint8_t a, b, i, tmp1, tmp2; + +	/* Initialize variables */ +	a = b = 0x80; +	tmp1 = tmp2 = 0; + +	/* Calculate first ECC byte */ +	for (i = 0; i < 4; i++) { +		if (reg3 & a)		/* LP15,13,11,9 --> ecc_code[0] */ +			tmp1 |= b; +		b >>= 1; +		if (reg2 & a)		/* LP14,12,10,8 --> ecc_code[0] */ +			tmp1 |= b; +		b >>= 1; +		a >>= 1; +	} + +	/* Calculate second ECC byte */ +	b = 0x80; +	for (i = 0; i < 4; i++) { +		if (reg3 & a)		/* LP7,5,3,1 --> ecc_code[1] */ +			tmp2 |= b; +		b >>= 1; +		if (reg2 & a)		/* LP6,4,2,0 --> ecc_code[1] */ +			tmp2 |= b; +		b >>= 1; +		a >>= 1; +	} + +	/* Store two of the ECC bytes */ +	ecc_code[1] = tmp1; +	ecc_code[0] = tmp2; +} + +/** + * nand_calculate_ecc - [NAND Interface] Calculate 3 byte ECC code for + * 256 byte block + * + * @dat:	raw data + * @ecc_code:	buffer for ECC + */ +int nand_calculate_ecc(const uint8_t *dat, uint8_t *ecc_code) +{ +	uint8_t idx, reg1, reg2, reg3; +	int j; + +	/* Initialize variables */ +	reg1 = reg2 = reg3 = 0; +	ecc_code[0] = ecc_code[1] = ecc_code[2] = 0; + +	/* Build up column parity */ +	for(j = 0; j < 256; j++) { + +		/* Get CP0 - CP5 from table */ +		idx = nand_ecc_precalc_table[dat[j]]; +		reg1 ^= (idx & 0x3f); + +		/* All bit XOR = 1 ? */ +		if (idx & 0x40) { +			reg3 ^= (uint8_t) j; +			reg2 ^= ~((uint8_t) j); +		} +	} + +	/* Create non-inverted ECC code from line parity */ +	nand_trans_result(reg2, reg3, ecc_code); + +	/* Calculate final ECC code */ +	ecc_code[0] = ~ecc_code[0]; +	ecc_code[1] = ~ecc_code[1]; +	ecc_code[2] = ((~reg1) << 2) | 0x03; +	return 0; +} diff --git a/ubi-utils/src/bin2nand/nandecc.h b/ubi-utils/src/bin2nand/nandecc.h new file mode 100644 index 0000000..8ae8a66 --- /dev/null +++ b/ubi-utils/src/bin2nand/nandecc.h @@ -0,0 +1,11 @@ +/* + * NAND ecc functions + */ +#ifndef _NAND_ECC_H +#define _NAND_ECC_H + +#include <stdint.h> + +extern int nand_calculate_ecc(const uint8_t *dat, uint8_t *ecc_code); + +#endif diff --git a/ubi-utils/src/libbootenv/bootenv.c b/ubi-utils/src/libbootenv/bootenv.c new file mode 100644 index 0000000..b6a1191 --- /dev/null +++ b/ubi-utils/src/libbootenv/bootenv.c @@ -0,0 +1,959 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Oliver Lohmann + */ + +#include <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" + +#define BOOTENV_MAXLINE 512 /* max line size of a bootenv.txt file */ + +/* Structures */ +struct bootenv { +	hashmap_t map;	 ///< Pointer to hashmap which holds data structure. +}; + +struct bootenv_list { +	hashmap_t head; ///< Pointer to list which holds the data structure. +}; + +/** + * @brief Remove the '\n' from a given line. + * @param line	Input/Output line. + * @param size	Size of the line. + * @param fp	File Pointer. + * @return 0 + * @return or error + */ +static int +remove_lf(char *line, size_t size, FILE* fp) +{ +	size_t i; + +	for (i = 0; i < size; i++) { +		if (line[i] == '\n') { +			line[i] = '\0'; +			return 0; +		} +	} + +	if (!feof(fp)) { +		return BOOTENV_EINVAL; +	} + +	return 0; +} + +/** + * @brief Determine if a line contains only WS. + * @param line The line to process. + * @param size Size of input line. + * @return 1	Yes, only WS. + * @return 0	No, contains data. + */ +static int +is_ws(const char *line, size_t size) +{ +	size_t i = 0; + +	while (i < size) { +		switch (line[i]) { +			case '\n': +				return 1; +			case '#': +				return 1; +			case ' ': +				i++; +				continue; +			case '\t': +				i++; +				continue; +			default: /* any other char -> no cmnt */ +				return 0; +		} +	} + +	return 0; +} + + +/* ------------------------------------------------------------------------- */ + +/** + * @brief Build a list from a comma seperated value string. + * @param list	Pointer to hashmap structure which shall store + *		the list. + * @param value	Comma seperated value string. + * @return 0 + * @return or error. + */ +static int +build_list_definition(hashmap_t list, const char *value) +{ +	int rc = 0; +	char *str = NULL; +	char *ptr = NULL; +	size_t len, i, j; + +	/* str: val1,val2 , val4,...,valN     */ +	len = strlen(value); +	str = (char*) malloc((len+1) * sizeof(char)); + +	/* 1. reformat string: remove spaces */ +	for (i = 0, j = 0; i < len; i++) { +		if (value[i] == ' ') +			continue; + +		str[j] = value[i]; +		j++; +	} +	str[j] = '\0'; + +	/* str: val1,val2,val4,...,valN\0*/ +	/* 2. replace ',' seperator with '\0' */ +	len = strlen(str); +	for (i = 0; i < len; i++) { +		if (str[i] == ',') { +			str[i] = '\0'; +		} +	} + +	/* str: val1\0val2\0val4\0...\0valN\0*/ +	/* 3. insert definitions into a hash map, using it like a list */ +	i = j = 0; +	ptr = str; +	while (((i = strlen(ptr)) > 0) && (j < len)) { +		rc = hashmap_add(list, ptr, ""); +		if (rc != 0) { +			free(str); +			return rc; +		} +		j += i+1; +		if (j < len) +			ptr += i+1; +	} + +	free(str); +	return rc; +} + +/** + * @brief Extract a key value pair and add it to a hashmap + * @param str	Input string which contains a key value pair. + * @param env	The updated handle which contains the new pair. + * @return 0 + * @return or error + * @note The input string format is: "key=value" + */ +static int +extract_pair(const char *str, bootenv_t env) +{ +	int rc = 0; +	char *key = NULL; +	char *val = NULL; + +	key = strdup(str); +	if (key == NULL) +		return -ENOMEM; + +	val = strstr(key, "="); +	if (val == NULL) { +		rc = BOOTENV_EBADENTRY; +		goto err; +	} + +	*val = '\0'; /* split strings */ +	val++; +	rc = bootenv_set(env, key, val); + +err: +	free(key); +	return rc; +} + +int +bootenv_destroy(bootenv_t* env) +{ +	int rc = 0; + +	if (env == NULL || *env == NULL) +		return -EINVAL; + +	bootenv_t tmp = *env; + +	rc = hashmap_free(tmp->map); +	if (rc != 0) +		return rc; + +	free(tmp); +	return rc; +} + +int +bootenv_create(bootenv_t* env) +{ +	bootenv_t res; +	res = (bootenv_t) calloc(1, sizeof(struct bootenv)); + +	if (res == NULL) +		return -ENOMEM; + +	res->map = hashmap_new(); + +	if (res->map == NULL) { +		free(res); +		return -ENOMEM; +	} + +	*env = res; + +	return 0; +} + + +/** + * @brief Read a formatted buffer and scan it for valid bootenv + *	  key/value pairs. Add those pairs into a hashmap. + * @param env	Hashmap which shall be used to hold the data. + * @param buf	Formatted buffer. + * @param size	Size of the buffer. + * @return 0 + * @return or error + */ +static int +rd_buffer(bootenv_t env, const char *buf, size_t size) +{ +	const char *curr = buf;	/* ptr to current key/value pair */ +	uint32_t i = 0;		/* current length */ +	uint32_t j = 0;		/* processed chars */ +	uint32_t items = 0;	/* processed items */ +	int rc = 0; + +	if (buf[size-1] != '\0') { +		return BOOTENV_EFMT; +	} + +	while ((i = strlen(curr)) != 0) { +		/* there is a key value pair remaining */ +		rc = extract_pair(curr, env); +		if (rc != 0) { +			rc = BOOTENV_EINVAL; +			return rc; +		} +		items++; + +		j += i; +		if (j >= size) +			return 0; /* finished, end of buffer */ +		curr += i + 1; +	} + +	return 0; +} + +/** + * If we have a single file containing the boot-parameter size should + * be specified either as the size of the file or as BOOTENV_MAXSIZE. + * If the bootparameter are in the middle of a file we need the exact + * length of the data. + */ +int +bootenv_read(FILE* fp, bootenv_t env, size_t size) +{ +	int rc; +	char *buf = NULL; +	size_t i = 0; + +	if ((fp == NULL) || (env == NULL)) +		return -EINVAL; + +	/* allocate temp buffer */ +	buf = (char*) calloc(1, size * sizeof(char)); +	if (buf == NULL) +		return -ENOMEM; + +	/* FIXME Andreas, please review this I removed size-1 and +	 * replaced it by just size, I saw the kernel image starting +	 * with a 0x0060.... and not with the 0x60.... what it should +	 * be. Is this a tools problem or is it a problem here where +	 * fp is moved not to the right place due to the former size-1 +	 * here. +	 */ +	while((i < size) && (!feof(fp))) { +		int c = fgetc(fp); + +		if (c == EOF) { +			buf[i++] = '\0'; +			break;	/* we have enough */ +		} + +		/* log_msg("%c", c); */	/* FIXME DBG */ + +		buf[i++] = c; +		if (ferror(fp)) { +			rc = -EIO; +			goto err; +		} +	} + +	/* transfer to hashmap */ +	rc = rd_buffer(env, buf, size); + +	/* FIXME DBG */ +	/* log_msg("\n%s:%d rc=%d\n", __func__, __LINE__, rc); */ + +err: +	free(buf); +	return rc; +} + + + +int +bootenv_read_txt(FILE* fp, bootenv_t env) +{ +	int rc = 0; +	char *buf = NULL; +	char *line = NULL; +	char *lstart = NULL; +	char *curr = NULL; +	size_t len; +	size_t size; + +	if ((fp == NULL) || (env == NULL)) +		return -EINVAL; + +	size = BOOTENV_MAXSIZE; + +	/* allocate temp buffers */ +	buf = (char*) calloc(1, size * sizeof(char)); +	lstart = line = (char*) calloc(1, size * sizeof(char)); +	if ((buf == NULL)  || (line == NULL)) { +		rc = -ENOMEM; +		goto err; +	} + +	curr = buf; +	while ((line = fgets(line, size, fp)) != NULL) { +		if (is_ws(line, size)) { +			continue; +		} +		rc = remove_lf(line, BOOTENV_MAXSIZE, fp); +		if (rc != 0) { +			goto err; +		} + +		/* copy new line to binary buffer */ +		len = strlen(line); +		if (len > size) { +			rc = -EFBIG; +			goto err; +		} +		size -= len; /* track remaining space */ + +		memcpy(curr, line, len); +		curr += len + 1; /* for \0 seperator */ +	} + +	rc = rd_buffer(env, buf, BOOTENV_MAXSIZE); +err: +	if (buf != NULL) +		free(buf); +	if (lstart != NULL) +		free(lstart); +	return rc; +} + +static int +fill_output_buffer(bootenv_t env, char *buf, size_t buf_size_max, +		size_t *written) +{ +	int rc = 0; +	size_t keys_size, i; +	size_t wr = 0; +	const char **keys = NULL; +	const char *val = NULL; + +	rc = bootenv_get_key_vector(env, &keys_size, 1, &keys); +	if (rc != 0) +		goto err; + +	for (i = 0; i < keys_size; i++) { +		if (wr > BOOTENV_MAXSIZE) { +			rc = -ENOSPC; +			goto err; +		} + +		rc = bootenv_get(env, keys[i], &val); +		if (rc != 0) +			goto err; + +		wr += snprintf(buf + wr, BOOTENV_MAXSIZE - wr, +				"%s=%s", keys[i], val); +		wr++; /* for \0 */ +	} + +	*written = wr; + +err: +	if (keys != NULL) +		free(keys); + +	return rc; +} + +int +bootenv_write(FILE* fp, bootenv_t env) +{ +	int rc = 0; +	size_t size = 0; +	char *buf = NULL; + +	if ((fp == NULL) || (env == NULL)) +		return -EINVAL; + +	buf = (char*) calloc(1, BOOTENV_MAXSIZE * sizeof(char)); +	if (buf == NULL) +		return -ENOMEM; + +	rc = fill_output_buffer(env, buf, BOOTENV_MAXSIZE, &size); +	if (rc != 0) +		goto err; + +	if (fwrite(buf, size, 1, fp) != 1) { +		rc = -EIO; +		goto err; +	} + +err: +	if (buf != NULL) +		free(buf); +	return rc; +} + +int +bootenv_size(bootenv_t env, size_t *size) +{ +	int rc = 0; +	char *buf = NULL; + +	if (env == NULL) +		return -EINVAL; + +	buf = (char*) calloc(1, BOOTENV_MAXSIZE * sizeof(char)); +	if (buf == NULL) +		return -ENOMEM; + +	rc = fill_output_buffer(env, buf, BOOTENV_MAXSIZE, size); +	if (rc != 0) +		goto err; + +err: +	if (buf != NULL) +		free(buf); +	return rc; +} + +int +bootenv_write_txt(FILE* fp, bootenv_t env) +{ +	int rc = 0; +	size_t size, wr, i; +	const char **keys = NULL; +	const char *key = NULL; +	const char *val = NULL; + +	if ((fp == NULL) || (env == NULL)) +		return -EINVAL; + +	rc = bootenv_get_key_vector(env, &size, 1, &keys); +	if (rc != 0) +		goto err; + +	for (i = 0; i < size; i++) { +		key = keys[i]; +		rc = bootenv_get(env, key, &val); +		if (rc != 0) +			goto err; + +		wr = fprintf(fp, "%s=%s\n", key, val); +		if (wr != strlen(key) + strlen(val) + 2) { +			rc = -EIO; +			goto err; +		} +	} + +err: +	if (keys != NULL) +		free(keys); +	return rc; +} + +int +bootenv_valid(bootenv_t env) +{ +	/* @FIXME No sanity check implemented. */ +	return 0; +} + +int +bootenv_copy_bootenv(bootenv_t in, bootenv_t *out) +{ +	int rc = 0; +	const char *tmp = NULL; +	const char **keys = NULL; +	size_t vec_size, i; + +	if ((in == NULL) || (out == NULL)) +		return -EINVAL; + +	/* purge output var for sure... */ +	rc = bootenv_destroy(out); +	if (rc != 0) +		return rc; + +	/* create the new map  */ +	rc = bootenv_create(out); +	if (rc != 0) +		goto err; + +	/* get the key list from the input map */ +	rc = bootenv_get_key_vector(in, &vec_size, 0, &keys); +	if (rc != 0) +		goto err; + +	if (vec_size != hashmap_size(in->map)) { +		rc = BOOTENV_ECOPY; +		goto err; +	} + +	/* make a deep copy of the hashmap */ +	for (i = 0; i < vec_size; i++) { +		rc = bootenv_get(in, keys[i], &tmp); +		if (rc != 0) +			goto err; + +		rc = bootenv_set(*out, keys[i], tmp); +		if (rc != 0) +			goto err; +	} + +err: +	if (keys != NULL) +		free(keys); + +	return rc; +} + +/* ------------------------------------------------------------------------- */ + + +int +bootenv_pdd_keep(bootenv_t env_old, bootenv_t env_new, bootenv_t *env_res, +		int *warnings, char *err_buf, size_t err_buf_size) +{ +	bootenv_list_t l_old = NULL; +	bootenv_list_t l_new = NULL; +	const char *pdd_old = NULL; +	const char *pdd_new = NULL; +	const char *tmp = NULL; +	const char **vec_old = NULL; +	const char **vec_new = NULL; +	const char **pdd_up_vec = NULL; +	size_t vec_old_size, vec_new_size, pdd_up_vec_size, i; +	int rc = 0; + +	if ((env_old == NULL) || (env_new == NULL) || (env_res == NULL)) +		return -EINVAL; + +	/* get the pdd strings, e.g.: +	 * pdd_old=a,b,c +	 * pdd_new=a,c,d,e */ +	rc = bootenv_get(env_old, "pdd", &pdd_old); +	if (rc != 0) +		goto err; +	rc = bootenv_get(env_new, "pdd", &pdd_new); +	if (rc != 0) +		goto err; + +	/* put it into a list and then convert it to an vector */ +	rc = bootenv_list_create(&l_old); +	if (rc != 0) +		goto err; +	rc  = bootenv_list_create(&l_new); +	if (rc != 0) +		goto err; + +	rc = bootenv_list_import(l_old, pdd_old); +	if (rc != 0) +		goto err; + +	rc = bootenv_list_import(l_new, pdd_new); +	if (rc != 0) +		goto err; + +	rc = bootenv_list_to_vector(l_old, &vec_old_size, &vec_old); +	if (rc != 0) +		goto err; + +	rc = bootenv_list_to_vector(l_new, &vec_new_size, &vec_new); +	if (rc != 0) +		goto err; + +	rc = bootenv_copy_bootenv(env_new, env_res); +	if (rc != 0) +		goto err; + +	/* calculate the update vector between the old and new pdd */ +	pdd_up_vec = hashmap_get_update_key_vector(vec_old, vec_old_size, +			vec_new, vec_new_size, &pdd_up_vec_size); + +	if (pdd_up_vec == NULL) { +		rc = -ENOMEM; +		goto err; +	} + +	if (pdd_up_vec_size != 0) { +		/* need to warn the user about the unset of +		 * some pdd/bootenv values */ +		*warnings = BOOTENV_WPDD_STRING_DIFFERS; + +		/* remove all entries in the new bootenv load */ +		for (i = 0; i < pdd_up_vec_size; i++) { +			bootenv_unset(*env_res, pdd_up_vec[i]); +		} +	} + +	/* generate the keep array and copy old pdd values to new bootenv */ +	for (i = 0; i < vec_old_size; i++) { +		rc = bootenv_get(env_old, vec_old[i], &tmp); +		if (rc != 0) { +			rc = BOOTENV_EPDDINVAL; +			goto err; +		} +		rc = bootenv_set(*env_res, vec_old[i], tmp); +		if (rc != 0) { +			goto err; +		} +	} +	/* put the old pdd string into the result map */ +	rc = bootenv_set(*env_res, "pdd", pdd_old); +	if (rc != 0) { +		goto err; +	} + + +err: +	if (vec_old != NULL) +		free(vec_old); +	if (vec_new != NULL) +		free(vec_new); +	if (pdd_up_vec != NULL) +		free(pdd_up_vec); + +	bootenv_list_destroy(&l_old); +	bootenv_list_destroy(&l_new); +	return rc; +} + + +int +bootenv_pdd_overwrite(bootenv_t env_old, bootenv_t env_new, +		      bootenv_t *env_res, int *warnings, +		      char *err_buf, size_t err_buf_size) +{ +	if ((env_old == NULL) || (env_new == NULL) || (env_res == NULL)) +		return -EINVAL; + +	return bootenv_copy_bootenv(env_new, env_res); +} + +int +bootenv_pdd_merge(bootenv_t env_old, bootenv_t env_new, bootenv_t *env_res, +		int *warnings, char *err_buf, size_t err_buf_size) +{ +	if ((env_old == NULL) || (env_new == NULL) || (env_res == NULL)) +		return -EINVAL; + +	snprintf(err_buf, err_buf_size, "The PDD merge operation is not " +			"implemented. Contact: <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/src/libbootenv/hashmap.c b/ubi-utils/src/libbootenv/hashmap.c new file mode 100644 index 0000000..250f71f --- /dev/null +++ b/ubi-utils/src/libbootenv/hashmap.c @@ -0,0 +1,412 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Oliver Lohmann + */ + +#include <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("[%d]: ", i); +			print_all(map->data[i]); +		} +	} + +	return 0; +} + +static const char** +sort_key_vector(const char** a, size_t size) +{ +	/* uses bubblesort */ +	size_t i, j; +	const char* tmp; + +	if (size <= 0) +		return a; + +	for (i = size - 1; i > 0; i--) { +		for (j = 0; j < i; j++) { +			if (strcmp(a[j], a[j+1]) > 0) { +				tmp  = a[j]; +				a[j] = a[j+1]; +				a[j+1] = tmp; +			} +		} +	} +	return a; +} + +const char** +hashmap_get_key_vector(hashmap_t map, size_t* size, int sort) +{ +	const char** res; +	size_t i, j; +	*size = map->entries; + +	res = (const char**) malloc(*size * sizeof(char*)); +	if (res == NULL) +		return NULL; + +	j = 0; +	for(i=0; i < map->maxsize; i++) { +		keys_to_array(map->data[i], res, &j); +	} + +	if (sort) +		res = sort_key_vector(res, *size); + +	return res; +} + +int +hashmap_key_is_in_vector(const char** vec, size_t size, const char* key) +{ +	size_t i; +	for (i = 0; i < size; i++) { +		if (strcmp(vec[i], key) == 0) /* found */ +			return 1; +	} + +	return 0; +} + +const char** +hashmap_get_update_key_vector(const char** vec1, size_t vec1_size, +		const char** vec2, size_t vec2_size, size_t* res_size) +{ +	const char** res; +	size_t i, j; + +	*res_size = vec2_size; + +	res = (const char**) malloc(*res_size * sizeof(char*)); +	if (res == NULL) +		return NULL; + +	/* get all keys from vec2 which are not set in vec1 */ +	j = 0; +	for (i = 0; i < vec2_size; i++) { +		if (!hashmap_key_is_in_vector(vec1, vec1_size, vec2[i])) +			res[j++] = vec2[i]; +	} + +	*res_size = j; +	return res; +} diff --git a/ubi-utils/src/libbootenv/hashmap.h b/ubi-utils/src/libbootenv/hashmap.h new file mode 100644 index 0000000..1b13e95 --- /dev/null +++ b/ubi-utils/src/libbootenv/hashmap.h @@ -0,0 +1,49 @@ +#ifndef __HASHMAP_H__ +#define __HASHMAP_H__ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Oliver Lohmann + */ + +#include <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/src/libcrc32/crc32.c b/ubi-utils/src/libcrc32/crc32.c new file mode 100644 index 0000000..666e217 --- /dev/null +++ b/ubi-utils/src/libcrc32/crc32.c @@ -0,0 +1,83 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Thomas Gleixner + */ + +/* + * CRC32 functions + * + * Can be compiled as seperate object, but is included into the ipl source + * so gcc can inline the functions. We optimize for size so the omission of + * the function frame is helpful. + * + */ + +#include <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/src/liberror/error.c b/ubi-utils/src/liberror/error.c new file mode 100644 index 0000000..c8c623c --- /dev/null +++ b/ubi-utils/src/liberror/error.c @@ -0,0 +1,177 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <stdio.h> +#include <stdarg.h> +#include <syslog.h> +#include <stdlib.h> +#include <sys/errno.h> +#include <string.h> +#include "error.h" + +#define MAXLINE 4096 + +static FILE *logfp = NULL; + +static void err_doit(int, int, const char *, va_list); + +int +read_procfile(FILE *fp_out, const char *procfile) +{ +	FILE *fp; + +	fp = fopen(procfile, "r"); +	if (!fp) +		return -ENOENT; + +	while(!feof(fp)) { +		int c = fgetc(fp); + +		if (c == EOF) +			return 0; + +		if (putc(c, fp_out) == EOF) +			return -EIO; + +		if (ferror(fp)) +			return -EIO; +	} +	return fclose(fp); +} + +void +error_initlog(const char *logfile) +{ +	logfp = fopen(logfile, "a+"); +	read_procfile(logfp, "/proc/cpuinfo"); +} + +void +info_msg(const char *fmt, ...) +{ +	FILE* fpout; +	char buf[MAXLINE + 1]; +	va_list	ap; +	int n; + +	fpout = stdout; + +	va_start(ap, fmt); +	vsnprintf(buf, MAXLINE, fmt, ap); +	n = strlen(buf); +	strcat(buf, "\n"); + +	fputs(buf, fpout); +	fflush(fpout); +	if (fpout != stdout) +		fclose(fpout); + +	va_end(ap); +	return; +} + +void +__err_ret(const char *fmt, ...) +{ +	va_list		ap; + +	va_start(ap, fmt); +	err_doit(1, LOG_INFO, fmt, ap); +	va_end(ap); +	return; +} + +void +__err_sys(const char *fmt, ...) +{ +	va_list		ap; + +	va_start(ap, fmt); +	err_doit(1, LOG_ERR, fmt, ap); +	va_end(ap); +	exit(EXIT_FAILURE); +} + + +void +__err_msg(const char *fmt, ...) +{ +	va_list	ap; + +	va_start(ap, fmt); +	err_doit(0, LOG_INFO, fmt, ap); +	va_end(ap); + +	return; +} + +void +__err_quit(const char *fmt, ...) +{ +	va_list		ap; + +	va_start(ap, fmt); +	err_doit(0, LOG_ERR, fmt, ap); +	va_end(ap); +	exit(EXIT_FAILURE); +} + +void +__err_dump(const char *fmt, ...) +{ +	va_list		ap; + +	va_start(ap, fmt); +	err_doit(1, LOG_ERR, fmt, ap); +	va_end(ap); +	abort();		/* dump core and terminate */ +	exit(EXIT_FAILURE);	/* shouldn't get here */ +} + + +static void +err_doit(int errnoflag, int level __attribute__((unused)), +	 const char *fmt, va_list ap) +{ +	FILE* fpout; +	int errno_save, n; +	char buf[MAXLINE + 1]; +	fpout = stderr; + +	errno_save = errno; /* value caller might want printed */ + +	vsnprintf(buf, MAXLINE, fmt, ap); /* safe */ + +	n = strlen(buf); + +	if (errnoflag) +		snprintf(buf + n, MAXLINE - n, ": %s", strerror(errno_save)); +	strcat(buf, "\n"); + +	if (logfp) { +		fputs(buf, logfp); +		fflush(logfp); +	} + +	fputs(buf, fpout); +	fflush(fpout); +	if (fpout != stderr) +		fclose(fpout); + +	return; +} diff --git a/ubi-utils/src/liblist/list.c b/ubi-utils/src/liblist/list.c new file mode 100644 index 0000000..6eb716b --- /dev/null +++ b/ubi-utils/src/liblist/list.c @@ -0,0 +1,149 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Oliver Lohmann + */ + +#include <stdlib.h> +#include <assert.h> +#include <stdio.h> + +#include "list.h" + +list_t +mk_empty(void) +{ +	return (list_t) NULL; +} + +int +is_empty(list_t l) +{ +	return l == NULL; +} + +info_t +head(list_t l) +{ +	assert(!is_empty(l)); +	return l->info; +} + +list_t +tail(list_t l) +{ +	assert(!is_empty(l)); +	return l->next; +} + +list_t +remove_head(list_t l) +{ +	list_t res; +	assert(!is_empty(l)); + +	res = l->next; +	free(l); +	return res; +} + +list_t +cons(info_t e, list_t l) +{ +	list_t res = malloc(sizeof(*l)); +	if (!res) +		return NULL; +	res->info = e; +	res->next = l; + +	return res; +} + +list_t +prepend_elem(info_t e, list_t l) +{ +	return cons(e,l); +} + +list_t +append_elem(info_t e, list_t l) +{ +	if (is_empty(l)) { +		return cons(e,l); +	} +	l->next = append_elem(e, l->next); + +	return l; +} + +list_t +insert_sorted(cmp_func_t cmp, info_t e, list_t l) +{ +	if (is_empty(l)) +		return cons(e, l); + +	switch (cmp(e, l->info)) { +	case -1: +	case  0: +		return l; +		break; +	case  1: +		l->next = insert_sorted(cmp, e, l); +		break; +	default: +		break; +	} + +	/* never reached */ +	return NULL; +} + +list_t +remove_all(free_func_t free_func, list_t l) +{ +	if (is_empty(l)) +		return l; +	list_t lnext = l->next; + +	if (free_func && l->info) { +		free_func(&(l->info)); +	} +	free(l); + +	return remove_all(free_func, lnext); +} + + +info_t +is_in(cmp_func_t cmp, info_t e, list_t l) +{ +	return +	(is_empty(l)) +	? NULL +	: (cmp(e, l->info)) == 0 ? l->info : is_in(cmp, e, l->next); +} + + +void +apply(process_func_t process_func, list_t l) +{ +	list_t ptr; +	void *i; +	foreach(i, ptr, l) { +		process_func(i); +	} +} diff --git a/ubi-utils/src/libpeb/peb.c b/ubi-utils/src/libpeb/peb.c new file mode 100644 index 0000000..08b770f --- /dev/null +++ b/ubi-utils/src/libpeb/peb.c @@ -0,0 +1,116 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <stdlib.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <assert.h> + +#include "peb.h" + +int +peb_cmp(peb_t eb_1, peb_t eb_2) +{ +	assert(eb_1); +	assert(eb_2); + +	return eb_1->num == eb_2->num ? 0 +		: eb_1->num > eb_2->num ? 1 : -1; +} + +int +peb_new(uint32_t eb_num, uint32_t eb_size, peb_t *peb) +{ +	int rc = 0; + +	peb_t res = (peb_t) malloc(sizeof(struct peb)); +	if (!res) { +		rc = -ENOMEM; +		goto err; +	} + +	res->num  = eb_num; +	res->size = eb_size; +	res->data = (uint8_t*) malloc(res->size * sizeof(uint8_t)); +	if (!res->data) { +		rc = -ENOMEM; +		goto err; +	} +	memset(res->data, 0xff, res->size); + +	*peb = res; +	return 0; +err: +	if (res) { +		if (res->data) +			free(res->data); +		free(res); +	} +	*peb = NULL; +	return rc; +} + +int +peb_fill(peb_t peb, uint8_t* buf, size_t buf_size) +{ +	if (!peb) +		return -EINVAL; + +	if (buf_size > peb->size) +		return -EINVAL; + +	memcpy(peb->data, buf, buf_size); +	return 0; +} + +int +peb_write(FILE* fp_out, peb_t peb) +{ +	size_t written = 0; + +	if (peb == NULL) +		return -EINVAL; + +	written = fwrite(peb->data, 1, peb->size, fp_out); + +	if (written != peb->size) +		return -EIO; + +	return 0; +} + +int +peb_free(peb_t* peb) +{ +	peb_t tmp = *peb; +	if (tmp) { +		if (tmp->data) +			free(tmp->data); +		free(tmp); +	} +	*peb = NULL; + +	return 0; +} + +void peb_dump(FILE* fp_out, peb_t peb) +{ +	fprintf(fp_out, "num: %08d\tsize: 0x%08x\n", peb->num, peb->size); +} diff --git a/ubi-utils/src/libpfi/pfi.c b/ubi-utils/src/libpfi/pfi.c new file mode 100644 index 0000000..c8d5ee4 --- /dev/null +++ b/ubi-utils/src/libpfi/pfi.c @@ -0,0 +1,461 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * @file pfi.c + * + * @author Oliver Lohmann + *	   Andreas Arnez + *	   Joern Engel + *	   Frank Haverkamp + * + * @brief libpfi holds all code to create and process pfi files. + * + * <oliloh@de.ibm.com> Wed Feb	8 11:38:22 CET 2006: Initial creation. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <syslog.h> +#include <stdarg.h> +#include <errno.h> + +#include "pfi.h" + +#define PFI_MAGIC     "PFI!\n" +#define PFI_DATA      "DATA\n" /* The same size as PFI_MAGIC */ +#define PFI_MAGIC_LEN 5 + +static const char copyright [] __attribute__((unused)) = +	"Copyright (c) International Business Machines Corp., 2006"; + +enum key_id { +	/* version 1 */ +	key_version,	      /* must be index position 0! */ +	key_mode, +	key_size, +	key_crc, +	key_label, +	key_flags, +	key_ubi_ids, +	key_ubi_size, +	key_ubi_type, +	key_ubi_names, +	key_ubi_alignment, +	key_raw_starts, +	key_raw_total_size, +	num_keys, +}; + +struct pfi_header { +	char defined[num_keys];	 /* reserve all possible keys even if +				    version does not require this. */ +	int mode_no;		 /* current mode no. -> can only increase */ +	union { +		char *str; +		uint32_t num; +	} value[num_keys]; +}; + + +#define PFI_MANDATORY	    0x0001 +#define PFI_STRING	    0x0002 +#define PFI_LISTVALUE	    0x0004	/* comma seperated list of nums */ +#define PFI_MANDATORY_UBI   0x0008 +#define PFI_MANDATORY_RAW   0x0010 + +struct key_descriptor { +	enum key_id id; +	const char *name; +	uint32_t flags; +}; + +static const struct key_descriptor key_desc_v1[] = { +	{ key_version, "version", PFI_MANDATORY }, +	{ key_mode, "mode", PFI_MANDATORY | PFI_STRING }, +	{ key_size, "size", PFI_MANDATORY }, +	{ key_crc, "crc", PFI_MANDATORY }, +	{ key_label, "label", PFI_MANDATORY | PFI_STRING }, +	{ key_flags, "flags", PFI_MANDATORY }, +	{ key_ubi_ids, "ubi_ids", PFI_MANDATORY_UBI | PFI_STRING }, +	{ key_ubi_size, "ubi_size", PFI_MANDATORY_UBI }, +	{ key_ubi_type, "ubi_type", PFI_MANDATORY_UBI | PFI_STRING }, +	{ key_ubi_names, "ubi_names", PFI_MANDATORY_UBI | PFI_STRING }, +	{ key_ubi_alignment, "ubi_alignment", PFI_MANDATORY_UBI }, +	{ key_raw_starts, "raw_starts", PFI_MANDATORY_RAW | PFI_STRING }, +	{ key_raw_total_size, "raw_total_size", PFI_MANDATORY_RAW }, +}; + +static const struct key_descriptor *key_descriptors[] = { +	NULL, +	key_desc_v1,					   /* version 1 */ +}; + +static const int key_descriptors_max[] = { +	0,						   /* version 0 */ +	sizeof(key_desc_v1)/sizeof(struct key_descriptor), /* version 1 */ +}; + +static const char* modes[] = {"raw", "ubi", NULL}; /* order isn't arbitrary! */ + +/* latest version contains all possible keys */ +static const struct key_descriptor *key_desc = key_desc_v1; + +#define PFI_IS_UBI(mode) \ +	(((mode) != NULL) && (strcmp("ubi", (mode)) == 0)) + +#define PFI_IS_RAW(mode) \ +	(((mode) != NULL) && (strcmp("raw", (mode)) == 0)) + +/** + * @return	 <0	On Error. + *		>=0	Mode no. + */ +static int +get_mode_no(const char* mode) +{ +	const char* ptr = modes[0]; +	int i = 0; +	while (ptr != NULL) { +		if(strcmp(ptr, mode) == 0) { +			return i; +		} +		ptr++; +		i++; +	} + +	return -1; +} + +static int +find_key_by_name (const char *name) +{ +	int i; + +	for (i = 0; i < num_keys; i++) { +		if (strcmp(name, key_desc[i].name) == 0) +			return i; +	} +	return -1; +} + +static int +check_valid (pfi_header head) +{ +	int i; +	int max_keys; +	uint32_t version; +	const char *mode; +	const struct key_descriptor *desc; +	uint32_t to_check = PFI_MANDATORY; + +	/* +	 * For the validity check the list of possible keys depends on +	 * the version of the PFI file used. +	 */ +	version = head->value[key_version].num; +	if (version > PFI_HDRVERSION) +		return PFI_ENOHEADER; + +	max_keys = key_descriptors_max[version]; +	desc = key_descriptors[version]; + +	if (!desc) +		return PFI_ENOVERSION; + +	mode = head->value[key_mode].str; +	if (PFI_IS_UBI(mode)) { +		to_check |= PFI_MANDATORY_UBI; +	} +	else if (PFI_IS_RAW(mode)) { +		to_check |= PFI_MANDATORY_RAW; +	} +	else { /* neither UBI nor RAW == ERR */ +		return PFI_EINSUFF; +	} + +	for (i = 0; i < max_keys; i++) { +		if ((desc[i].flags & to_check) && !head->defined[i]) { +			fprintf(stderr, "libpfi: %s missing\n", desc[i].name); +			return PFI_EINSUFF; +		} +	} + +	return 0; +} + +int pfi_header_init (pfi_header *head) +{ +	int i; +	pfi_header self = (pfi_header) malloc(sizeof(*self)); + +	*head = self; +	if (self == NULL) +		return PFI_ENOMEM; + +	/* initialize maximum number of possible keys */ +	for (i = 0; i < num_keys; i++) { +		memset(self, 0, sizeof(*self)); +		self->defined[i] = 0; +	} + +	return 0; +} + +int pfi_header_destroy (pfi_header *head) +{ +	int i; +	pfi_header self = *head; + +	for (i = 0; i < num_keys; i++) { +		if (self->defined[i] && (key_desc[i].flags & PFI_STRING) && +		    self->value[i].str) { +			free(self->value[i].str); +		} +	} +	free(*head); +	*head = NULL; +	return 0; +} + +int pfi_header_setnumber (pfi_header head, +			   const char *key, uint32_t value) +{ +	int key_id = find_key_by_name(key); + +	if (key_id < 0) +		return PFI_EUNDEF; + +	if (key_desc[key_id].flags & PFI_STRING) +		return PFI_EBADTYPE; + +	head->value[key_id].num = value; +	head->defined[key_id] = 1; +	return 0; +} + +int pfi_header_setvalue (pfi_header head, +			  const char *key, const char *value) +{ +	int key_id = find_key_by_name(key); + +	if ((uint32_t)value == (uint32_t)NULL) +		return PFI_EINSUFF; + +	if ((key_id < 0) || (key_id >= num_keys)) +		return PFI_EUNDEF; + +	if (key_desc[key_id].flags & PFI_STRING) { +		/* +		 * The value is a string. Copy to a newly allocated +		 * buffer. Delete the old value, if already set. +		 */ +		size_t len = strlen(value) + 1; +		char *old_str = NULL; +		char *str; + +		old_str = head->value[key_id].str; +		if (old_str != NULL) +			free(old_str); + +		str = head->value[key_id].str = (char *) malloc(len); +		if (str == NULL) +			return PFI_ENOMEM; + +		strcpy(str, value); +	} else { +		int len; +		int ret; +		/* FIXME: here we assume that the value is always +		   given in hex and starts with '0x'. */ +		ret = sscanf(value, "0x%x%n", &head->value[key_id].num, &len); +		if (ret < 1 || value[len] != '\0') +			return PFI_EBADTYPE; +	} +	head->defined[key_id] = 1; +	return 0; +} + +int pfi_header_getnumber (pfi_header head, +			   const char *key, uint32_t *value) +{ +	int key_id = find_key_by_name(key); + +	if (key_id < 0) +		return PFI_EUNDEF; + +	if (key_desc[key_id].flags & PFI_STRING) +		return PFI_EBADTYPE; + +	if (!head->defined[key_id]) +		return PFI_EUNDEF; + +	*value = head->value[key_id].num; +	return 0; +} + +int pfi_header_getstring (pfi_header head, +			   const char *key, char *value, size_t size) +{ +	int key_id = find_key_by_name(key); + +	if (key_id < 0) +		return PFI_EUNDEF; + +	if (!(key_desc[key_id].flags & PFI_STRING)) +		return PFI_EBADTYPE; + +	if (!head->defined[key_id]) +		return PFI_EUNDEF; + +	strncpy(value, head->value[key_id].str, size-1); +	value[size-1] = '\0'; +	return 0; +} + +int pfi_header_write (FILE *out, pfi_header head) +{ +	int i; +	int ret; + +	pfi_header_setnumber(head, "version", PFI_HDRVERSION); + +	if ((ret = check_valid(head)) != 0) +		return ret; + +	/* OK.	Now write the header. */ + +	ret = fwrite(PFI_MAGIC, 1, PFI_MAGIC_LEN, out); +	if (ret < PFI_MAGIC_LEN) +		return ret; + + +	for (i = 0; i < num_keys; i++) { +		if (!head->defined[i]) +			continue; + +		ret = fprintf(out, "%s=", key_desc[i].name); +		if (ret < 0) +			return PFI_EFILE; + +		if (key_desc[i].flags & PFI_STRING) { +			ret = fprintf(out, "%s", head->value[i].str); +			if (ret < 0) +				return PFI_EFILE; +		} else { +			ret = fprintf(out, "0x%8x", head->value[i].num); +			if (ret < 0) +				return PFI_EFILE; + +		} +		ret = fprintf(out, "\n"); +		if (ret < 0) +			return PFI_EFILE; +	} +	ret = fprintf(out, "\n"); +	if (ret < 0) +		return PFI_EFILE; + +	ret = fflush(out); +	if (ret != 0) +		return PFI_EFILE; + +	return 0; +} + +int pfi_header_read (FILE *in, pfi_header head) +{ +	char magic[PFI_MAGIC_LEN]; +	char mode[PFI_KEYWORD_LEN]; +	char buf[256]; + +	if (PFI_MAGIC_LEN != fread(magic, 1, PFI_MAGIC_LEN, in)) +		return PFI_EFILE; +	if (memcmp(magic, PFI_MAGIC, PFI_MAGIC_LEN) != 0)  { +		if (memcmp(magic, PFI_DATA, PFI_MAGIC_LEN) == 0) { +			return PFI_DATA_START; +		} +		return PFI_ENOHEADER; +	} + +	while (fgets(buf, sizeof(buf), in) != NULL && buf[0] != '\n') { +		char *value; +		char *end; +		value = strchr(buf, '='); +		if (value == NULL) +			return PFI_ENOHEADER; + +		*value = '\0'; +		value++; +		end = strchr(value, '\n'); +		if (end) +		       *end = '\0'; + +		if (pfi_header_setvalue(head, buf, value)) +			return PFI_ENOHEADER; +	} + +	if (check_valid(head) != 0) +		return PFI_ENOHEADER; + +	/* set current mode no. in head */ +	pfi_header_getstring(head, "mode", mode, PFI_KEYWORD_LEN); +	if (head->mode_no > get_mode_no(mode)) { +		return PFI_EMODE; +	} +	head->mode_no = get_mode_no(mode); +	return 0; +} + +int pfi_header_dump (FILE *out, pfi_header head __attribute__((__unused__))) +{ +	fprintf(out, "Sorry not implemented yet. Write mail to " +		"Andreas Arnez and complain!\n"); +	return 0; +} + +int pfi_read (FILE *in, pfi_read_func func, void *priv_data) +{ +	int rc; +	pfi_header header; + +	rc = pfi_header_init (&header); +	if (0 != rc) +		return rc; +	if (!func) +		return PFI_EINVAL; + +	while ((0 == rc) && !feof(in)) { +		/* +		 * Read header and check consistency of the fields. +		 */ +		rc = pfi_header_read( in, header ); +		if (0 != rc) +			break; +		if (func) { +			rc = func(in, header, priv_data); +			if (rc != 0) +				break; +		} +	} + +	pfi_header_destroy(&header); +	return rc; +} diff --git a/ubi-utils/src/libpfiflash/pfiflash.c b/ubi-utils/src/libpfiflash/pfiflash.c new file mode 100644 index 0000000..0859a22 --- /dev/null +++ b/ubi-utils/src/libpfiflash/pfiflash.c @@ -0,0 +1,617 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/** + * @file pfiflash.c + * + * @author Oliver Lohmann <oliloh@de.ibm.com> + * + * @brief This library is provides an interface to the pfiflash utility. + * + * <oliloh@de.ibm.com> Wed Mar 15 11:39:19 CET 2006 Initial creation. + * + * @TODO Comare data before writing it. This implies that the volume + * parameters are compared first: size, alignment, name, type, ..., + * this is the same, compare the data. Volume deletion is deffered + * until the difference has been found out. + */ + +#include <stdio.h> +#include <errno.h> +#include <fcntl.h> +#define __USE_GNU +#include <string.h> + +#include <libubi.h> +#include <pfiflash.h> + +#include <mtd/ubi-user.h>	/* FIXME Is this ok here!!?? */ + +#include "ubimirror.h" +#include "error.h" +#include "reader.h" +#include "example_ubi.h" +#include "bootenv.h" + +static const char copyright [] __attribute__((unused)) = +	"Copyright (c) International Business Machines Corp., 2006"; + +#define EBUF(fmt...) do {			\ +		snprintf(err_buf, err_buf_size, fmt);	\ +	} while (0) + +static pdd_func_t pdd_funcs[PDD_HANDLING_NUM]  = +	{ +		&bootenv_pdd_keep, +		&bootenv_pdd_merge, +		&bootenv_pdd_overwrite +	}; +/**< An array of PDD function pointers indexed by the algorithm. */ + + +typedef enum ubi_update_process_t { +	UBI_REMOVE = 0, +	UBI_WRITE, +} ubi_update_process_t; + +static int +skip_raw_sections(FILE* pfi, list_t pfi_raws, +		  char* err_buf, size_t err_buf_size) +{ +	int rc = 0; + +	void *i; +	list_t ptr; +	size_t j, skip_size; + +	if (is_empty(pfi_raws)) +		return 0; + +	foreach(i, ptr, pfi_raws) { +		skip_size = ((pfi_raw_t)i)->data_size; +		for(j = 0;  j < skip_size; j++) { +			fgetc(pfi); +			if (ferror(pfi)) { +				EBUF("Cannot skip raw section in PFI."); +				rc = -EIO; +				goto err; +			} +		} +	} + err: +	return rc; +} + +/** + * @brief Wraps the ubi_mkvol functions and implements a hook for the bootenv + *	  update. + * @param devno UBI device number. + * @param s Current seqnum. + * @param u Information about the UBI volume from the PFI. + * @param err_buf An error buffer. + * @param err_buf_size The size of the error buffer. + * @return 0 On Sucess. + * @return else Error. + */ +static int +my_ubi_mkvol(int devno, int s, pfi_ubi_t u, char *err_buf, size_t err_buf_size) +{ +	int rc = 0; +	int type; +	ubi_lib_t ulib = NULL; + +	log_msg("%s(vol_id=%d, size=%d, data_size=%d, type=%d, " +		"alig=%d, nlen=%d, name=%s)", __func__, +		u->ids[s], u->size, u->data_size, u->type, u->alignment, +		strnlen(u->names[s], PFI_UBI_VOL_NAME_LEN), u->names[s]); + +	rc = ubi_open(&ulib); +	if (rc != 0) { +		goto err; +	} + +	switch (u->type) { +	case pfi_ubi_static: +		type = UBI_STATIC_VOLUME; break; +	case pfi_ubi_dynamic: +		type = UBI_DYNAMIC_VOLUME; break; +	default: +		type = UBI_DYNAMIC_VOLUME; +	} + +	rc = ubi_mkvol(ulib, devno, u->ids[s], type, u->size, u->alignment, +		       u->names[s]); +	if (rc != 0) { +		EBUF("Cannot create volume: %d", u->ids[s]); +		goto err; +	} + + err: +	if (ulib != NULL) +		ubi_close(&ulib); +	return rc; +} + +/** + * @brief A wrapper around the UBI library function ubi_rmvol. + * @param devno UBI device number. + * @param s Current seqnum. + * @param u Information about the UBI volume from the PFI. + * @param err_buf An error buffer. + * @param err_buf_size The size of the error buffer. + * + * If the volume does not exist, the function will return success. + */ +static int +my_ubi_rmvol(int devno, uint32_t id, char *err_buf, size_t err_buf_size) +{ +	int rc = 0; +	ubi_lib_t ulib = NULL; +	int fd; + +	log_msg("%s(id=%d)", __func__, id); + +	rc = ubi_open(&ulib); +	if (rc != 0) +		goto err; + +	/** +	 * Truncate if it exist or not. +	 */ +	fd = ubi_vol_open(ulib, devno, id, O_RDWR); +	if (fd == -1) +		return 0;	/* not existent, return */ + +	rc = ubi_vol_update(fd, 0); +	if (rc < 0) { +		fprintf(stderr, "update failed rc=%d errno=%d\n", rc, errno); +		ubi_vol_close(fd); +		goto err;	/* if EBUSY than empty device, continue */ +	} +	ubi_vol_close(fd); + +	rc = ubi_rmvol(ulib, devno, id); +	if (rc != 0) { +		/* @TODO Define a ubi_rmvol return value which says +		 * sth like EUBI_NOSUCHDEV. In this case, a failed +		 * operation is acceptable. Everything else has to be +		 * classified as real error. But talk to Andreas Arnez +		 * before defining something odd... +		 */ +		/* if ((errno == EINVAL) || (errno == ENODEV)) +		   return 0; */ /* currently it is EINVAL or ENODEV */ + +		dbg_msg("Remove UBI volume %d returned with error: %d " +			"errno=%d", id, rc, errno); +		goto err; +	} + err: +	if (ulib != NULL) +		ubi_close(&ulib); +	return rc; +} + +static int +read_bootenv_volume(int devno, uint32_t id, bootenv_t bootenv_old, +		    char *err_buf, size_t err_buf_size) +{ +	int rc = 0; +	ubi_lib_t ulib = NULL; +	FILE* fp_in = NULL; + +	rc = ubi_open(&ulib); +	if (rc) +		return rc; + +	fp_in = ubi_vol_fopen_read(ulib, devno, id); +	if (!fp_in) { +		EBUF("Cannot open bootenv volume"); +		rc = -EIO; +		goto err; +	} + +	log_msg("%s reading old bootenvs", __func__); + +	/* Save old bootenvs for reference */ +	rc = bootenv_read(fp_in, bootenv_old, BOOTENV_MAXSIZE); +	if (rc) +		EBUF("Cannot read bootenv_old"); + err: +	if (fp_in) +		fclose(fp_in); +	if (ulib) +		ubi_close(&ulib); +	return rc; +} + +static int +write_bootenv_volume(int devno, uint32_t id, bootenv_t bootenv_old, +		     pdd_func_t pdd_f, +		     FILE* fp_in, /* new pdd data contained in pfi */ +		     size_t fp_in_size,	/* data size of new pdd data in pfi */ +		     char *err_buf, size_t err_buf_size) +{ +	int rc = 0; +	int warnings = 0; +	ubi_lib_t ulib = NULL; +	bootenv_t bootenv_new = NULL; +	bootenv_t bootenv_res = NULL; +	size_t update_size = 0; +	FILE *fp_out = NULL; + +	log_msg("%s(id=%d, fp_in=%p)", __func__, id, fp_in); + +	/* Workflow: +	 * 1. Apply PDD operation and get the size of the returning +	 *    bootenv_res section. Without the correct size it wouldn't +	 *    be possible to call UBI update vol. +	 * 2. Call UBI update vol +	 * 3. Get FILE* to vol dev +	 * 4. Write to FILE* +	 */ + +	rc = ubi_open(&ulib); +	if (rc != 0) { +		goto err; +	} + +	rc = bootenv_create(&bootenv_new); +	if (rc != 0) +		goto err; +	rc = bootenv_create(&bootenv_res); +	if (rc != 0) +		goto err; + +	rc = bootenv_read(fp_in, bootenv_new, fp_in_size); +	if (rc != 0) +		goto err; + +	rc = pdd_f(bootenv_old, bootenv_new, &bootenv_res, &warnings, +		   err_buf, err_buf_size); +	if (rc != 0) +		goto err; +	if (warnings) { +		/* @TODO Do sth with the warning */ +		dbg_msg("A warning in the PDD operation occured: %d", +			warnings); +	} +	log_msg("... (2)"); + +	rc = bootenv_size(bootenv_res, &update_size); +	if (rc != 0) +		goto err; + +	fp_out = ubi_vol_fopen_update(ulib, devno, id, update_size); +	if (fp_out == NULL) +		goto err; + +	rc = bootenv_write(fp_out, bootenv_res); +	if (rc != 0) { +		EBUF("Write operation on ubi%d_%d failed.", devno, id); +		rc = -EIO; +		goto err; +	} + + err: +	if (ulib != NULL) +		ubi_close(&ulib); +	if (bootenv_new != NULL) +		bootenv_destroy(&bootenv_new); +	if (bootenv_res != NULL) +		bootenv_destroy(&bootenv_res); +	if (fp_out) +		fclose(fp_out); +	return rc; +} + +static int +write_normal_volume(int devno, uint32_t id, size_t update_size, FILE* fp_in, +		    char *err_buf, size_t err_buf_size) +{ +	int rc = 0; +	ubi_lib_t ulib = NULL; +	FILE* fp_out = NULL; +	int c; +	size_t i; + +	log_msg("%s(id=%d, update_size=%d fp_in=%p)", +		__func__, id, update_size, fp_in); + +	rc = ubi_open(&ulib); +	if (rc) +		return rc; + +	fp_out = ubi_vol_fopen_update(ulib, devno, id, update_size); +	if (fp_out == NULL) { +		rc = -1; +		goto err; +	} + +	log_msg("starting the update ... "); /* FIXME DBG */ +	for (i = 0; i < update_size; i++) { +		c = getc(fp_in); +		if (c == EOF && ferror(fp_in)) { +			rc = -EIO; +			goto err; +		} +		if (putc(c, fp_out) == EOF) { +			rc = -EIO; +			goto err; +		} +		/*  FIXME DBG */ +		/* if ((i & 0xFFF) == 0xFFF) log_msg("."); */ +	} +	/* log_msg("\n"); */		/*  FIXME DBG */ + err: +	if (fp_out) +		fclose(fp_out); +	if (ulib) +		ubi_close(&ulib); +	return rc; +} + + +/** + * @brief ... + * @precondition	The PFI file contains at least one ubi_id entry. + *			This is assured by the PFI read process. + * @postcondition	The used seqnum number is set in the UBI PFI + *			header list. + *			The UBI volumes specified by seqnum are processed. + */ +static int +process_ubi_volumes(FILE* pfi, int seqnum, list_t pfi_ubis, +		    bootenv_t bootenv_old, pdd_func_t pdd_f, +		    ubi_update_process_t ubi_update_process, +		    char *err_buf, size_t err_buf_size) +{ +	int rc = 0; +	pfi_ubi_t u; +	list_t ptr; + +	foreach(u, ptr, pfi_ubis) { +		int s = seqnum; +		if (seqnum > (u->ids_size - 1)) { +			s = 0; /* per default use the first */ +		} +		u->curr_seqnum = s; + +		switch (ubi_update_process) { +		case UBI_REMOVE: +			if ((u->ids[s] == EXAMPLE_BOOTENV_VOL_ID_1) || +			    (u->ids[s] == EXAMPLE_BOOTENV_VOL_ID_2)) { +				rc =read_bootenv_volume(EXAMPLE_UBI_DEVICE, +							u->ids[s], +							bootenv_old, err_buf, +							err_buf_size); +				if (rc != 0) +					goto err; +				} +			rc = my_ubi_rmvol(EXAMPLE_UBI_DEVICE,  u->ids[s], +					  err_buf, err_buf_size); +			if (rc != 0) +				goto err; +			break; +		case UBI_WRITE: +			rc = my_ubi_mkvol(EXAMPLE_UBI_DEVICE, s, u, +					  err_buf, err_buf_size); +			if (rc != 0) +				goto err; +			if ((u->ids[s] == EXAMPLE_BOOTENV_VOL_ID_1) || +			    (u->ids[s] == EXAMPLE_BOOTENV_VOL_ID_2)) { +				rc = write_bootenv_volume(EXAMPLE_UBI_DEVICE, +							  u->ids[s], +							  bootenv_old, pdd_f, +							  pfi, +							  u->data_size, +							  err_buf, +							  err_buf_size); +			} +			else { +				rc = write_normal_volume(EXAMPLE_UBI_DEVICE, +							 u->ids[s], +							 u->data_size, pfi, +							 err_buf, +							 err_buf_size); +			} +			if (rc != 0) +				goto err; +			break; +		default: +			EBUF("Invoked unknown UBI operation."); +			rc = -1; +			goto err; + +		} +		if (rc != 0) { +			goto err; +		} +	} + err: +	return rc; + +} + +static int +erase_unmapped_ubi_volumes(int devno, list_t pfi_ubis, +			   char *err_buf, size_t err_buf_size) +{ +	int rc = 0; +	list_t ptr; +	pfi_ubi_t u; +	size_t i; +	uint8_t ubi_volumes[PFI_UBI_MAX_VOLUMES]; + +	for (i = 0; i < PFI_UBI_MAX_VOLUMES; i++) { +		ubi_volumes[i] = 1; +	} + +	foreach(u, ptr, pfi_ubis) { +		/* iterate over each vol_id */ +		for(i = 0; i < u->ids_size; i++) { +			if (u->ids[i] > PFI_UBI_MAX_VOLUMES) { +				EBUF("PFI file contains an invalid " +				     "volume id: %d", u->ids[i]); +				goto err; +			} +			/* remove from removal list */ +			ubi_volumes[u->ids[i]] = 0; +		} +	} + +	for (i = 0; i < PFI_UBI_MAX_VOLUMES; i++) { +		if (ubi_volumes[i]) { +			rc = my_ubi_rmvol(devno, i, err_buf, err_buf_size); +			if (rc != 0) +				goto err; +		} +	} + err: +	return rc; +} + +static int +mirror_ubi_volumes(uint32_t devno, list_t pfi_ubis, +		   char *err_buf, size_t err_buf_size) +{ +	int rc = 0; +	list_t ptr; +	uint32_t j; +	pfi_ubi_t i; +	ubi_lib_t  ulib = NULL; + +	log_msg("%s(...)", __func__); + +	rc = ubi_open(&ulib); +	if (rc != 0) +		goto err; + +	/** +	 * Execute all mirror operations on redundant groups. +	 * Create a volume within a redundant group if it does +	 * not exist already (this is a precondition of +	 * ubimirror). +	 */ +	foreach(i, ptr, pfi_ubis) { +		for(j = 0; j < i->ids_size; j++) { +			/* skip self-match */ +			if (i->ids[j] == i->ids[i->curr_seqnum]) +				continue; + +			rc = my_ubi_rmvol(devno, i->ids[j], err_buf, +					  err_buf_size); +			if (rc != 0) +				goto err; + +			rc = my_ubi_mkvol(devno, j, i, err_buf, err_buf_size); +			if (rc != 0) +				goto err; +		} +	} + +	foreach(i, ptr, pfi_ubis) { +		rc = ubimirror(devno, i->curr_seqnum, i->ids, +			       i->ids_size, err_buf, err_buf_size); +		if (rc != 0) +			goto err; +	} + + + err: +	if (ulib != NULL) +		ubi_close(&ulib); +	return rc; +} + +int +pfiflash(FILE* pfi, int complete, int seqnum, pdd_handling_t pdd_handling, +	 char *err_buf, size_t err_buf_size) +{ +	int rc = 0; +	pdd_func_t pdd_f = NULL; + +	if (pfi == NULL) +		return -EINVAL; + +	/** +	 * If the user didnt specify a seqnum we start per default +	 * with the index 0 +	 */ +	int curr_seqnum = seqnum < 0 ? 0 : seqnum; + +	list_t pfi_raws   = mk_empty(); /* list of raw sections from a pfi */ +	list_t pfi_ubis   = mk_empty(); /* list of ubi sections from a pfi */ + +	bootenv_t bootenv; +	rc = bootenv_create(&bootenv); +	if (rc != 0) { +		EBUF("Cannot create bootenv variable"); +	} + +	rc = read_pfi_headers(&pfi_raws, &pfi_ubis, pfi, +			      err_buf, err_buf_size); +	if (rc != 0) { +		EBUF("Cannot read PFI headers."); +		goto err; +	} + +	/* @TODO: If you want to implement an IPL update - start here. */ +	rc = skip_raw_sections(pfi, pfi_raws, err_buf, err_buf_size); +	if (rc != 0) { +		goto err; +	} + +	if (complete) { +		rc = erase_unmapped_ubi_volumes(EXAMPLE_UBI_DEVICE, pfi_ubis, +						err_buf, err_buf_size); +		if (rc != 0) { +			EBUF("Cannot delete unmapped UBI volumes."); +			goto err; +		} +	} + +	if ((pdd_handling >= 0) && (pdd_handling < PDD_HANDLING_NUM)) { +		pdd_f = pdd_funcs[pdd_handling]; +	} +	else { +		EBUF("Used unknown PDD handling algorithm (pdd_handling)"); +	} + +	rc = process_ubi_volumes(pfi, curr_seqnum, pfi_ubis, bootenv, pdd_f, +				 UBI_REMOVE, err_buf, err_buf_size); +	if  (rc != 0) { +		goto err; +	} +	rc = process_ubi_volumes(pfi, curr_seqnum, pfi_ubis, bootenv, pdd_f, +				 UBI_WRITE, err_buf, err_buf_size); +	if  (rc != 0) { +		goto err; +	} +	if (seqnum < 0) { /* mirror redundant pairs */ +		rc = mirror_ubi_volumes(EXAMPLE_UBI_DEVICE, pfi_ubis, +					err_buf, err_buf_size); +		if (rc != 0) +			goto err; +	} + + err: +	pfi_raws = remove_all((free_func_t)&free_pfi_raw, pfi_raws); +	pfi_ubis = remove_all((free_func_t)&free_pfi_ubi, pfi_ubis); +	bootenv_destroy(&bootenv); +	return rc; +} diff --git a/ubi-utils/src/libreader/reader.c b/ubi-utils/src/libreader/reader.c new file mode 100644 index 0000000..c8242df --- /dev/null +++ b/ubi-utils/src/libreader/reader.c @@ -0,0 +1,442 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Oliver Lohmann + * + * Read in PFI (partial flash image) data and store it into internal + * data structures for further processing. Take also care about + * special handling if the data contains PDD (platform description + * data/boot-parameters). + */ + +#include <string.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> + +#include "bootenv.h" +#include "reader.h" + +/* @FIXME hard coded offsets right now - get them from Artem? */ +#define NAND_DEFAULT_VID_HDR_OFF 1984 +#define NOR_DEFAULT_VID_HDR_OFF 64 + +#define EBUF_PFI(fmt...)						\ +	do { int i = snprintf(err_buf, err_buf_size, "%s\n", label);	\ +	     snprintf(err_buf + i, err_buf_size - i, fmt);		\ +	} while (0) + +#define EBUF(fmt...) \ +	do { snprintf(err_buf, err_buf_size, fmt); } while (0) + + +int +read_pdd_data(FILE* fp_pdd, pdd_data_t* pdd_data, +	      char* err_buf, size_t err_buf_size) +{ +	int rc = 0; +	bootenv_t pdd = NULL; +	pdd_data_t res = NULL; +	const char* value; + +	res = (pdd_data_t) malloc(sizeof(struct pdd_data)); +	if (!res) { +		rc = -ENOMEM; +		goto err; +	} +	rc = bootenv_create(&pdd); +	if (rc != 0) { +		goto err; +	} +	rc = bootenv_read_txt(fp_pdd, pdd); +	if (rc != 0) { +		goto err; +	} +	rc = bootenv_get(pdd, "flash_type", &value); +	if (rc != 0) { +		goto err; +	} + +	if (strcmp(value, "NAND") == 0) { +		res->flash_type = NAND_FLASH; +		res->vid_hdr_offset = NAND_DEFAULT_VID_HDR_OFF; +	} +	else if (strcmp(value, "NOR") == 0){ +		res->flash_type = NOR_FLASH; +		res->vid_hdr_offset = NOR_DEFAULT_VID_HDR_OFF; +	} +	else { +		snprintf(err_buf, err_buf_size, +			 "Unkown flash type: %s", value); +		goto err; +	} + +	rc = bootenv_get_num(pdd, "flash_eraseblock_size", +			     &(res->eb_size)); +	if (rc != 0) { +		EBUF("Cannot read 'flash_eraseblock_size' from pdd."); +		goto err; +	} + +	rc = bootenv_get_num(pdd, "flash_size", +			     &(res->flash_size)); +	if (rc != 0) { +		EBUF("Cannot read 'flash_size' from pdd."); +		goto err; +	} + +	goto out; + err: +	if (res) { +		free(res); +		res = NULL; +	} + out: +	bootenv_destroy(&pdd); +	*pdd_data = res; +	return rc; +} + +int +read_pfi_raw(pfi_header pfi_hd, FILE* fp_pfi, pfi_raw_t* pfi_raw, +	     const char* label, char* err_buf, size_t err_buf_size) +{ +	int rc = 0; +	char tmp_str[PFI_KEYWORD_LEN]; +	bootenv_list_t raw_start_list = NULL; +	pfi_raw_t res; + +	res = (pfi_raw_t) malloc(sizeof(struct pfi_raw)); +	if (!res) +		return -ENOMEM; + +	rc = pfi_header_getnumber(pfi_hd, "size", &(res->data_size)); +	if (rc != 0) { +		EBUF_PFI("Cannot read 'size' from PFI."); +		goto err; +	} + +	rc = pfi_header_getstring(pfi_hd, "raw_starts", +				  tmp_str, PFI_KEYWORD_LEN); +	if (rc != 0) { +		EBUF_PFI("Cannot read 'raw_starts' from PFI."); +		goto err; +	} + +	rc = bootenv_list_create(&raw_start_list); +	if (rc != 0) { +		goto err; +	} + +	rc = bootenv_list_import(raw_start_list, tmp_str); +	if (rc != 0) { +		EBUF_PFI("Cannot translate PFI value: %s", tmp_str); +		goto err; +	} + +	rc = bootenv_list_to_num_vector(raw_start_list, +					&(res->starts_size), &(res->starts)); +	if (rc != 0) { +		EBUF_PFI("Cannot create numeric value array: %s", tmp_str); +		goto err; +	} + +	goto out; + + err: +	if (res) { +		free(res); +		res = NULL; +	} + out: +	bootenv_list_destroy(&raw_start_list); +	*pfi_raw = res; +	return rc; +} + +int +read_pfi_ubi(pfi_header pfi_hd, FILE* fp_pfi, pfi_ubi_t* pfi_ubi, +	     const char *label, char* err_buf, size_t err_buf_size) +{ +	int rc = 0; +	const char** tmp_names = NULL; +	char tmp_str[PFI_KEYWORD_LEN]; +	bootenv_list_t ubi_id_list = NULL; +	bootenv_list_t ubi_name_list = NULL; +	pfi_ubi_t res; +	uint32_t i; + +	res = (pfi_ubi_t) calloc(1, sizeof(struct pfi_ubi)); +	if (!res) +		return -ENOMEM; + +	rc = pfi_header_getnumber(pfi_hd, "size", &(res->data_size)); +	if (rc != 0) { +		EBUF_PFI("Cannot read 'size' from PFI."); +		goto err; +	} + +	rc = pfi_header_getstring(pfi_hd, "ubi_ids", tmp_str, PFI_KEYWORD_LEN); +	if (rc != 0) { +		EBUF_PFI("Cannot read 'ubi_ids' from PFI."); +		goto err; +	} + +	rc = bootenv_list_create(&ubi_id_list); +	if (rc != 0) { +		goto err; +	} +	rc = bootenv_list_create(&ubi_name_list); +	if (rc != 0) { +		goto err; +	} + +	rc = bootenv_list_import(ubi_id_list, tmp_str); +	if (rc != 0) { +		EBUF_PFI("Cannot translate PFI value: %s", tmp_str); +		goto err; +	} + +	rc = bootenv_list_to_num_vector(ubi_id_list, &(res->ids_size), +					&(res->ids)); +	if (rc != 0) { +		EBUF_PFI("Cannot create numeric value array: %s", tmp_str); +		goto err; +	} + +	if (res->ids_size == 0) { +		rc = -1; +		EBUF_PFI("Sanity check failed: No ubi_ids specified."); +		goto err; +	} + +	rc = pfi_header_getstring(pfi_hd, "ubi_type", +				  tmp_str, PFI_KEYWORD_LEN); +	if (rc != 0) { +		EBUF_PFI("Cannot read 'ubi_type' from PFI."); +		goto err; +	} +	if (strcmp(tmp_str, "static") == 0) +		res->type = pfi_ubi_static; +	else if (strcmp(tmp_str, "dynamic") == 0) +		res->type = pfi_ubi_dynamic; +	else { +		EBUF_PFI("Unknown ubi_type in PFI."); +		goto err; +	} + +	rc = pfi_header_getnumber(pfi_hd, "ubi_alignment", &(res->alignment)); +	if (rc != 0) { +		EBUF_PFI("Cannot read 'ubi_alignment' from PFI."); +		goto err; +	} + +	rc = pfi_header_getnumber(pfi_hd, "ubi_size", &(res->size)); +	if (rc != 0) { +		EBUF_PFI("Cannot read 'ubi_size' from PFI."); +		goto err; +	} + +	rc = pfi_header_getstring(pfi_hd, "ubi_names", +				  tmp_str, PFI_KEYWORD_LEN); +	if (rc != 0) { +		EBUF_PFI("Cannot read 'ubi_names' from PFI."); +		goto err; +	} + +	rc = bootenv_list_import(ubi_name_list, tmp_str); +	if (rc != 0) { +		EBUF_PFI("Cannot translate PFI value: %s", tmp_str); +		goto err; +	} +	rc = bootenv_list_to_vector(ubi_name_list, &(res->names_size), +				    &(tmp_names)); +	if (rc != 0) { +		EBUF_PFI("Cannot create string array: %s", tmp_str); +		goto err; +	} + +	if (res->names_size != res->ids_size) { +		EBUF_PFI("Sanity check failed: ubi_ids list does not match " +			 "sizeof ubi_names list."); +		rc = -1; +	} + +	/* copy tmp_names to own structure */ +	res->names = (char**) calloc(1, res->names_size * sizeof (char*)); +	if (res->names == NULL) +		goto err; + +	for (i = 0; i < res->names_size; i++) { +		res->names[i] = calloc(PFI_UBI_VOL_NAME_LEN + 1, sizeof(char)); +		if (res->names[i] == NULL) +			goto err; +		strncpy(res->names[i], tmp_names[i], PFI_UBI_VOL_NAME_LEN + 1); +	} + +	goto out; + + err: +	if (res) { +		if (res->names) { +			for (i = 0; i < res->names_size; i++) { +				if (res->names[i]) { +					free(res->names[i]); +				} +			} +			free(res->names); +		} +		if (res->ids) { +			free(res->ids); +		} +		free(res); +		res = NULL; +	} + + out: +	bootenv_list_destroy(&ubi_id_list); +	bootenv_list_destroy(&ubi_name_list); +	if (tmp_names != NULL) +		free(tmp_names); +	*pfi_ubi = res; +	return rc; +} + + +int +free_pdd_data(pdd_data_t* pdd_data) +{ +	if (*pdd_data) { +		free(*pdd_data); +	} +	*pdd_data = NULL; + +	return 0; +} + +int +free_pfi_raw(pfi_raw_t* pfi_raw) +{ +	pfi_raw_t tmp = *pfi_raw; +	if (tmp) { +		if (tmp->starts) +			free(tmp->starts); +		free(tmp); +	} +	*pfi_raw = NULL; + +	return 0; +} + +int +free_pfi_ubi(pfi_ubi_t* pfi_ubi) +{ +	size_t i; +	pfi_ubi_t tmp = *pfi_ubi; +	if (tmp) { +		if (tmp->ids) +			free(tmp->ids); +		if (tmp->names) { +			for (i = 0; i < tmp->names_size; i++) { +				if (tmp->names[i]) { +					free(tmp->names[i]); +				} +			} +			free(tmp->names); +		} +		free(tmp); +	} +	*pfi_ubi = NULL; + +	return 0; +} + + +int +read_pfi_headers(list_t *pfi_raws, list_t *pfi_ubis, FILE* fp_pfi, +		 char* err_buf, size_t err_buf_size) +{ +	int rc = 0; +	char mode[PFI_KEYWORD_LEN]; +	char label[PFI_LABEL_LEN]; + +	*pfi_raws = mk_empty(); pfi_raw_t raw = NULL; +	*pfi_ubis = mk_empty(); pfi_ubi_t ubi = NULL; +	pfi_header pfi_header = NULL; + +	/* read all headers from PFI and store them in lists */ +	rc = pfi_header_init(&pfi_header); +	if (rc != 0) { +		EBUF("Cannot initialize pfi header."); +		goto err; +	} +	while ((rc == 0) && !feof(fp_pfi)) { +		rc = pfi_header_read(fp_pfi, pfi_header); +		if (rc != 0) { +			if (rc == PFI_DATA_START) { +				rc = 0; +				break; /* data section starts, +					  all headers read */ +			} +			else { +				goto err; +			} +		} +		rc = pfi_header_getstring(pfi_header, "label", label, +					  PFI_LABEL_LEN); +		if (rc != 0) { +			EBUF("Cannot read 'label' from PFI."); +			goto err; +		} +		rc = pfi_header_getstring(pfi_header, "mode", mode, +					  PFI_KEYWORD_LEN); +		if (rc != 0) { +			EBUF("Cannot read 'mode' from PFI."); +			goto err; +		} +		if (strcmp(mode, "ubi") == 0) { +			rc = read_pfi_ubi(pfi_header, fp_pfi, &ubi, label, +					  err_buf, err_buf_size); +			if (rc != 0) { +				goto err; +			} +			*pfi_ubis = append_elem(ubi, *pfi_ubis); +		} +		else if (strcmp(mode, "raw") == 0) { +			rc = read_pfi_raw(pfi_header, fp_pfi, &raw, label, +					  err_buf, err_buf_size); +			if (rc != 0) { +				goto err; +			} +			*pfi_raws = append_elem(raw, *pfi_raws); +		} +		else { +			EBUF("Recvieved unknown mode from PFI: %s", mode); +			goto err; +		} +	} +	goto out; + + err: +	*pfi_raws = remove_all((free_func_t)&free_pfi_raw, *pfi_raws); +	*pfi_ubis = remove_all((free_func_t)&free_pfi_ubi, *pfi_ubis); + out: +	pfi_header_destroy(&pfi_header); +	return rc; + +} diff --git a/ubi-utils/src/libubi/libubi.c b/ubi-utils/src/libubi/libubi.c new file mode 100644 index 0000000..9b9a793 --- /dev/null +++ b/ubi-utils/src/libubi/libubi.c @@ -0,0 +1,773 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * UBI (Unsorted Block Images) library. + * + * Author: Artem B. Bityutskiy + *         Oliver Lohmann + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <limits.h> +#include <errno.h> +#include <sys/ioctl.h> +#include <stdint.h> +#include <mtd/ubi-user.h> + +#include "libubi.h" +#include "libubi_int.h" +#include "libubi_sysfs.h" + +/** + * struct ubi_lib - UBI library descriptor. + * + * @ubi		    general UBI information + * + * @sysfs_root	    sysfs root directory + * @ubi_root	    UBI root directory in sysfs + * + * @nlen_max	    full path to the "maximum volume name length" sysfs file + * @version	    full path to the "UBI version" sysfs file + * + * @cdev_path	    path pattern to UBI character devices + * @cdev_path_len   maximum length of the @cdev_path string after substitution + * @udev_path	    path to sysfs directories corresponding to UBI devices + * @wear_path	    path to sysfs file containing UBI wear information + * @vol_count_path  path to sysfs file containing the number of volumes in an + *		    UBI device + * @tot_ebs_path    path to sysfs file containing the total number of + *		    eraseblock on an UBI device + * @avail_ebs_path  path to sysfs file containing the number of unused + *		    eraseblocks on an UBI device, available for new volumes + * @eb_size_path    path to sysfs file containing size of UBI eraseblocks + * @nums_path	    path to sysfs file containing major and minor number of an + *		    UBI device + * @vol_cdev_path   path to UBI volume character devices + * @vdev_path	    path to sysfs directories corresponding to UBI volume + *		    devices + * @vol_nums_path   path to sysfs file containing major and minor number of an + *		    UBI volume device + * @vol_bytes_path  path to sysfs file containing size of an UBI volume device + *		    in bytes + * @vol_ebs_path    path to sysfs file containing the number of eraseblocks in + *		    an UBI volume device + * @vol_type_path   path to sysfs file containing type of an UBI volume + * @vol_name_path   @FIXME: Describe me. + * + * This structure is created and initialized by 'ubi_init()' and is passed to + * all UBI library calls. + */ +struct ubi_lib +{ +	struct ubi_info ubi; + +	char *sysfs_root; +	char *ubi_root; + +	char *nlen_max; +	char *version; +	char *cdev_path; +	int  cdev_path_len; +	char *udev_path; +	char *wear_path; +	char *vol_count_path; +	char *tot_ebs_path; +	char *avail_ebs_path; +	char *eb_size_path; +	char *nums_path; +	int  vol_cdev_path_len; +	char *vol_cdev_path; +	char *vdev_path; +	char *vol_nums_path; +	char *vol_bytes_path; +	char *vol_ebs_path; +	char *vol_type_path; +	char *vol_name_path; +}; + + +/** + * mkpath - compose full path from 2 given components. + * + * @path  first component @name	 second component + * + * Returns the resulting path in case of success and %NULL in case of failure. + * Callers have to take care the resulting path is freed. + */ +static char* +mkpath(const char *path, const char *name) +{ +	char *n; +	int len1 = strlen(path); +	int len2 = strlen(name); + +	n = malloc(len1 + len2 + 2); +	if (!n) +		return NULL; + +	memcpy(n, path, len1); +	if (n[len1 - 1] != '/') +		n[len1++] = '/'; + +	memcpy(n + len1, name, len2 + 1); +	return n; +} + + +static int +get_ubi_info(ubi_lib_t desc, struct ubi_info *ubi) +{ +	int err; +	int n = 1; +	char *path; +	struct stat stat; + +	err = sysfs_read_int(desc->version, (int*) &ubi->version); +	if (err) +		return -1; + +	err = sysfs_read_int(desc->nlen_max, (int*) &ubi->nlen_max); +	if (err) +		return -1; + +	/* Calculate number of UBI devices */ +	do { +		char dir[20]; + +		sprintf(&dir[0], "ubi%d", n); +		path = mkpath(desc->sysfs_root, dir); +		if (!path) +			return ENOMEM; + +		err = lstat(path, &stat); +		if (err == 0) +			n += 1; +		free(path); +	} while (err == 0); + +	if (errno != ENOENT) +		return -1; + +	if (n == 0) { +		ubi_err("no UBI devices found"); +		errno = EINVAL; +		return -1; +	} + +	errno = 0; +	ubi->dev_count = n; +	return 0; +} + +void +ubi_dump_handler(ubi_lib_t desc) +{ +	ubi_lib_t d = desc; +	printf(	"UBI Library Descriptor:\n" +		"ubi_root:	 %s\n" +		"nlen_max:	 %s\n" +		"version:	 %s\n" +		"cdev_path:	 %s\n" +		"udev_path:	 %s\n" +		"wear_path:	 %s\n" +		"vol_count_path: %s\n" +		"tot_ebs_path:	 %s\n" +		"avail_ebs_path: %s\n" +		"eb_size_path:	 %s\n" +		"nums_path:	 %s\n" +		"vol_cdev_path:	 %s\n" +		"vdev_path:	 %s\n" +		"vol_nums_path:	 %s\n" +		"vol_bytes_path: %s\n" +		"vol_ebs_path:	 %s\n" +		"vol_type_path:	 %s\n" +		"vol_name_path:	 %s\n" +		"cdev_path_len:	 %d\n\n", +	       d->ubi_root, d->nlen_max, d->version, d->cdev_path, +	       d->udev_path, d->wear_path, d->vol_count_path, +	       d->tot_ebs_path, d->avail_ebs_path, d->eb_size_path, +	       d->nums_path, d->vol_cdev_path, d->vdev_path, +	       d->vol_nums_path, d->vol_bytes_path, d->vol_ebs_path, +	       d->vol_type_path, d->vol_name_path, d->cdev_path_len); +} + +int +ubi_set_cdev_pattern(ubi_lib_t desc, const char *pattern) +{ +	char *patt; + +	patt = strdup(pattern); +	if (!patt) { +		ubi_err("cannot allocate memory"); +		return -1; +	} + +	if (desc->cdev_path) +		free(desc->cdev_path); + +	desc->cdev_path = patt; +	desc->cdev_path_len = strlen(patt) + 1 + UBI_MAX_ID_SIZE; + +	ubi_dbg("ubi dev pattern is now \"%s\"", patt); + +	return 0; +} + +int +ubi_set_vol_cdev_pattern(ubi_lib_t desc, const char *pattern) +{ +	char *patt; + +	patt = strdup(pattern); +	if (!patt) { +		ubi_err("cannot allocate memory"); +		return -1; +	} + +	free(desc->vol_cdev_path); +	desc->vol_cdev_path = patt; +	desc->vol_cdev_path_len = strlen(patt) + 1 + 2 * UBI_MAX_ID_SIZE; + +	ubi_dbg("ubi volume dev pattern is now \"%s\"", patt); + +	return 0; +} + +int +ubi_open(ubi_lib_t *desc) +{ +	int err = -1; +	ubi_lib_t res; +	struct stat stat; + +	res = calloc(1, sizeof(struct ubi_lib)); +	if (!res) { +		ubi_err("cannot allocate memory"); +		return -1; +	} + +	res->cdev_path = NULL; +	err = ubi_set_cdev_pattern(res, UBI_CDEV_PATH); +	if (err) +		goto error; + +	/* TODO: this actually has to be discovered */ +	res->sysfs_root = strdup(UBI_SYSFS_ROOT); +	if (!res->sysfs_root) +		goto error; + +	res->ubi_root = mkpath(res->sysfs_root, UBI_ROOT); +	if (!res->ubi_root) +		goto error; + +	res->nlen_max = mkpath(res->ubi_root, UBI_NLEN_MAX); +	if (!res->nlen_max) +		goto error; + +	res->version =	mkpath(res->ubi_root, UBI_VERSION); +	if (!res->version) +		goto error; + +	res->udev_path = mkpath(res->ubi_root, "ubi%d/"); +	if (!res->udev_path) +		goto error; + +	res->wear_path = mkpath(res->udev_path, UBI_WEAR); +	if (!res->wear_path) +		goto error; + +	res->vol_count_path = mkpath(res->udev_path, UBI_VOL_COUNT); +	if (!res->vol_count_path) +		goto error; + +	res->tot_ebs_path = mkpath(res->udev_path, UBI_AVAIL_EBS); +	if (!res->tot_ebs_path) +		goto error; + +	res->avail_ebs_path = mkpath(res->udev_path, UBI_TOT_EBS); +	if (!res->avail_ebs_path) +		goto error; + +	res->eb_size_path = mkpath(res->udev_path, UBI_EB_SIZE); +	if (!res->eb_size_path) +		goto error; + +	res->nums_path = mkpath(res->udev_path, UBI_NUMS); +	if (!res->nums_path) +		goto error; + +	err = ubi_set_vol_cdev_pattern(res, UBI_VOL_CDEV_PATH); +	if (err) +		goto error; + +	res->vdev_path = mkpath(res->udev_path, "%d/"); +	if (!res->vdev_path) +		goto error; + +	res->vol_nums_path = mkpath(res->vdev_path, UBI_NUMS); +	if (!res->vol_nums_path) +		goto error; + +	res->vol_bytes_path = mkpath(res->vdev_path, UBI_VBYTES); +	if (!res->vol_bytes_path) +		goto error; + +	res->vol_ebs_path = mkpath(res->vdev_path, UBI_VEBS); +	if (!res->vol_ebs_path) +		goto error; + +	res->vol_type_path = mkpath(res->vdev_path, UBI_VTYPE); +	if (!res->vol_type_path) +		goto error; + +	res->vol_name_path = mkpath(res->vdev_path, UBI_VNAME); +	if (!res->vol_name_path) +		goto error; + +	/* Check if UBI exists in the system */ +	err = lstat(res->ubi_root, &stat); +	if (err) { +		perror("lstat"); +		fprintf(stderr, "%s\n", res->ubi_root); +		err = UBI_ENOTFOUND; +		goto error; +	} + +	err = get_ubi_info(res, &res->ubi); +	if (err) +		goto error; + +	*desc = res; + +	ubi_dbg("opened library successfully."); + +	return 0; + +error: +	ubi_close(&res); + +	if (err == -1 && errno == ENOMEM) +		ubi_err("Cannot allocate memory"); + +	return err; +} + +int +ubi_close(ubi_lib_t *desc) +{ +	ubi_lib_t tmp = *desc; + +	free(tmp->vol_name_path); +	free(tmp->vol_type_path); +	free(tmp->vol_ebs_path); +	free(tmp->vol_bytes_path); +	free(tmp->vol_nums_path); +	free(tmp->vdev_path); +	free(tmp->vol_cdev_path); +	free(tmp->nums_path); +	free(tmp->eb_size_path); +	free(tmp->avail_ebs_path); +	free(tmp->tot_ebs_path); +	free(tmp->vol_count_path); +	free(tmp->wear_path); +	free(tmp->udev_path); +	free(tmp->cdev_path); +	free(tmp->version); +	free(tmp->nlen_max); +	free(tmp->ubi_root); +	free(tmp->sysfs_root); +	free(tmp); + +	*desc = NULL; + +	return 0; +} + +void +ubi_perror(const char *prefix, int code) +{ +	if (code == 0) +		return; + +	fprintf(stderr, "%s: ", prefix); + +	switch (code) { +	case UBI_ENOTFOUND: +		fprintf(stderr, "UBI was not found in system\n"); +		break; +	case UBI_EBUG: +		fprintf(stderr, "an UBI or UBI library bug\n"); +		break; +	case UBI_EINVAL: +		fprintf(stderr, "invalid parameter\n"); +		break; +	case -1: +		perror(prefix); +		break; +	default: +		ubi_err("unknown error code %d", code); +		break; +	} +} + +int +ubi_get_dev_info(ubi_lib_t desc, unsigned int devn, struct ubi_dev_info *di) +{ +	int err; + +	if (devn >= desc->ubi.dev_count) { +		ubi_err("bad device number, max is %d\n", +			desc->ubi.dev_count - 1); +		return UBI_EINVAL; +	} + +	err = sysfs_read_dev_subst(desc->nums_path, &di->major, +				   &di->minor, 1, devn); +	if (err) +		return -1; + +	err = sysfs_read_ull_subst(desc->wear_path, &di->wear, 1, devn); +	if (err) +		return -1; + +	err = sysfs_read_uint_subst(desc->vol_count_path, +				    &di->vol_count, 1, devn); +	if (err) +		return -1; + +	err = sysfs_read_uint_subst(desc->eb_size_path, &di->eb_size, 1, devn); +	if (err) +		return -1; + +	err = sysfs_read_uint_subst(desc->tot_ebs_path, &di->total_ebs, 1, devn); +	if (err) +		return -1; + +	err = sysfs_read_uint_subst(desc->avail_ebs_path, +				    &di->avail_ebs, 1, devn); +	if (err) +		return -1; + +#if 0 +	ubi_dbg("major:minor %d:%d, wear %llu, EB size %d, " +		"vol. count %d, tot. EBs %d, avail. EBs %d", +		di->major, di->minor, di->wear, di->eb_size, +		di->vol_count, di->total_ebs, di->avail_ebs); +#endif + +	return err; +} + +int +ubi_get_vol_info(ubi_lib_t desc, unsigned int devn, unsigned int vol_id, +		struct ubi_vol_info *req) +{ +	int err; +	int len; +	char buf1[10]; +	char buf2[desc->ubi.nlen_max]; + +	err = sysfs_read_dev_subst(desc->vol_nums_path, &req->major, +				   &req->minor, 2, devn, vol_id); +	if (err) +		return -1; + +	err = sysfs_read_ull_subst(desc->vol_bytes_path, +				   &req->bytes, 2, devn, vol_id); +	if (err) +		return -1; + +	err = sysfs_read_uint_subst(desc->vol_ebs_path, +				    &req->eraseblocks, 2, devn, vol_id); +	if (err) +		return -1; + +	len = sysfs_read_data_subst(desc->vol_type_path, &buf1[0], +				    10, 2,  devn, vol_id); +	if (len == -1) +		return -1; + +	if (buf1[len - 1] != '\n') { +		ubi_err("bad volume type"); +		return UBI_EBUG; +	} + +	if (!strncmp(&buf1[0], "static", sizeof("static") - 1)) { +		req->type = UBI_STATIC_VOLUME; +	} else if (!strncmp(&buf1[0], "dynamic", sizeof("dynamic") - 1)) { +		req->type = UBI_DYNAMIC_VOLUME; +	} else { +		ubi_err("bad type %s", &buf1[0]); +		return -1; +	} + +	len = sysfs_read_data_subst(desc->vol_name_path, &buf2[0], +				    desc->ubi.nlen_max, 2,  devn, vol_id); +	if (len == -1) +		return -1; + +	if (buf2[len - 1] != '\n') { +		ubi_err("bad volume name"); +		return UBI_EBUG; +	} + +	req->name = malloc(len); +	if (!req->name) { +		ubi_err("cannot allocate memory"); +		return -1; +	} + +	memcpy(req->name, &buf2[0], len - 1); +	req->name[len - 1] = '\0'; + +	return 0; +} + +/** + * ubi_cdev_open - open a UBI device + * + * @desc	UBI library descriptor + * @devn	Number of UBI device to open + * @flags	Flags to pass to open() + * + * This function opens a UBI device by number and returns a file + * descriptor.  In case of an error %-1 is returned and errno is set + * appropriately. + */ +static int +ubi_cdev_open(ubi_lib_t desc, int devn, int flags) +{ +	char *buf; +	int fd; + +	ubi_dbg("desc=%p, devn=%d, flags=%08x\n", desc, devn, flags); + +	if (desc == NULL) { +		ubi_err("desc is NULL\n"); +		return -1; +	} +	if (desc->vol_cdev_path_len == 0) { +		ubi_err("path_len == 0\n"); +		return -1; +	} +	buf = malloc(desc->cdev_path_len); + +	sprintf(buf, desc->cdev_path, devn); + +	fd = open(buf, flags); +	if (fd == -1) +		ubi_dbg("cannot open %s", buf); + +	free(buf); +	return fd; +} + +/** + * ubi_cdev_close - close a UBI device + * + * @dev_fd	file descriptor of UBI device to close + * + * This function closes the given UBI device. + */ +static int +ubi_cdev_close(int dev_fd) +{ +	return close(dev_fd); +} + +/** + * @size is now in bytes. + */ +int +ubi_mkvol(ubi_lib_t desc, int devn, int vol_id, int vol_type, +	  long long bytes, int alignment, const char *name) +{ +	int fd; +	int err; +	struct ubi_mkvol_req req; + +	if ((fd = ubi_cdev_open(desc, devn, O_RDWR)) == -1) +		return -1; + +	req.vol_id = vol_id; +	req.bytes = bytes; +	req.vol_type = vol_type; +	req.alignment = alignment; +	req.name_len = strlen(name); +	req.name = name; + +	/* printf("DBG: %s(vol_id=%d, bytes=%lld, type=%d, alig=%d, nlen=%d, " +	       "name=%s)\n", __func__, vol_id, bytes, vol_type, alignment, +	       strlen(name), name);*/ + +	err = ioctl(fd, UBI_IOCMKVOL, &req); +	if (err < 0) { +		ubi_err("ioctl returned %d errno=%d\n", err, errno); +		goto out_close; +	} + +	ubi_dbg("created volume %d, size %lld, name \"%s\" " +		"at UBI dev %d\n", vol_id, bytes, name, devn); + +	close(fd); +	return err; + out_close: +	ubi_cdev_close(fd); +	return err; +} + +int +ubi_rmvol(ubi_lib_t desc, int devn, int vol_id) +{ +	int fd; +	int err; + +	if ((fd = ubi_cdev_open(desc, devn, O_RDWR)) == -1) +		return -1; + +	err = ioctl(fd, UBI_IOCRMVOL, &vol_id); +	if (err < 0) +		goto out_close; + +	ubi_dbg("removed volume %d", vol_id); + + out_close: +	ubi_cdev_close(fd); +	return err; +} + +int +ubi_get_info(ubi_lib_t desc, struct ubi_info *ubi) +{ +	memcpy(ubi, &desc->ubi, sizeof(struct ubi_info)); +	return 0; +} + + +int +ubi_vol_open(ubi_lib_t desc, int devn, int vol_id, int flags) +{ +	char *buf; +	int fd; + +	ubi_dbg("desc=%p, devn=%d, vol_id=%d, flags=%08x\n", +		desc, devn, vol_id, flags); + +	if (desc == NULL) { +		ubi_err("desc is NULL\n"); +		return -1; +	} +	if (desc->vol_cdev_path_len == 0) { +		ubi_err("path_len == 0\n"); +		return -1; +	} +	buf = malloc(desc->cdev_path_len); + +	sprintf(buf, desc->vol_cdev_path, devn, vol_id); + +	fd = open(buf, flags); +	if (fd == -1) +		ubi_dbg("cannot open %s", buf); + +	free(buf); +	return fd; +} + +int +ubi_vol_close(int vol_fd) +{ +	return close(vol_fd); +} + + +int +ubi_vol_update(int vol_fd, unsigned long long bytes) +{ +	int err; + +	err = ioctl(vol_fd, UBI_IOCVOLUP, &bytes); +	if (err) { +		ubi_err("%s failure calling update ioctl\n" +			"    IOCTL(%08x) err=%d errno=%d\n", +			__func__, UBI_IOCVOLUP, err, errno); +	} +	return err; +} + +FILE * +ubi_vol_fopen_read(ubi_lib_t desc, int devn, uint32_t vol_id) +{ +	FILE *fp; +	int fd; + +	fd = ubi_vol_open(desc, devn, vol_id, O_RDONLY); +	if (fd == -1) +		return NULL; + +	fp = fdopen(fd, "r"); +	if (fp == NULL) +		ubi_vol_close(fd); + +	return fp; +} + +FILE * +ubi_vol_fopen_update(ubi_lib_t desc, int devn, uint32_t vol_id, +		     unsigned long long bytes) +{ +	FILE *fp; +	int fd; +	int err; + +	fd = ubi_vol_open(desc, devn, vol_id, O_RDWR); +	if (fd == -1) +		return NULL; + +	fp = fdopen(fd, "r+"); +	if (fp == NULL) { +		printf("DBG: %s(errno=%d)\n", __func__, errno); +		ubi_vol_close(fd); +		return NULL; +	} +	err = ubi_vol_update(fd, bytes); +	if (err < 0) { +		printf("DBG: %s() fd=%d err=%d\n", __func__, fd, err); +		fclose(fp); +		return NULL; +	} +	return fp; +} + +int +ubi_vol_get_used_bytes(int vol_fd, unsigned long long *bytes) +{ +	off_t res; + +	res = lseek(vol_fd, 0, SEEK_END); +	if (res == (off_t)-1) +		return -1; +	*bytes = (unsigned long long) res; +	res = lseek(vol_fd, 0, SEEK_SET); +	return res == (off_t)-1 ? -1 : 0; +} diff --git a/ubi-utils/src/libubi/libubi_int.h b/ubi-utils/src/libubi/libubi_int.h new file mode 100644 index 0000000..1640010 --- /dev/null +++ b/ubi-utils/src/libubi/libubi_int.h @@ -0,0 +1,119 @@ +#ifndef __UBI_INT_H__ +#define __UBI_INT_H__ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * UBI (Unsorted Block Images) library. + * + * Author: Artem B. Bityutskiy + */ + +/* + * Enable/disable UBI library debugging messages. + */ +#undef UBILIB_DEBUG + +/* + * UBI library error message. + */ +#define ubi_err(fmt, ...) do {						\ +		fprintf(stderr, "UBI Library Error at %s: ", __func__); \ +		fprintf(stderr, fmt, ##__VA_ARGS__);			\ +		fprintf(stderr, "\n");					\ +	} while(0) + +#ifdef UBILIB_DEBUG +#define ubi_dbg(fmt, ...) do {						\ +		fprintf(stderr, "UBI Debug: %s: ", __func__);		\ +		fprintf(stderr, fmt, ##__VA_ARGS__);			\ +		fprintf(stderr, "\n");					\ +	} while(0) + +#else +#define ubi_dbg(fmt, ...) +#endif + +/** + * SYSFS Entries. + * + * @def UBI_ROOT + *	@brief Name of the root UBI directory in sysfs. + * + * @def UBI_NLEN_MAX + *	@brief Name of syfs file containing the maximum UBI volume name length. + * + * @def UBI_VERSION + *      @brief Name of sysfs file containing UBI version. + * + * @def UBI_WEAR + *	@brief Name of sysfs file containing wear level of an UBI device. + * + * @def UBI_VOL_COUNT + *	@brief Name of sysfs file contaning the of volume on an UBI device + * + * @def UBI_TOT_EBS + *	@brief Name of sysfs file contaning the total number of + *	eraseblocks on an UBI device. + * + * @def UBI_AVAIL_EBS + *	@brief Name of sysfs file contaning the number of unused eraseblocks on + *	an UBI device. + * + * @def UBI_EB_SIZE + *	@brief Name of sysfs file containing size of UBI eraseblocks. + * + * @def UBI_NUMS + *      @brief Name of sysfs file containing major and minor numbers + *      of an UBI device or an UBI volume device. + * + * @def UBI_VBYTES + *	@brief Name of sysfs file containing size of an UBI volume device in + *	bytes. + * + * @def UBI_VEBS + *	@brief Name of sysfs file containing size of an UBI volume device in + *	eraseblocks. + * + * @def UBI_VTYPE + *	@brief Name of sysfs file containing type of an UBI volume device. + * + * @def UBI_VNAME + *	@brief Name of sysfs file containing name of an UBI volume device. + **/ +#define UBI_ROOT	"ubi" +#define UBI_NLEN_MAX	"volume_name_max" +#define UBI_VERSION	"version" +#define UBI_WEAR	"wear" +#define UBI_VOL_COUNT	"volumes_count" +#define UBI_TOT_EBS	"total_eraseblocks" +#define UBI_AVAIL_EBS	"avail_eraseblocks" +#define UBI_EB_SIZE	"eraseblock_size" +#define UBI_NUMS	"dev" +#define UBI_VBYTES	"bytes" +#define UBI_VEBS	"eraseblocks" +#define UBI_VTYPE	"type" +#define UBI_VNAME	"name" + +#define UBI_CDEV_PATH	"/dev/ubi%d" +#define UBI_VOL_CDEV_PATH "/dev/ubi%d_%d" +#define UBI_SYSFS_ROOT	"/sys/class" + +#define UBI_MAX_ID_SIZE	9 + +#endif /* !__UBI_INT_H__ */ diff --git a/ubi-utils/src/libubi/libubi_sysfs.c b/ubi-utils/src/libubi/libubi_sysfs.c new file mode 100644 index 0000000..f7ecebc --- /dev/null +++ b/ubi-utils/src/libubi/libubi_sysfs.c @@ -0,0 +1,231 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * UBI (Unsorted Block Images) library. + * + * Author: Artem B. Bityutskiy + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <unistd.h> +#include <limits.h> +#include <errno.h> +#include <stdarg.h> + +#include "libubi_int.h" + +int +sysfs_read_data(const char *file, void *buf, int len) +{ +	int fd; +	ssize_t rd; + +	fd = open(file, O_RDONLY); +	if (fd == -1) { +		ubi_err("cannot open file %s", file); +		return -1; +	} + +	rd = read(fd, buf, len); +	if (rd == -1) +		ubi_err("cannot read file %s", file); + +	close(fd); + +	return rd; +} + +int +sysfs_read_data_subst(const char *patt, void *buf, int len, int n, ...) +{ +	va_list args; +	char buf1[strlen(patt) + 20 * n]; + +	va_start(args, n); +	vsprintf(&buf1[0], patt, args); +	va_end(args); + +	return sysfs_read_data(&buf1[0], buf, len); +} + +int +sysfs_read_dev(const char *file, unsigned int *major, unsigned int *minor) +{ +	int fd; +	int ret; +	ssize_t rd; +	int err = -1; +	char buf[40]; + +	fd = open(file, O_RDONLY); +	if (fd == -1) { +		ubi_err("cannot open file %s", file); +		return -1; +	} + +	rd = read(fd, &buf[0], 20); +	if (rd == -1) { +		ubi_err("cannot read file %s", file); +		goto error; +	} +	if (rd < 4) { +		ubi_err("bad contents of file %s:", file); +		goto error; +	} + +	err = -1; +	if (buf[rd -1] != '\n') { +		ubi_err("bad contents of file %s", file); +		goto error; +	} + +	ret = sscanf(&buf[0], "%d:%d\n", major, minor); +	if (ret != 2) { +		ubi_err("bad contents of file %s", file); +		goto error; +	} + +	err = 0; + +error: +	close(fd); + +	return err; +} + +int +sysfs_read_dev_subst(const char *patt, unsigned int *major, +		unsigned int *minor, int n, ...) +{ +	va_list args; +	char buf[strlen(patt) + 20 * n]; + +	va_start(args, n); +	vsprintf(&buf[0], patt, args); +	va_end(args); + +	return sysfs_read_dev(&buf[0], major, minor); +} + +static int +sysfs_read_ull(const char *file, unsigned long long *num) +{ +	return 0; +} + +int +sysfs_read_ull_subst(const char *patt, unsigned long long *num, int n, ...) +{ +	va_list args; +	char buf[strlen(patt) + 20 * n]; + +	va_start(args, n); +	vsprintf(&buf[0], patt, args); +	va_end(args); + +	return	sysfs_read_ull(&buf[0], num); +} + +static int +sysfs_read_uint(const char *file, unsigned int *num) +{ +	return 0; +} + +int +sysfs_read_uint_subst(const char *patt, unsigned int *num, int n, ...) +{ +	va_list args; +	char buf[strlen(patt) + 20 * n]; + +	va_start(args, n); +	vsprintf(&buf[0], patt, args); +	va_end(args); + +	return	sysfs_read_uint(&buf[0], num); +} + +int +sysfs_read_ll(const char *file, long long *num) +{ +	int fd; +	ssize_t rd; +	int err = -1; +	char buf[20]; +	char *endptr; + +	fd = open(file, O_RDONLY); +	if (fd == -1) +		return -1; + +	rd = read(fd, &buf[0], 20); +	if (rd == -1) +		goto out; + +	if (rd < 2) { +		ubi_err("bad contents in file %s: \"%c%c...\"", +			file, buf[0], buf[1]); +		goto out_errno; +	} + +	*num = strtoll(&buf[0], &endptr, 10); +	if (endptr == &buf[0] || *endptr != '\n') { +		ubi_err("bad contents in file %s: \"%c%c...\"", +			file, buf[0], buf[1]); +		goto out_errno; +	} + +	if  (*num < 0) { +		ubi_err("bad number in file %s: %lld", file, *num); +		goto out_errno; +	} + +	err = 0; + +out_errno: +	errno = EINVAL; + +out: +	close(fd); +	return err; +} + +int +sysfs_read_int(const char *file, int *num) +{ +	int err; +	long long res = 0; + +	err = sysfs_read_ll(file, &res); +	if (err) +		return err; + +	if (res < 0 || res > INT_MAX) { +		ubi_err("bad number in file %s: %lld", file, res); +		errno = EINVAL; +		return -1; +	} + +	*num = res; +	return 0; +} diff --git a/ubi-utils/src/libubi/libubi_sysfs.h b/ubi-utils/src/libubi/libubi_sysfs.h new file mode 100644 index 0000000..2fb6072 --- /dev/null +++ b/ubi-utils/src/libubi/libubi_sysfs.h @@ -0,0 +1,109 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * UBI (Unsorted Block Images) library. + * + * Author: Artem B. Bityutskiy + */ + +/** + * sysfs_read_data - read data from a sysfs file. + * + * @file  path to the file to read from + * @buf	  furrer where to store read data + * @len	  length of provided buffer @buf + * + * This function returns the number of read bytes or -1 in case of error. + */ +int sysfs_read_data(const char *file, void *buf, int len); + +/** + * sysfs_read_data_subst - form path to a sysfs file and read data from it. + * + * @patt  path to the file to read from + * @buf	  furrer where to store read data + * @len	  length of provided buffer @buf + * @n	  number of parameters to substitute to @patt + * + * This function forms path to a sysfs file by means of substituting parameters + * to @patt and then reads @len bytes from this file and stores the read data + * to @buf. This function returns the number of read bytes or -1 in case of + * error. + */ +int sysfs_read_data_subst(const char *patt, void *buf, int len, int n, ...); + +/** + * sysfs_read_dev - read major and minor number from a sysfs file. + * + * @file   path to the file to read from + * @major  major number is returned here + * @minor  minor number is returned here + */ +int sysfs_read_dev(const char *file, unsigned int *major, +		unsigned int *minor); +/** + * sysfs_read_dev_subst - for path to a file and read major and minor number + * from it. + * + * @patt   pattern of the path to the file to read from + * @major  major number is returned here + * @minor  minor number is returned here + * @n	   number of arguments to substitute + * + * This function substitures arguments to the @patt file path pattern and reads + * major and minor numbers from the resulting file. + */ +int sysfs_read_dev_subst(const char *patt, unsigned int *major, +		unsigned int *minor, int n, ...); + +/** + * sysfs_read_ull_subst - form path to a sysfs file and read an unsigned long + * long value from there. + * + * @patt  pattern of file path + * @num	  the read value is returned here + * @n	  number of parameters to substitute + * + * + * This function first forms the path to a sysfs file by means of substituting + * passed parameters to the @patt string, and then read an 'unsigned long long' + * value from this file. + */ +int sysfs_read_ull_subst(const char *patt, unsigned long long *num, +		int n, ...); + +/** + * sysfs_read_uint_subst - the same as 'sysfs_read_uint_subst()' but reads an + * unsigned int value. + */ +int sysfs_read_uint_subst(const char *patt, unsigned int *num, +		int n, ...); + +/** + * sysfs_read_ll - read a long long integer from an UBI sysfs file. + * + * @file  file name from where to read + * @num	  the result is returned here + */ +int sysfs_read_ll(const char *file, long long *num); + +/** + * sysfs_read_int - the same as 'sysfs_read_ll()' but reads an 'int' value. + */ +int sysfs_read_int(const char *file, int *num); diff --git a/ubi-utils/src/libubigen/ubigen.c b/ubi-utils/src/libubigen/ubigen.c new file mode 100644 index 0000000..0cfa687 --- /dev/null +++ b/ubi-utils/src/libubigen/ubigen.c @@ -0,0 +1,486 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Oliver Lohmann + * + * Add UBI headers to binary data. + */ + +#include <stdlib.h> +#include <stdint.h> +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <mtd/ubi-header.h> + +#include "config.h" +#include "ubigen.h" +#include "crc32.h" + +#define UBI_NAME_SIZE		256 +#define DEFAULT_VID_OFFSET	((DEFAULT_PAGESIZE) - (UBI_VID_HDR_SIZE)) +#define MIN(a,b)		((a) < (b) ? (a) : (b)) + +static uint32_t crc32_table[256]; + +struct ubi_info { +	struct ubi_vid_hdr* v;	/* Volume ID header */ +	struct ubi_ec_hdr* ec;	/* Erase count header */ + +	FILE* fp_in;		/* Input Stream */ +	FILE* fp_out;		/* Output stream */ + +	size_t eb_size;		/* Physical EB size in bytes */ +	size_t leb_size;	/* Size of a logical EB in a physical EB */ +	size_t leb_total;	/* Total input size in logical EB */ +	size_t alignment;	/* Block alignment */ +	size_t data_pad;	/* Size of padding in each physical EB */ + +	size_t bytes_total;	/* Total input size in bytes */ +	size_t bytes_read;	/* Nymber of read bytes (total) */ + +	uint32_t blks_written;	/* Number of written logical EB */ + +	uint8_t* buf;		/* Allocated buffer */ +	uint8_t* ptr_ec_hdr;	/* Pointer to EC hdr in buf */ +	uint8_t* ptr_vid_hdr;	/* Pointer to VID hdr in buf */ +	uint8_t* ptr_data;	/* Pointer to data region in buf */ +}; + + +static uint32_t +byte_to_blk(uint64_t byte, uint32_t eb_size) +{ +	return (byte % eb_size) == 0 +		? (byte / eb_size) +		: (byte / eb_size) + 1; +} + +static int +validate_ubi_info(ubi_info_t u) +{ +	if ((u->v->vol_type != UBI_VID_DYNAMIC) && +	    (u->v->vol_type != UBI_VID_STATIC)) { +		return EUBIGEN_INVALID_TYPE; +	} + +	if (ubi32_to_cpu(u->ec->vid_hdr_offset) < UBI_VID_HDR_SIZE) { +		return EUBIGEN_INVALID_HDR_OFFSET; +	} + +	return 0; +} + +static int +skip_blks(ubi_info_t u, uint32_t blks) +{ +	uint32_t i; +	size_t read = 0, to_read = 0; + +	/* Step to a maximum of leb_total - 1 to keep the +	   restrictions. */ +	for (i = 0; i < MIN(blks, u->leb_total-1); i++) { +		/* Read in data */ +		to_read = MIN(u->leb_size, +			      (u->bytes_total - u->bytes_read)); +		read = fread(u->ptr_data, 1, to_read, u->fp_in); +		if (read != to_read) { +			return -EIO; +		} +		u->bytes_read += read; +		u->blks_written++; +	} + +	return 0; +} + +static void +clear_buf(ubi_info_t u) +{ +	memset(u->buf, 0xff, u->eb_size); +} + +static void +write_ec_hdr(ubi_info_t u) +{ +	memcpy(u->ptr_ec_hdr, u->ec, UBI_EC_HDR_SIZE); +} + +static int +fill_data_buffer_from_file(ubi_info_t u, size_t* read) +{ +	size_t to_read = 0; + +	if (u-> fp_in == NULL) +		return -EIO; + +	to_read = MIN(u->leb_size, (u->bytes_total - u->bytes_read)); +	*read = fread(u->ptr_data, 1, to_read, u->fp_in); +	if (*read != to_read) { +		return -EIO; +	} +	return 0; +} + +static void +add_static_info(ubi_info_t u, size_t data_size, ubigen_action_t action) +{ +	uint32_t crc = clc_crc32(crc32_table, UBI_CRC32_INIT, +				 u->ptr_data, data_size); + +	u->v->data_size = cpu_to_ubi32(data_size); +	u->v->data_crc = cpu_to_ubi32(crc); + +	if (action & BROKEN_DATA_CRC) { +		u->v->data_crc = +			cpu_to_ubi32(ubi32_to_cpu(u->v->data_crc) + 1); +	} +	if (action & BROKEN_DATA_SIZE) { +		u->v->data_size = +			cpu_to_ubi32(ubi32_to_cpu(u->v->data_size) + 1); +	} +} + +static void +write_vid_hdr(ubi_info_t u, ubigen_action_t action) +{ +	uint32_t crc = clc_crc32(crc32_table, UBI_CRC32_INIT, +				 u->v, UBI_VID_HDR_SIZE_CRC); +	/* Write VID header */ +	u->v->hdr_crc = cpu_to_ubi32(crc); +	if (action & BROKEN_HDR_CRC) { +		u->v->hdr_crc = cpu_to_ubi32(ubi32_to_cpu(u->v->hdr_crc) + 1); +	} +	memcpy(u->ptr_vid_hdr, u->v, UBI_VID_HDR_SIZE); +} + +static int +write_to_output_stream(ubi_info_t u) +{ +	size_t written; + +	written = fwrite(u->buf, 1, u->eb_size, u->fp_out); +	if (written != u->eb_size) { +		return -EIO; +	} +	return 0; +} + +int +ubigen_write_leb(ubi_info_t u, ubigen_action_t action) +{ +	int rc = 0; +	size_t read = 0; + +	clear_buf(u); +	write_ec_hdr(u); + +	rc = fill_data_buffer_from_file(u, &read); +	if (rc != 0) +		return rc; + +	if (u->v->vol_type == UBI_VID_STATIC)  { +		add_static_info(u, read, action); +	} + +	u->v->lnum = cpu_to_ubi32(u->blks_written); + +	if (action & MARK_AS_UPDATE) { +		u->v->copy_flag = (u->v->copy_flag)++; +	} + +	write_vid_hdr(u, action); +	rc = write_to_output_stream(u); +	if (rc != 0) +		return rc; + +	/* Update current handle */ +	u->bytes_read += read; +	u->blks_written++; +	return 0; +} + +int +ubigen_write_complete(ubi_info_t u) +{ +	size_t i; +	int rc = 0; + +	for (i = 0; i < u->leb_total; i++) { +		rc = ubigen_write_leb(u,  NO_ERROR); +		if (rc != 0) +			return rc; +	} + +	return 0; +} + +int +ubigen_write_broken_update(ubi_info_t u, uint32_t blk) +{ +	int rc = 0; + +	rc = skip_blks(u, blk); +	if (rc != 0) +		return rc; + +	rc = ubigen_write_leb(u, MARK_AS_UPDATE | BROKEN_DATA_CRC); +	if (rc != 0) +		return rc; + + +	return 0; +} + +void +dump_info(ubi_info_t u) +{ +#ifdef DEBUG +	int err = 0; +	if (!u) { +		fprintf(stderr, "<empty>"); +		return; +	} +	if (!u->ec) { +		fprintf(stderr, "<ec-empty>"); +		err = 1; +	} +	if (!u->v) { +		fprintf(stderr, "<v-empty>"); +		err = 1; +	} +	if (err) return; + +	fprintf(stderr, "ubi volume\n"); +	fprintf(stderr, "version      :	  %8d\n", u->v->version); +	fprintf(stderr, "vol_id	      :	  %8d\n", ubi32_to_cpu(u->v->vol_id)); +	fprintf(stderr, "vol_type     :	  %8s\n", +		u->v->vol_type == UBI_VID_STATIC ? +		"static" : "dynamic"); +	fprintf(stderr, "used_ebs     :	  %8d\n", +		ubi32_to_cpu(u->v->used_ebs)); +	fprintf(stderr, "eb_size      : 0x%08x\n", u->eb_size); +	fprintf(stderr, "leb_size     : 0x%08x\n", u->leb_size); +	fprintf(stderr, "data_pad     : 0x%08x\n", +		ubi32_to_cpu(u->v->data_pad)); +	fprintf(stderr, "leb_total    :	  %8d\n", u->leb_total); +	fprintf(stderr, "header offs  : 0x%08x\n", +		ubi32_to_cpu(u->ec->vid_hdr_offset)); +	fprintf(stderr, "bytes_total  :	  %8d\n", u->bytes_total); +	fprintf(stderr, "  +  in MiB  : %8.2f M\n", +		((float)(u->bytes_total)) / 1024 / 1024); +	fprintf(stderr, "-------------------------------\n\n"); +#else +	return; +#endif +} + +int +ubigen_destroy(ubi_info_t *u) +{ +	if (u == NULL) +		return -EINVAL; + +	ubi_info_t tmp = *u; + +	if (tmp) { +		if (tmp->v) +			free(tmp->v); +		if (tmp->ec) +			free(tmp->ec); +		if (tmp->buf) +			free(tmp->buf); +		free(tmp); +	} +	*u = NULL; +	return 0; +} + +void +ubigen_init(void) +{ +	init_crc32_table(crc32_table); +} + +int +ubigen_create(ubi_info_t* u, uint32_t vol_id, uint8_t vol_type, +	      uint32_t eb_size, uint64_t ec, uint32_t alignment, +	      uint8_t version, uint32_t vid_hdr_offset, uint8_t compat_flag, +	      size_t data_size, FILE* fp_in, FILE* fp_out) +{ +	int rc = 0; +	ubi_info_t res = NULL; +	uint32_t crc; +	uint32_t data_offset; + +	if (alignment == 0) { +		rc = EUBIGEN_INVALID_ALIGNMENT; +		goto ubigen_create_err; +	} +	if ((fp_in == NULL) || (fp_out == NULL)) { +		rc = -EINVAL; +		goto ubigen_create_err; +	} + +	res = (ubi_info_t) calloc(1, sizeof(struct ubi_info)); +	if (res == NULL) { +		rc = -ENOMEM; +		goto ubigen_create_err; +	} + +	res->v = (struct ubi_vid_hdr*) calloc(1, sizeof(struct ubi_vid_hdr)); +	if (res->v == NULL) { +		rc = -ENOMEM; +		goto ubigen_create_err; +	} + +	res->ec = (struct ubi_ec_hdr*) calloc(1, sizeof(struct ubi_ec_hdr)); +	if (res->ec == NULL) { +		rc = -ENOMEM; +		goto ubigen_create_err; +	} + +	/* data which is needed in the general process */ +	vid_hdr_offset = vid_hdr_offset ? vid_hdr_offset : DEFAULT_VID_OFFSET; +	data_offset = vid_hdr_offset + UBI_VID_HDR_SIZE; +	res->bytes_total = data_size; +	res->eb_size = eb_size ? eb_size : DEFAULT_BLOCKSIZE; +	res->data_pad = (res->eb_size - data_offset) % alignment; +	res->leb_size = res->eb_size - data_offset - res->data_pad; +	res->leb_total = byte_to_blk(data_size, res->leb_size); +	res->alignment = alignment; + +	if ((res->eb_size < (vid_hdr_offset + UBI_VID_HDR_SIZE))) { +		rc = EUBIGEN_TOO_SMALL_EB; +		goto ubigen_create_err; +	} +	res->fp_in = fp_in; +	res->fp_out = fp_out; + +	/* vid hdr data which doesn't change */ +	res->v->magic = cpu_to_ubi32(UBI_VID_HDR_MAGIC); +	res->v->version = version ? version : UBI_VERSION; +	res->v->vol_type = vol_type; +	res->v->vol_id = cpu_to_ubi32(vol_id); +	res->v->compat = compat_flag; +	res->v->data_pad = cpu_to_ubi32(res->data_pad); + +	/* static only: used_ebs */ +	if (res->v->vol_type == UBI_VID_STATIC) { +		res->v->used_ebs = cpu_to_ubi32(byte_to_blk +						(res->bytes_total, +						 res->leb_size)); +	} + +	/* ec hdr (fixed, doesn't change) */ +	res->ec->magic = cpu_to_ubi32(UBI_EC_HDR_MAGIC); +	res->ec->version = version ? version : UBI_VERSION; +	res->ec->ec = cpu_to_ubi64(ec); +	res->ec->vid_hdr_offset = cpu_to_ubi32(vid_hdr_offset); + +	res->ec->data_offset = cpu_to_ubi32(data_offset); + +	crc = clc_crc32(crc32_table, UBI_CRC32_INIT, res->ec, +			UBI_EC_HDR_SIZE_CRC); +	res->ec->hdr_crc = cpu_to_ubi32(crc); + +	/* prepare a read buffer */ +	res->buf = (uint8_t*) malloc (res->eb_size * sizeof(uint8_t)); +	if (res->buf == NULL) { +		rc = -ENOMEM; +		goto ubigen_create_err; +	} + +	/* point to distinct regions within the buffer */ +	res->ptr_ec_hdr = res->buf; +	res->ptr_vid_hdr = res->buf + ubi32_to_cpu(res->ec->vid_hdr_offset); +	res->ptr_data = res->buf + ubi32_to_cpu(res->ec->vid_hdr_offset) +		+ UBI_VID_HDR_SIZE; + +	rc = validate_ubi_info(res); +	if (rc != 0) { +		fprintf(stderr, "Volume validation failed: %d\n", rc); +		goto ubigen_create_err; +	} + +	dump_info(res); +	*u = res; +	return rc; + + ubigen_create_err: +	if (res) { +		if (res->v) +			free(res->v); +		if (res->ec) +			free(res->ec); +		if (res->buf) +			free(res->buf); +		free(res); +	} +	*u = NULL; +	return rc; +} + +int +ubigen_get_leb_size(ubi_info_t u, size_t* size) +{ +	if (u == NULL) +		return -EINVAL; + +	*size = u->leb_size; +	return 0; +} + + +int +ubigen_get_leb_total(ubi_info_t u, size_t* total) +{ +	if (u == NULL) +		return -EINVAL; + +	*total = u->leb_total; +	return 0; +} + +int +ubigen_set_lvol_rec(ubi_info_t u, size_t reserved_bytes, +		    const char* vol_name, struct ubi_vol_tbl_record *lvol_rec) +{ +	uint32_t crc; + +	if ((u == NULL) || (vol_name == NULL)) +		return -EINVAL; + +	memset(lvol_rec, 0x0, UBI_VTBL_RECORD_SIZE); + +	lvol_rec->reserved_pebs = +		cpu_to_ubi32(byte_to_blk(reserved_bytes, u->leb_size)); +	lvol_rec->alignment = cpu_to_ubi32(u->alignment); +	lvol_rec->data_pad = u->v->data_pad; +	lvol_rec->vol_type = u->v->vol_type; + +	lvol_rec->name_len = +		cpu_to_ubi16((uint16_t)strlen((const char*)vol_name)); + +	memcpy(lvol_rec->name, vol_name, UBI_VOL_NAME_MAX + 1); + +	crc = clc_crc32(crc32_table, UBI_CRC32_INIT, +			lvol_rec, UBI_VTBL_RECORD_SIZE_CRC); +	lvol_rec->crc =	 cpu_to_ubi32(crc); + +	return 0; +} diff --git a/ubi-utils/src/libubimirror/ubimirror.c b/ubi-utils/src/libubimirror/ubimirror.c new file mode 100644 index 0000000..bf6b37c --- /dev/null +++ b/ubi-utils/src/libubimirror/ubimirror.c @@ -0,0 +1,217 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <stdio.h> +#include <errno.h> +#include <unistd.h> +#include <memory.h> +#include <fcntl.h> + +#include <libubi.h> +#include "ubimirror.h" + +#define COMPARE_BUF_SIZE    (128 * 1024) + +#define EBUF(fmt...) do {			\ +	snprintf(err_buf, err_buf_size, fmt);	\ +} while (0) + +enum { +	compare_error = -1, +	seek_error = -2, +	write_error = -3, +	read_error = -4, +	update_error = -5, +	ubi_error = -6, +	open_error = -7, +	close_error = -8, +	compare_equal = 0, +	compare_different = 1 +}; + +/* + * Read len number of bytes from fd. + * Return 0 on EOF, -1 on error. + */ +static ssize_t fill_buffer(int fd, unsigned char *buf, size_t len) +{ +	ssize_t got, have = 0; + +	do { +		got = read(fd, buf + have, len - have); +		if( got == -1 && errno != EINTR ) +			return -1; +		have += got; +	} while ( got > 0 && have < len); +	return have; +} + +/* + * Write len number of bytes to fd. + * Return bytes written (>= 0), -1 on error. + */ +static ssize_t flush_buffer(int fd, unsigned char *buf, size_t len) +{ +	ssize_t done, have = 0; + +	do { +		done = write(fd, buf + have, len - have); +		if( done == -1 && errno != EINTR ) +			return -1; +		have += done; +	} while ( done > 0 && have < len); +	return have; +} + +/* + *  Compare two files.  Return 0, 1, or -1, depending on whether the + *  files are equal, different, or an error occured. + *  Return compare-different when target volume can not be read. Might be + *  an interrupted volume update and then the target device returns -EIO but + *  can be updated. + * + *  fd_a is source + *  fd_b is destination + */ +static int +compare_files(int fd_a, int fd_b) +{ +	unsigned char buf_a[COMPARE_BUF_SIZE], buf_b[COMPARE_BUF_SIZE]; +	ssize_t len_a, len_b; +	int rc; + +	for (;;) { +		len_a = fill_buffer(fd_a, buf_a, sizeof(buf_a)); +		if (len_a == -1){ +			rc = compare_error; +			break; +		} +		len_b = fill_buffer(fd_b, buf_b, sizeof(buf_b)); +		if (len_b == -1){ +			rc = compare_different; +			break; +		} +		if( len_a != len_b ){ +			rc = compare_different; +			break; +		} +		if( len_a == 0 ){	/* Size on both filies equal and EOF */ +			rc = compare_equal; +			break; +		} +		if( memcmp(buf_a, buf_b, len_a) != 0 ){ +			rc = compare_different; +			break; +		} +	} +	/* Position both files at the beginning */ +	if( lseek(fd_a, 0, SEEK_SET) == -1 || +	    lseek(fd_b, 0, SEEK_SET) == -1 ) +		rc = seek_error; +	return rc; +} + +static int +copy_files(int fd_in, int fd_out) +{ +	unsigned char buf_a[COMPARE_BUF_SIZE]; +	ssize_t len_a, len_b; +	unsigned long long update_size, copied; + +	if( ubi_vol_get_used_bytes(fd_in, &update_size) == -1 || +	    ubi_vol_update(fd_out, update_size) == -1 ) +		return update_error; +	for( copied = 0; copied < update_size; copied += len_b ){ +		len_a = fill_buffer(fd_in, buf_a, sizeof(buf_a)); +		if (len_a == -1) +			return read_error; +		if (len_a == 0)		/* Reach EOF */ +			return 0; +		len_b = flush_buffer(fd_out, buf_a, len_a); +		if( len_b != len_a ) +			return write_error; +	} +	return 0; +} + +int +ubimirror(uint32_t devno, int seqnum, uint32_t *ids, size_t ids_size, +	  char *err_buf, size_t err_buf_size) +{ +	int rc = 0; +	uint32_t src_id; +	ubi_lib_t ulib = NULL; +	int fd_in = -1, i = 0, fd_out = -1; + +	if (ids_size == 0) +		return 0; +	else { +		if ((seqnum < 0) || (seqnum > (ids_size - 1))) { +			EBUF("volume id %d out of range", seqnum); +			return EUBIMIRROR_NO_SRC; +		} +		src_id = ids[seqnum]; +	} + +	rc = ubi_open(&ulib); +	if (rc != 0) +		return ubi_error; + +	fd_in = ubi_vol_open(ulib, devno, src_id, O_RDONLY); +	if (fd_in == -1){ +		EBUF("open error source volume %d", ids[i]); +		rc = open_error; +		goto err; +	} + +	for (i = 0; i < ids_size; i++) { +		if (ids[i] == src_id)		/* skip self-mirror */ +			continue; + +		fd_out = ubi_vol_open(ulib, devno, ids[i], O_RDWR); +		if (fd_out == -1){ +			EBUF("open error destination volume %d", ids[i]); +			rc = open_error; +			goto err; +		} +		rc = compare_files(fd_in, fd_out); +		if (rc < 0 ){ +			EBUF("compare error volume %d and %d", src_id, ids[i]); +			goto err; +		} +		rc = copy_files(fd_in, fd_out); +		if (rc != 0) { +			EBUF("mirror error volume %d to %d", src_id, ids[i]); +			goto err; +		} +		if( (rc = ubi_vol_close(fd_out)) == -1 ){ +			EBUF("close error volume %d", ids[i]); +			rc = close_error; +			goto err; +		}else +			fd_out = -1; +	} +err: +	if (ulib != NULL) +		ubi_close(&ulib); +	if (fd_in != -1) +		ubi_vol_close(fd_in); +	if (fd_out != -1) +		ubi_vol_close(fd_out); +	return rc; +} diff --git a/ubi-utils/src/mkbootenv/mkbootenv.c b/ubi-utils/src/mkbootenv/mkbootenv.c new file mode 100644 index 0000000..49ce597 --- /dev/null +++ b/ubi-utils/src/mkbootenv/mkbootenv.c @@ -0,0 +1,173 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Oliver Lohmann + * + * Create boot-parameter/pdd data from an ASCII-text input file. + */ + +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <getopt.h> +#include <argp.h> +#include <unistd.h> +#include <errno.h> +#include <mtd/ubi-header.h> + +#include "config.h" +#include "bootenv.h" +#include "error.h" + +const char *argp_program_version = PACKAGE_VERSION; +const char *argp_program_bug_address = PACKAGE_BUGREPORT; +static char doc[] = "\nVersion: " PACKAGE_VERSION "\n\tBuilt on " +	BUILD_CPU" "BUILD_OS" at "__DATE__" "__TIME__"\n" +	"\n" +	"mkbootenv - processes bootenv text files and convertes " +	"them into a binary format.\n"; + +static const char copyright [] __attribute__((unused)) = +	"Copyright (c) International Business Machines Corp., 2006"; + +static struct argp_option options[] = { +	{ .name = "copyright", +	  .key = 'c', +	  .arg = NULL, +	  .flags = 0, +	  .doc = "Print copyright information.", +	  .group = 1 }, +	{ .name = "output", +	  .key = 'o', +	  .arg = "<output>", +	  .flags = 0, +	  .doc = "Write the the output data to <output> instead of stdout.", +	  .group = 1 }, +	{ .name = NULL, +	  .key = 0, +	  .arg = NULL, +	  .flags = 0, +	  .doc = NULL, +	  .group = 0 }, +}; + +typedef struct myargs { +	FILE* fp_in; +	FILE* fp_out; + +	char *arg1; +	char **options;			/* [STRING...] */ +} myargs; + + + +static error_t +parse_opt(int key, char *arg, struct argp_state *state) +{ +	int err = 0; + +	myargs *args = state->input; + +	switch (key) { +	case 'c': +		fprintf(stderr, "%s\n", copyright); +		exit(0); +		break; +	case 'o': +		args->fp_out = fopen(arg, "wb"); +		if ((args->fp_out) == NULL) { +			fprintf(stderr, +			"Cannot open file %s for output\n", arg); +			exit(1); +		} +		break; +	case ARGP_KEY_ARG: +		args->fp_in = fopen(arg, "rb"); +		if ((args->fp_in) == NULL) { +			fprintf(stderr, +			"Cannot open file %s for input\n", arg); +			exit(1); +		} +		args->arg1 = arg; +		args->options = &state->argv[state->next]; +		state->next = state->argc; +		break; +	case ARGP_KEY_END: +		if (err) { +			fprintf(stderr, "\n"); +			argp_usage(state); +			exit(1); +		} +		break; +	default: +		return(ARGP_ERR_UNKNOWN); +	} + +	return 0; +} + +static struct argp argp = { +	.options     = options, +	.parser	     = parse_opt, +	.args_doc    = "[bootenv-txt-file]", +	.doc	     = doc, +	.children    = NULL, +	.help_filter = NULL, +	.argp_domain = NULL, +}; + +int +main(int argc, char **argv) { +	int rc = 0; +	bootenv_t env; + +	myargs args = { +		.fp_in = stdin, +		.fp_out = stdout, +		.arg1 = NULL, +		.options = NULL, +	}; + +	argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &args); + +	rc = bootenv_create(&env); +	if (rc != 0) { +		err_msg("Cannot create bootenv handle."); +		goto err; +	} +	rc = bootenv_read_txt(args.fp_in, env); +	if (rc != 0) { +		err_msg("Cannot read bootenv from input file."); +		goto err; +	} +	rc = bootenv_write(args.fp_out, env); +	if (rc != 0) { +		err_msg("Cannot write bootenv to output file."); +		goto err; +	} + +	if (args.fp_in != stdin) { +		fclose(args.fp_in); +	} +	if (args.fp_out != stdout) { +		fclose(args.fp_out); +	} + +err: +	bootenv_destroy(&env); +	return rc; +} diff --git a/ubi-utils/src/mkpfi/f128_nand_sample.cfg b/ubi-utils/src/mkpfi/f128_nand_sample.cfg new file mode 100644 index 0000000..e468d9d --- /dev/null +++ b/ubi-utils/src/mkpfi/f128_nand_sample.cfg @@ -0,0 +1,38 @@ +[targets] +complete=ipl,spl,bootenv,kernel,rootfs +bootcode=spl,bootenv + +# Build sections +[ipl]  +image=ipl.bin +raw_starts=0x00000000 +raw_total_size=128kiB  + +[spl] +image=u-boot.bin +ubi_ids=2,3 +ubi_size=2MiB +ubi_type=static +ubi_names=spl_0,spl_1 + +[bootenv] +bootenv_file=bootenv_complete.txt +ubi_ids=4,5 +ubi_size=128kiB +ubi_type=static +ubi_names=bootenv_0,bootenv_1 + +[kernel] +image=vmlinux.bin +ubi_ids=6,7 +ubi_size=6MiB +ubi_type=static +ubi_names=kernel_0,kernel_1 + +[rootfs] +image=rootfs.bin +ubi_ids=8,9 +ubi_alignment=2kiB +ubi_size=16MiB  +ubi_type=dynamic +ubi_names=rootfs_0,rootfs_1 diff --git a/ubi-utils/src/mkpfi/f64_nor_sample.cfg b/ubi-utils/src/mkpfi/f64_nor_sample.cfg new file mode 100644 index 0000000..fd44e27 --- /dev/null +++ b/ubi-utils/src/mkpfi/f64_nor_sample.cfg @@ -0,0 +1,39 @@ +[targets] +complete=ipl,spl,bootenv,kernel,rootfs +bootcode=spl,bootenv +rootfs=rootfs + +# Build sections +[ipl]  +image=ipl.bin +raw_starts=0x02FE0000, 0x03FE0000 +raw_total_size=128kiB  + +[spl] +image=u-boot.bin +ubi_ids=2,3 +ubi_size=2MiB +ubi_type=static +ubi_names=spl_0,spl_1 + +[bootenv] +bootenv_file=bootenv_complete.txt +ubi_ids=4,5 +ubi_size=128kiB +ubi_type=static +ubi_names=bootenv_0,bootenv_1 + +[kernel] +image=vmlinux.bin +ubi_ids=6,7 +ubi_size=6MiB +ubi_type=static +ubi_names=kernel_0,kernel_1 + +[rootfs] +image=rootfs.bin +ubi_ids=8,9 +ubi_alignment=2kiB +ubi_size=16128kiB  +ubi_type=dynamic +ubi_names=rootfs_0,rootfs_1 diff --git a/ubi-utils/src/mkpfi/mkpfi b/ubi-utils/src/mkpfi/mkpfi new file mode 100755 index 0000000..2cce587 --- /dev/null +++ b/ubi-utils/src/mkpfi/mkpfi @@ -0,0 +1,723 @@ +#!/usr/bin/perl +# +# Copyright (c) International Business Machines Corp., 2006 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See +# the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# + +# +# mkpfi +# +# This perl program is assembles PFI files from a config file. +# +# Author: Oliver Lohmann (oliloh@de.ibm.com) +# +use warnings; +use strict; +use lib "/usr/lib/perl5"; # Please change this path as you need it, or +			  # make a proposal how this could be done +			  # nicer. +use Getopt::Long; +use Pod::Usage; +use Config::IniFiles; +use File::Temp; + +# ---------------------------------------------------------------------------- +# Versions +our $version : unique = "0.1"; +our $pfi_version : unique = "0x1"; + +# ---------------------------------------------------------------------------- +# Globals +my $verbose = 0; +my $cfg; + +my %opts = (); +my %files = (config => ""); +my @tmp_files; + +my %tools = (ubicrc32 => "ubicrc32"); + +# ---------------------------------------------------------------------------- +# Processing the input sections +# +# The idea is to combine each section entry with a function +# in order to allow some kind of preprocessing for the values +# before they are written into the PFI file. +# This is especially useful to be more verbose and +# user-friendly in the layout file. +# +# All key-function hashes are applied after the general +# validation of the configuration file. +# If any mandatory key is missing in a section the user +# will be informed and the PFI creation process is aborted. +# +# Default keys will be checked for their presence inside the config +# file. If they are missing, they will be generated with appr. values. + +# Mandatory keys for UBI volumes. +my %ubi_keys = ("ubi_ids"       => \&check_id_list, +		"ubi_size"      => \&replace_num, +		"ubi_type"      => \&replace_type, +		"ubi_names"     => \&remove_spaces, +		"ubi_alignment" => \&replace_num); + +# Mandatory keys for RAW sections. +my %raw_keys = ("raw_starts"     => \&expand_starts, +		"raw_total_size" => \&replace_num); + +# Common default keys for documentation and control purposes. +my %common_keys = ("flags" => \&replace_num, +		   "label" => \&do_nothing); + +# Define any defaults here. Values which maintained in this default +# region need not to be specified by the user explicitly. +my %def_ubi_keys      = ("ubi_alignment" => [\&set_default, "0x1"]); +my %def_raw_keys      = (); +my %def_common_keys   = ("flags"	 => [\&set_default, "0x0"], +			 "label"	 => [\&generate_label, ""]); + +# ---------------------------------------------------------------------------- +# Input keys, actually the path to the input data. + +my %input_keys = ("image" => \&do_nothing); + +# Placeholder keys allow the replacement via a special +# purpose function. E.g. the bootenv_file key will be used +# to generate bootenv binary data from an text file and +# replace the bootenv_file key with an image key to handle it +# in the same way in the further creation process. +my %input_placeholder_keys = ("bootenv_file" => \&create_bootenv_image); + +# ---------------------------------------------------------------------------- +# Helper + +# @brief Get current time string. +sub get_date { +	my $tmp = scalar localtime; +	$tmp =~ s/ /_/g; +	return $tmp; +} + +# @brief Print an info message to stdout. +sub INFO($) { +	my $str = shift; + +	if (!$verbose) { +		return; +	} + +	print STDOUT $str; +} + +# @brief Print an error message to stderr. +sub ERR($) { +	my $str = shift; +	print STDERR $str; +} + +# @brief Print a warning message to stderr. +sub WARN($) { +	my $str = shift; +	print STDERR $str; +} + +sub parse_command_line($) { +	my $opt = shift; +	my $result = GetOptions( "help"	     => \$$opt{'help'}, +				 "man"	     => \$$opt{'man'}, +				 "config=s"  => \$$opt{'config'}, +				 "verbose"   => \$$opt{'verbose'}, +			       ) or pod2usage(2); +	pod2usage(1) if defined ($$opt{help}); +	pod2usage(-verbose => 2) if defined ($$opt{man}); + +	$verbose = $$opt{verbose} if defined $$opt{verbose}; + +	if (!defined $$opt{config}) { +		ERR("[ ERROR: No config file specified. Aborting...\n"); +		exit 1; +	} + +} + +# @brief Check if all needed tools are in PATH. +sub check_tools { +	my $err = 0; +	my $key; + +	foreach $key (keys %tools) { +		if (`which $tools{$key}` eq "") { +			ERR("\n") if ($err == 0); +			ERR("! Please add the tool \'$tools{$key}\' " . +				"to your path!\n"); +			$err = 1; +		} +	} +	die "[ ERROR: Did not find all needed tools!\n" if $err; +} + +sub open_cfg_file($) { +	my $fname = shift; +	my $res = new Config::IniFiles( -file => $fname ); + +	die "[ ERROR: Cannot load your config file!\n" if (!defined $res); +	return $res; +} + +sub set_default($$$$) { +	my ($cfg, $section, $parameter, $def_value) = @_; +	$cfg->newval($section, $parameter, $def_value); +	return; +} + +sub generate_label($$$$) { +	my ($cfg, $section, $parameter, $def_value) = @_; +	my $new_label = $def_value . $section; +	$new_label .= "_" . get_date; +	$cfg->newval($section, $parameter, $new_label); +	return; +} + +# @brief   Converts any num to a unified hex string, i.e the resulting value +#	   always starts with "0x" and is aligned to 8 hexdigits. +# @return  Returns 0 on success, otherwise an error occured. +# +sub any_num_to_hex($$) { +	my $val = shift; +	my $res = shift; + +	# M(iB) +	if ($val =~ m/([0-9]+)[Mm][i]?[Bb]?/g) { +		$$res = sprintf("0x%08x", $1 * 1024 * 1024); +	} +	# k(iB) +	elsif ($val =~ m/([0-9]+)[kK][i]?[Bb]?/g) { +		$$res = sprintf("0x%08x", $1 * 1024); +	} +	# hex +	elsif ($val =~ m/0x?([0-9a-fA-F]+)/g) { +		$$res = sprintf("0x%08x", hex $1); +	} +	# decimal +	elsif ($val =~ m/^([0-9]+)$/g) { +		$$res = sprintf("0x%08x", $1); +	} +	else { +		$$res = ""; +		return -1; +	} + +	return 0; +} + +sub remove_spaces($$$) { +	my ($cfg, $section, $parameter) = @_; +	my ($start, @starts, @new_starts); +	my $val = $cfg->val($section, $parameter); +	my $res; + +	$val =~ s/ //g; # spaces +	$cfg->newval($section, $parameter, $val); +} + +sub expand_starts($$$) { +	my ($cfg, $section, $parameter) = @_; +	my ($start, @starts, @new_starts); +	my $val = $cfg->val($section, $parameter); +	my $res; + +	$val =~ s/ //g; # spaces +	@starts = split(/,/, $val); + +	foreach $start (@starts) { +		if (any_num_to_hex($start, \$res) != 0) { +			ERR("[ ERROR: [$section]\n"); +			ERR("[        Expecting a list of numeric " . +			    "values for parameter: $parameter\n"); +			exit 1; +		} +		push (@new_starts, $res); +	} +	$res = join(',', @starts); + +	$cfg->newval($section, $parameter, $res); +} + +sub check_id_list($$$) { +	my ($cfg, $section, $parameter) = @_; +	my $val = $cfg->val($section, $parameter); +	my $res; + +	if (!($val =~ m/^[0-9]+[,0-9]*/)) { +		ERR("[ ERROR: Syntax error in 'ubi_ids' in " . +		    "section '$section': $val\n"); +			ERR("[ Aborting... "); +			exit 1; +	} +} + +sub replace_type($$$) { +	my ($cfg, $section, $parameter) = @_; +	my $val = $cfg->val($section, $parameter); +	my $res; + +	$res = lc($val); +	grep {$res eq $_} ('static', 'dynamic') +	    or die "[ ERROR: Unknown UBI Volume Type in " . +	    "section '$section': $val\n"; + +	$cfg->newval($section, $parameter, $res); +} + + +sub replace_num($$$) { +	my ($cfg, $section, $parameter) = @_; +	my $val = $cfg->val($section, $parameter); +	my $res = ""; + +	if (any_num_to_hex($val, \$res) != 0) { +		ERR("[ ERROR: [$section]\n"); +		ERR("[        Expecting a numeric value " . +		    "for parameter: $parameter\n"); +		exit 1; +	} +	$cfg->newval($section, $parameter, $res); +} + +sub do_nothing($$$) { +	my ($cfg, $section, $parameter) = @_; +	return; +} + +sub bootenv_sanity_check($) { +	my $env = shift;	# hash array containing bootenv +	my %pdd = (); + +	defined($$env{'pdd'}) or return "'pdd' not defined"; +	foreach (split /,/, $$env{'pdd'}) { +		defined($$env{$_}) or return "undefined '$_' in pdd"; +		$pdd{$_} = 1; +	} + +	defined $$env{'pdd_preserve'} or +		return ""; +	foreach (split /,/, $$env{'pdd_preserve'}) { +		defined($pdd{$_}) +			or return "pdd_preserve field '$_' not in pdd"; +	} +	return ""; +} + +sub create_bootenv_image($$$) { +	my ($cfg, $section, $parameter) = @_; +	my $txt_fn = $cfg->val($section, "bootenv_file"); +	my $in; + +	my %value = (); +	my @key = (); + +	open $in, "<", $txt_fn +		or die "[ ERROR: can't open bootenv file '$txt_fn'.\n"; +	while (<$in>) { +		next if (/^\s*(\#.*)?$/); # Skip comments/whitespace. + +		if (/^(\S+?)\+\=(.*)$/) { +			defined($value{$1}) or +				die "$txt_fn:$.: error: appending to" . +					" non-existent '$1'\n"; +			$value{$1} .= $2; +		} elsif (/^(\S+?)\=(.*)$/) { +			not defined($value{$1}) or +				die "$txt_fn:$.: error: trying to" . +					" redefine '$1'\n"; +			push @key, $1; +			$value{$1} = $2; +		} else { +			die "$txt_fn:$.: error: unrecognized syntax\n"; +		} +	} +	close $in; + +	$_ = &bootenv_sanity_check(\%value) +		and die "$txt_fn: error: $_\n"; + +	my $tmp_file = new File::Temp(); +	push (@tmp_files, $tmp_file); + +	foreach (@key) { +		print $tmp_file "$_=", $value{$_}, "\0"; +	} +	close $tmp_file; + +	$cfg->newval($section, "image", $tmp_file-> filename); +} + +sub process_keys($$$) { +	my ($cfg, $section, $keys) = @_; +	my @parameters = $cfg->Parameters($section); +	my $i; + +	for ($i = 0 ; $i < scalar(@parameters) ; $i++ ) { +		if (defined($$keys{$parameters[$i]})) { +			$$keys{$parameters[$i]}->($cfg, $section, +					$parameters[$i]); +		} +	} + +} + +sub is_in_keylist($$) { +	my ($key, $keys) = @_; +	my $i; + +	for ($i = 0; $i < scalar(@$keys); $i++) { +		if ($$keys[$i] eq $key) { +			return 1; +		} +	} + +	return 0; +} + +sub check_default_keys($$$) { +	my ($cfg, $section, $keys) = @_; +	my @parameters = $cfg->Parameters($section); +	my $key; + +	foreach $key (keys %$keys) { +		if (!is_in_keylist($key, \@parameters)) { +			$$keys{$key}[0]-> +				($cfg, $section, $key, $$keys{$key}[1]); +		} +	} + +} + + + +sub check_keys($$$) { +	my ($cfg, $section, $keys) = @_; +	my @parameters = $cfg->Parameters($section); +	my ($i, $key, $err); + +	$err = 0; +	for ($i = 0 ; $i < scalar(@$keys) ; $i++ ) { +		if (!is_in_keylist($$keys[$i], \@parameters)) { +			ERR("[ ERROR: [$section]\n") if $err == 0; +			$err = 1; +			ERR("[        Missing key '$$keys[$i]'\n"); +		} +	} + +	if ($err) { +		ERR("[ Aborting...\n"); +		exit 1; +	} +} + +sub push_pfi_data($$$$$) { +	my ($cfg, $section, $pfi_infos, $keys, $mode) = @_; +	my ($tmp, $i, $hdr); + +	my %pfi_info = (); +	$pfi_info{'mode'} = $mode; +	$pfi_info{'image'} = $cfg->val($section, "image"); + +	# Build the PFI header +	$hdr  = sprintf("PFI!\n"); +	$hdr .= sprintf("version=0x%08x\n", hex $pfi_version); +	$hdr .= sprintf("mode=$mode\n"); + +	# calculate the size of the binary data part +	$tmp = -s $cfg->val($section, "image"); +	if (!defined $tmp) { +		ERR("[ ERROR: [$section]\n"); +		ERR("[        Missing input image: " +				. $cfg->val($section, "image") . "\n"); +		exit 1; +	} +	# Check for the image to fit into the given space +	my $quota; +	if ($mode eq 'raw') { +		$quota = oct $cfg->val($section, "raw_total_size"); +	} elsif ($mode eq 'ubi') { +		$quota = oct $cfg->val($section, "ubi_size"); +	} +	$tmp <= $quota +		or die "[ERROR: image file too big: " . +		$cfg->val($section, "image") . "\n"; +	$pfi_info{'size'} = $tmp; + +	$hdr .= sprintf("size=0x%08x\n", $tmp); + +	my $img_file = $cfg->val($section, "image"); +	my $crc32 = `$tools{'ubicrc32'} $img_file 2>&1`; +	if (any_num_to_hex($crc32, \$tmp) != 0) { +		die "[ ERROR: $tools{'ubicrc32'} returned with errors"; +	} +	$hdr .= sprintf("crc=$tmp\n"); + + +	# Process all remaining keys +	for ($i = 0; $i < scalar (@$keys); $i++) { +		if ($$keys[$i] eq "image") { # special case image input file +			if (! -e ($tmp = $cfg->val($section, "image"))) { +				ERR("[ ERROR: [$section]\n"); +				ERR("[        Cannot find input file $tmp\n"); +				exit 1; +			} +			next; +		} +		$hdr .= sprintf("%s=%s\n", $$keys[$i], +				$cfg->val($section, $$keys[$i])); +	} + +	$hdr .= sprintf("\n"); # end marker for PFI-header + +	$pfi_info{'header'} = $hdr; + +	# store in the header list +	push @$pfi_infos, \%pfi_info; +} + +sub process_section($$$$$$) { +	my ($cfg, $section, $pfi_infos, $custom_keys, +			$def_custom_keys, $mode) = @_; +	my @keys = (keys %common_keys, keys %$custom_keys); +	my @complete_keys = (@keys, keys %input_keys); + +	# set defaults if necessary +	check_default_keys($cfg, $section, $def_custom_keys); +	check_default_keys($cfg, $section, \%def_common_keys); + +	# check for placeholders... +	process_keys($cfg, $section, \%input_placeholder_keys); + +	# VALIDATE layout.cfg entries +	check_keys($cfg, $section, \@complete_keys); + +	# execute linked functions (if any) +	process_keys($cfg, $section, \%common_keys); +	process_keys($cfg, $section, $custom_keys); + +	push_pfi_data($cfg, $section, $pfi_infos, \@keys, $mode); +} + +sub get_section_info($$) { +	my ($cfg, $section) = @_; +	my @parameters = $cfg->Parameters($section); +	my ($ubi, $raw, $i, @res); + +	$ubi = $raw = 0; +	for ($i = 0 ; $i < scalar(@parameters) ; $i++ ) { +		if ($parameters[$i] =~ m/ubi_/gi) { +			$ubi = 1; +			@res = (\%ubi_keys, \%def_ubi_keys, "ubi"); +		} +		if ($parameters[$i] =~ m/raw_/gi) { +			$raw = 1; +			@res = (\%raw_keys, \%def_raw_keys, "raw"); +		} +	} + +	if (($ubi + $raw) != 1)	{ # double definition in section +		ERR("[ ERROR: Layout error in section '$section'\n"); +		exit 1; +	} + +	return @res; +} + +sub mk_target_list($$) { +	my $val = shift; +	my $tmp = shift; +	my $complete = 0; + +	if ($val =~ m/\((.*)\)/g) { +		$val = $1; +		$complete = 1; +	} +	$val =~ s/ //g; # spaces + +	@$tmp = split(/,/, $val); + +	return $complete; +} + +sub copy_bytes($$$) { +	my ($in, $out, $to_copy) = @_; + +	while ($to_copy) { +		my $buf; +		my $bufsize = 1024*1024; + +		$bufsize < $to_copy or $bufsize = $to_copy; +		read($in, $buf, $bufsize) == $bufsize +			or die "[ ERROR: Image file shrunk during operation\n"; +		print $out $buf; +		$to_copy -= $bufsize; +	} +} + +sub write_target($$) { +	my ($pfi_infos, $target) = @_; +	my ($pfi_info); + +	INFO("[ Writting target pfi file: '$target.pfi'...\n"); +	if (-e "$target.pfi") { +		WARN("! Replaced old pfi...\n"); +		`rm -f $target.pfi`; +	} +	open(FILE, ">", "$target.pfi") +		or die "[ ERROR: Cannot create output file: $target.pfi\n"; +	binmode(FILE); + +	# @FIXME sort by mode (first raw, then ubi) +	# Currently this ordering is based on a string comparism. :-) +	@$pfi_infos = sort {(lc $$a{'mode'}) cmp (lc $$b{'mode'})} @$pfi_infos; + +	# Print all headers first +	foreach $pfi_info (@$pfi_infos) { +		print FILE $$pfi_info{'header'}; + +	} +	# Print the linked data sections +	print FILE "DATA\n"; +	foreach $pfi_info (@$pfi_infos) { +		open(IMAGE, "<", $$pfi_info{'image'}) +				or die "[ ERROR: Cannot open input image: " . +				"$$pfi_info{'image'}" . "\n"; +		binmode(IMAGE); +		©_bytes(\*IMAGE, \*FILE, $$pfi_info{'size'}); +		close(IMAGE) or die "[ ERROR: Cannot close input image: " . +				"$$pfi_info{'image'}" . "\n"; +	} +	close(FILE) or die "[ ERROR: Cannot close output file: $target.pfi\n"; +} + +sub process_config($) { +	my $cfg = shift; +	my @sections = $cfg->Sections; +	my ($i, $j, $keylist, $def_keylist, $mode, $tmp, +			@tlist, $complete,@pfi_infos); + +	my @parameters = $cfg->Parameters("targets") or +		die "[ ERROR: Config file has no 'targets' section!\n"; + +	for ($i = 0 ; $i < scalar(@parameters) ; $i++ ) { +		INFO("[ Processing target '$parameters[$i]'...\n"); +		@pfi_infos = (); + +		# get a list of subtargets +		$complete = mk_target_list($cfg->val("targets", +					$parameters[$i]), \@tlist); +		# build all subtargets +		for ($j = 0 ; $j < scalar(@tlist) ; $j++ ) { +			($keylist, $def_keylist, $mode) +				= get_section_info($cfg, $tlist[$j]); +			process_section($cfg, $tlist[$j], +					\@pfi_infos, +					$keylist, $def_keylist, $mode); +		} + +		write_target(\@pfi_infos, $parameters[$i]); +	} + +	INFO("[ Success.\n"); + + +} + +sub clear_files() { +	# @FIXME: +	# Works implicitly and Fedora seems to have removed +	# the cleanup call. Thus for now, inactive. +	# File::Temp::cleanup(); +} + +require 5.008_000;		# Tested with version 5.8.0. +select STDOUT; $| = 1;		# make STDOUT output unbuffered +select STDERR; $| = 1;		# make STDERR output unbuffered + +parse_command_line(\%opts); +check_tools; +$cfg = open_cfg_file($opts{config}); +process_config($cfg); +clear_files; + +__END__ + + +=head1 NAME + +mkpfi - Using GetOpt::Long, Pod::Usage, Config::IniFiles + + +=head1 SYNOPSIS + +mkpfi  [OPTIONS ...] + + +	OPTION + +	[--config] [--help] [--man] + + +=head1 ABSTRACT + +Perl script for generating pdd pfi files from given config files. + +=head1 OPTIONS + +=over + +=item B<--help> + +Print out brief help message. + +=item B<--usage> + +Print usage. + +=item B<--config> + +Config input file. + +=item B<--man> + +Print manual page, same as 'perldoc mkpfi'. + +=item B<--verbose> + +Be verbose! + +=back + +=head1 BUGS + +Report via MTD mailing list + + +=head1 SEE ALSO + +http://www.linux-mtd.infradead.org/ + + +=head1 AUTHOR + +Oliver Lohmann (oliloh@de.ibm.com) + +=cut diff --git a/ubi-utils/src/nand2bin/nand2bin.c b/ubi-utils/src/nand2bin/nand2bin.c new file mode 100644 index 0000000..a728fb5 --- /dev/null +++ b/ubi-utils/src/nand2bin/nand2bin.c @@ -0,0 +1,327 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Frank Haverkamp + * + * An utility to decompose NAND images and strip OOB off. Not yet finished ... + */ +#include <config.h> +#include <argp.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdint.h> +#include <getopt.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include "nandecc.h" + +#define MAXPATH		1024 +#define MIN(x,y)	((x)<(y)?(x):(y)) + +struct args { +	const char *oob_file; +	const char *output_file; +	size_t pagesize; + +	/* special stuff needed to get additional arguments */ +	char *arg1; +	char **options;		/* [STRING...] */ +}; + +static struct args myargs = { +	.output_file = "data.bin", +	.oob_file = "oob.bin", +	.pagesize = 2048, +	.arg1 = NULL, +	.options = NULL, +}; + +static error_t parse_opt (int key, char *arg, struct argp_state *state); + +const char *argp_program_bug_address = "<haver@vnet.ibm.com>"; + +static char doc[] = "\nVersion: " VERSION "\n\t" +	HOST_OS" "HOST_CPU" at "__DATE__" "__TIME__"\n" +	"\nSplit data and OOB.\n"; + +static struct argp_option options[] = { +	{ .name = "pagesize", +	  .key = 'p', +	  .arg = "<pagesize>", +	  .flags = 0, +	  .doc = "NAND pagesize", +	  .group = OPTION_ARG_OPTIONAL }, + +	{ .name = "output", +	  .key = 'o', +	  .arg = "<output>", +	  .flags = 0, +	  .doc = "Data output file", +	  .group = OPTION_ARG_OPTIONAL }, + +	{ .name = "oob", +	  .key = 'O', +	  .arg = "<oob>", +	  .flags = 0, +	  .doc = "OOB output file", +	  .group = OPTION_ARG_OPTIONAL }, + +	{ .name = NULL, .key = 0, .arg = NULL, .flags = 0, +	  .doc = NULL, .group = 0 }, +}; + +static struct argp argp = { +	.options = options, +	.parser = parse_opt, +	.args_doc = "input.mif", +	.doc =	doc, +	.children = NULL, +	.help_filter = NULL, +	.argp_domain = NULL, +}; + +/* + * str_to_num - Convert string into number and cope with endings like + *              k, K, kib, KiB for kilobyte + *              m, M, mib, MiB for megabyte + */ +uint32_t str_to_num(char *str) +{ +	char *s = str; +	ulong num = strtoul(s, &s, 0); + +	if (*s != '\0') { +		if (strcmp(s, "KiB") == 0) +			num *= 1024; +		else if (strcmp(s, "MiB") == 0) +			num *= 1024*1024; +		else { +			fprintf(stderr, "WARNING: Wrong number format " +				"\"%s\", check your paramters!\n", str); +		} +	} +	return num; +} + +/* + * @brief Parse the arguments passed into the test case. + * + * @param key            The parameter. + * @param arg            Argument passed to parameter. + * @param state          Location to put information on parameters. + * + * @return error + * + * Get the `input' argument from `argp_parse', which we know is a + * pointer to our arguments structure. + */ +static error_t +parse_opt(int key, char *arg, struct argp_state *state) +{ +	struct args *args = state->input; + +	switch (key) { +	case 'p': /* --pagesize<pagesize> */ +		args->pagesize = str_to_num(arg);		break; + +	case 'o': /* --output=<output.bin> */ +		args->output_file = arg; +		break; + +	case 'O': /* --oob=<oob.bin> */ +		args->output_file = arg; +		break; + +	case ARGP_KEY_NO_ARGS: +		/* argp_usage(state); */ +		break; + +	case ARGP_KEY_ARG: +		args->arg1 = arg; +		/* Now we consume all the rest of the arguments. +                   `state->next' is the index in `state->argv' of the +                   next argument to be parsed, which is the first STRING +                   we're interested in, so we can just use +                   `&state->argv[state->next]' as the value for +                   arguments->strings. + +                   _In addition_, by setting `state->next' to the end +                   of the arguments, we can force argp to stop parsing here and +                   return. */ + +		args->options = &state->argv[state->next]; +		state->next = state->argc; +		break; + +	case ARGP_KEY_END: +		/* argp_usage(state); */ +		break; + +	default: +		return(ARGP_ERR_UNKNOWN); +	} + +	return 0; +} + +static int calc_oobsize(size_t pagesize) +{ +	switch (pagesize) { +	case 512:  return 16; +	case 2048: return 64; +	default: +		exit(EXIT_FAILURE); +	} +	return 0; +} + +static inline void +hexdump(FILE *fp, const uint8_t *buf, size_t size) +{ +	int k; + +	for (k = 0; k < size; k++) { +		fprintf(fp, "%02x ", buf[k]); +		if ((k & 15) == 15) +			fprintf(fp, "\n"); +	} +} + +static int +process_page(uint8_t* buf, uint8_t *oobbuf, size_t pagesize) +{ +	int eccpoi, oobsize; +	size_t i; + +	switch (pagesize) { +	case 2048: oobsize = 64; eccpoi = 64 / 2; break; +	case 512:  oobsize = 16; eccpoi = 16 / 2; break; +	default: +		fprintf(stderr, "Unsupported page size: %d\n", pagesize); +		return -EINVAL; +	} +	memset(oobbuf, 0xff, oobsize); + +	for (i = 0; i < pagesize; i += 256, eccpoi += 3) { +		oobbuf[eccpoi++] = 0x0; +		/* Calculate ECC */ +		nand_calculate_ecc(&buf[i], &oobbuf[eccpoi]); +	} +	return 0; +} + +static int decompose_image(FILE *in_fp, FILE *bin_fp, FILE *oob_fp, +			   size_t pagesize) +{ +	int read, rc, page = 0; +	size_t oobsize = calc_oobsize(pagesize); +	uint8_t *buf = malloc(pagesize); +	uint8_t *oob = malloc(oobsize); +	uint8_t *calc_oob = malloc(oobsize); +	uint8_t *calc_buf = malloc(pagesize); + +	if (!buf) +		exit(EXIT_FAILURE); +	if (!oob) +		exit(EXIT_FAILURE); +	if (!calc_oob) +		exit(EXIT_FAILURE); +	if (!calc_buf) +		exit(EXIT_FAILURE); + +	while (!feof(in_fp)) { +		read = fread(buf, 1, pagesize, in_fp); +		if (ferror(in_fp)) { +			fprintf(stderr, "I/O Error."); +			exit(EXIT_FAILURE); +		} +		rc = fwrite(buf, 1, pagesize, bin_fp); +		if (ferror(bin_fp)) { +			fprintf(stderr, "I/O Error."); +			exit(EXIT_FAILURE); +		} +		read = fread(oob, 1, oobsize, in_fp); +		if (ferror(in_fp)) { +			fprintf(stderr, "I/O Error."); +			exit(EXIT_FAILURE); +		} +		rc = fwrite(oob, 1, oobsize, oob_fp); +		if (ferror(bin_fp)) { +			fprintf(stderr, "I/O Error."); +			exit(EXIT_FAILURE); +		} +		process_page(buf, calc_oob, pagesize); + +		memcpy(calc_buf, buf, pagesize); + +		rc = nand_correct_data(calc_buf, oob); +		if ((rc != -1) || (memcmp(buf, calc_buf, pagesize) != 0)) { +			fprintf(stdout, "Page %d: data does not match!\n", +				page); +		} +		page++; +	} +	free(calc_buf); +	free(calc_oob); +	free(oob); +	free(buf); +	return 0; +} + +int +main(int argc, char *argv[]) +{ +	FILE *in, *bin, *oob; + +	argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &myargs); + +	if (!myargs.arg1) { +		fprintf(stderr, "Please specify input file!\n"); +		exit(EXIT_FAILURE); +	} + +	in = fopen(myargs.arg1, "r"); +	if (!in) { +		perror("Cannot open file"); +		exit(EXIT_FAILURE); +	} +	bin = fopen(myargs.output_file, "w+"); +	if (!bin) { +		perror("Cannot open file"); +		exit(EXIT_FAILURE); +	} +	oob = fopen(myargs.oob_file, "w+"); +	if (!oob) { +		perror("Cannot open file"); +		exit(EXIT_FAILURE); +	} + +	decompose_image(in, bin, oob, myargs.pagesize); + +	fclose(in); +	fclose(bin); +	fclose(oob); + +	exit(EXIT_SUCCESS); +} diff --git a/ubi-utils/src/nand2bin/nandcorr.c b/ubi-utils/src/nand2bin/nandcorr.c new file mode 100644 index 0000000..7f1a547 --- /dev/null +++ b/ubi-utils/src/nand2bin/nandcorr.c @@ -0,0 +1,85 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * ECC algorithm for NAND FLASH. Detects and corrects 1 bit errors in + * a 256 bytes of data. + * + * Reimplement by Thomas Gleixner after staring long enough at the + * mess in drivers/mtd/nand/nandecc.c + * + */ + +#include "nandecc.h" + +static int countbits(uint32_t byte) +{ +	int res = 0; + +	for (;byte; byte >>= 1) +		res += byte & 0x01; +	return res; +} + +int nand_correct_data(uint8_t *dat, const uint8_t *fail_ecc) + +{ +	uint8_t s0, s1, s2; + +	/* +	 * Do error detection +	 * +	 * Be careful, the index magic is due to a pointer to a +	 * uint32_t. +	 */ +	s0 = fail_ecc[1]; +	s1 = fail_ecc[2]; +	s2 = fail_ecc[3]; + +	/* Check for a single bit error */ +	if( ((s0 ^ (s0 >> 1)) & 0x55) == 0x55 && +	    ((s1 ^ (s1 >> 1)) & 0x55) == 0x55 && +	    ((s2 ^ (s2 >> 1)) & 0x54) == 0x54) { + +		uint32_t byteoffs, bitnum; + +		byteoffs = (s1 << 0) & 0x80; +		byteoffs |= (s1 << 1) & 0x40; +		byteoffs |= (s1 << 2) & 0x20; +		byteoffs |= (s1 << 3) & 0x10; + +		byteoffs |= (s0 >> 4) & 0x08; +		byteoffs |= (s0 >> 3) & 0x04; +		byteoffs |= (s0 >> 2) & 0x02; +		byteoffs |= (s0 >> 1) & 0x01; + +		bitnum = (s2 >> 5) & 0x04; +		bitnum |= (s2 >> 4) & 0x02; +		bitnum |= (s2 >> 3) & 0x01; + +		dat[byteoffs] ^= (1 << bitnum); + +		return 1; +	} + +	if(countbits(s0 | ((uint32_t)s1 << 8) | ((uint32_t)s2 <<16)) == 1) +		return 1; + +	return -1; +} + diff --git a/ubi-utils/src/pddcustomize/pddcustomize.c b/ubi-utils/src/pddcustomize/pddcustomize.c new file mode 100644 index 0000000..f71d916 --- /dev/null +++ b/ubi-utils/src/pddcustomize/pddcustomize.c @@ -0,0 +1,496 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Oliver Lohmann + * + * PDD (platform description data) contains a set of system specific + * boot-parameters. Some of those parameters need to be handled + * special on updates, e.g. the MAC addresses. They must also be kept + * if the system is updated and one must be able to modify them when + * the system has booted the first time. This tool is intended to do + * PDD modification. + */ + +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <getopt.h> +#include <argp.h> +#include <unistd.h> +#include <errno.h> +#include <mtd/ubi-header.h> + +#include "config.h" +#include "bootenv.h" +#include "error.h" +#include "example_ubi.h" +#include "libubi.h" +#include "ubimirror.h" + +typedef enum action_t { +	ACT_NORMAL   = 0, +	ACT_LIST, +	ACT_ARGP_ABORT, +	ACT_ARGP_ERR, +} action_t; + +#define ABORT_ARGP do {			\ +	state->next = state->argc;	\ +	args->action = ACT_ARGP_ABORT;	\ +} while (0) + +#define ERR_ARGP do {			\ +	state->next = state->argc;	\ +	args->action = ACT_ARGP_ERR;	\ +} while (0) + +const char *argp_program_version = PACKAGE_VERSION; +const char *argp_program_bug_address = PACKAGE_BUGREPORT; +static char doc[] = "\nVersion: " PACKAGE_VERSION "\n\tBuilt on " +	BUILD_CPU" "BUILD_OS" at "__DATE__" "__TIME__"\n" +	"\n" +	"pddcustomize - customize bootenv and pdd values.\n"; + +static const char copyright [] __attribute__((unused)) = +	"FIXME: insert license type"; /* FIXME */ + +static struct argp_option options[] = { +	{ name: "copyright", key: 'c', arg: NULL,    flags: 0, +	  doc: "Print copyright information.", +	  group: 1 }, + +	{ name: "input", key: 'i', arg: "<input>",    flags: 0, +	  doc: "Binary input file. For debug purposes.", +	  group: 1 }, + +	{ name: "output", key: 'o', arg: "<output>",	flags: 0, +	  doc: "Binary output file. For debug purposes.", +	  group: 1 }, + +	{ name: "list", key: 'l', arg: NULL,	flags: 0, +	  doc: "List card bootenv/pdd values.", +	  group: 1 }, + +	{ name: "both", key: 'b', arg: NULL,	flags: 0, +	  doc: "Mirror updated PDD to redundand copy.", +	  group: 1 }, + +	{ name: "side", key: 's', arg: "<seqnum>",    flags: 0, +	  doc: "The side/seqnum to update.", +	  group: 1 }, + +	{ name: "host", key: 'x', arg: NULL,	flags: 0, +	  doc: "use x86 platform for debugging.", +	  group: 1 }, + +	{ name: NULL, key: 0, arg: NULL, flags: 0, doc: NULL, group: 0 }, +}; + +typedef struct myargs { +	action_t action; +	const char* file_in; +	const char* file_out; +	int both; +	int side; +	int x86;		/* X86 host, use files for testing */ +	bootenv_t env_in; + +	char *arg1; +	char **options;		/* [STRING...] */ +} myargs; + +static int +get_update_side(const char* str) +{ +	uint32_t i = strtoul(str, NULL, 0); + +	if ((i != 0) && (i != 1)) { +		return -1; +	} + +	return i; +} + +static int +extract_pair(bootenv_t env, const char* str) +{ +	int rc = 0; +	char* key; +	char* val; + +	key = strdup(str); +	if (key == NULL) +		return -ENOMEM; + +	val = strstr(key, "="); +	if (val == NULL) { +		err_msg("Wrong argument: %s\n" +			"Expecting key=value pair.\n", str); +		rc = -1; +		goto err; +	} + +	*val = '\0'; /* split strings */ +	val++; +	rc = bootenv_set(env, key, val); + +err: +	free(key); +	return rc; +} + +static error_t +parse_opt(int key, char *arg, struct argp_state *state) +{ +	int rc = 0; +	int err = 0; + +	myargs *args = state->input; + +	switch (key) { +	case 'c': +		err_msg("%s\n", copyright); +		ABORT_ARGP; +		break; +	case 'l': +		args->action = ACT_LIST; +		break; +	case 'b': +		args->both = 1; +		break; +	case 'x': +		args->x86 = 1; +		break; +	case 's': +		args->side = get_update_side(arg); +		if (args->side < 0) { +			err_msg("Unsupported seqnum: %d.\n" +				"Supported seqnums are '0' and '1'\n", +				args->side, arg); +			ERR_ARGP; +		} +		break; +	case 'i': +		args->file_in = arg; +		break; +	case 'o': +		args->file_out = arg; +		break; +	case ARGP_KEY_ARG: +		rc = extract_pair(args->env_in, arg); +		if (rc != 0) +			ERR_ARGP; +		break; +	case ARGP_KEY_END: +		if (err) { +			err_msg("\n"); +			argp_usage(state); +			ERR_ARGP; +		} +		break; +	default: +		return(ARGP_ERR_UNKNOWN); +	} + +	return 0; +} + +static struct argp argp = { +	options:     options, +	parser:	     parse_opt, +	args_doc:    "[key=value] [...]", +	doc:	     doc, +	children:    NULL, +	help_filter: NULL, +	argp_domain: NULL, +}; + + +static int +list_bootenv(bootenv_t env) +{ +	int rc = 0; +	rc = bootenv_write_txt(stdout, env); +	if (rc != 0) { +		err_msg("Cannot list bootenv/pdd. rc: %d\n", rc); +		goto err; +	} +err: +	return rc; +} + +static int +process_key_value(bootenv_t env_in, bootenv_t env) +{ +	int rc = 0; +	size_t size, i; +	const char* tmp; +	const char** key_vec = NULL; + +	rc = bootenv_get_key_vector(env_in, &size, 0, &key_vec); +	if (rc != 0) +		goto err; + +	for (i = 0; i < size; i++) { +		rc = bootenv_get(env_in, key_vec[i], &tmp); +		if (rc != 0) { +			err_msg("Cannot read value to input key: %s. rc: %d\n", +					key_vec[i], rc); +			goto err; +		} +		rc = bootenv_set(env, key_vec[i], tmp); +		if (rc != 0) { +			err_msg("Cannot set value key: %s. rc: %d\n", +					key_vec[i], rc); +			goto err; +		} +	} + +err: +	if (key_vec != NULL) +		free(key_vec); +	return rc; +} + +static int +read_bootenv(const char* file, bootenv_t env) +{ +	int rc = 0; +	FILE* fp_in = NULL; + +	fp_in = fopen(file, "rb"); +	if (fp_in == NULL) { +		err_msg("Cannot open file: %s\n", file); +		return -EIO; +	} + +	rc = bootenv_read(fp_in, env, BOOTENV_MAXSIZE); +	if (rc != 0) { +		err_msg("Cannot read bootenv from file %s. rc: %d\n", +			file, rc); +		goto err; +	} + +err: +	fclose(fp_in); +	return rc; +} + +/* + * Read bootenv from ubi volume + */ +static int +ubi_read_bootenv(uint32_t devno, uint32_t id, bootenv_t env) +{ +	ubi_lib_t ulib = NULL; +	int rc = 0; +	FILE* fp_in = NULL; + +	rc = ubi_open(&ulib); +	if( rc ){ +		err_msg("Cannot allocate ubi structure\n"); +		return rc; +	} + +	fp_in = ubi_vol_fopen_read(ulib, devno, id); +	if (fp_in == NULL) { +		err_msg("Cannot open volume:%d number:%d\n", devno, id); +		goto err; +	} + +	rc = bootenv_read(fp_in, env, BOOTENV_MAXSIZE); +	if (rc != 0) { +		err_msg("Cannot read volume:%d number:%d\n", devno, id); +		goto err; +	} + +err: +	if( fp_in ) +		fclose(fp_in); +	ubi_close(&ulib); +	return rc; +} + +static int +write_bootenv(const char* file, bootenv_t env) +{ +	int rc = 0; +	FILE* fp_out; + +	fp_out = fopen(file, "wb"); +	if (fp_out == NULL) { +		err_msg("Cannot open file: %s\n", file); +		return -EIO; +	} + +	rc = bootenv_write(fp_out, env); +	if (rc != 0) { +		err_msg("Cannot write bootenv to file %s. rc: %d\n", file, rc); +		goto err; +	} + +err: +	fclose(fp_out); +	return rc; +} + +/* + * Read bootenv from ubi volume + */ +static int +ubi_write_bootenv(uint32_t devno, uint32_t id, bootenv_t env) +{ +	ubi_lib_t ulib = NULL; +	int rc = 0; +	FILE* fp_out; +	size_t nbytes ; + +	rc = bootenv_size(env, &nbytes); +	if( rc ){ +		err_msg("Cannot determine size of bootenv structure\n"); +		return rc; +	} +	rc = ubi_open(&ulib); +	if( rc ){ +		err_msg("Cannot allocate ubi structure\n"); +		return rc; +	} +	fp_out = ubi_vol_fopen_update(ulib, devno, id, +			(unsigned long long)nbytes); +	if (fp_out == NULL) { +		err_msg("Cannot open volume:%d number:%d\n", devno, id); +		goto err; +	} + +	rc = bootenv_write(fp_out, env); +	if (rc != 0) { +		err_msg("Cannot write bootenv to volume %d number:%d\n", +			devno, id); +		goto err; +	} + +err: +	if( fp_out ) +		fclose(fp_out); +	ubi_close(&ulib); +	return rc; +} + +static int +do_mirror(int volno) +{ +	char errbuf[1024]; +	uint32_t ids[2]; +	int rc; +	int src_volno_idx = 0; + +	ids[0] = EXAMPLE_BOOTENV_VOL_ID_1; +	ids[1] = EXAMPLE_BOOTENV_VOL_ID_2; + +	if (volno == EXAMPLE_BOOTENV_VOL_ID_2) +		src_volno_idx = 1; + +	rc = ubimirror(EXAMPLE_UBI_DEVICE, src_volno_idx, ids, 2, errbuf, +		       sizeof errbuf); +	if( rc ) +		err_msg(errbuf); +	return rc; +} + +int +main(int argc, char **argv) { +	int rc = 0; +	bootenv_t env = NULL; +	uint32_t boot_volno; +	myargs args = { +		.action = ACT_NORMAL, +		.file_in  = NULL, +		.file_out = NULL, +		.side = -1, +		.x86 = 0, +		.both = 0, +		.env_in = NULL, + +		.arg1 = NULL, +		.options = NULL, +	}; + +	rc = bootenv_create(&env); +	if (rc != 0) { +		err_msg("Cannot create bootenv handle. rc: %d", rc); +		goto err; +	} + +	rc = bootenv_create(&(args.env_in)); +	if (rc != 0) { +		err_msg("Cannot create bootenv handle. rc: %d", rc); +		goto err; +	} + +	argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &args); +	if (args.action == ACT_ARGP_ERR) { +		rc = -1; +		goto err; +	} +	if (args.action == ACT_ARGP_ABORT) { +		rc = 0; +		goto out; +	} + +	if ((args.side == 0) || (args.side == -1)) +		boot_volno = EXAMPLE_BOOTENV_VOL_ID_1; +	else +		boot_volno = EXAMPLE_BOOTENV_VOL_ID_2; + +	if( args.x86 ) +		rc = read_bootenv(args.file_in, env); +	else +		rc = ubi_read_bootenv(EXAMPLE_UBI_DEVICE, boot_volno, env); +	if (rc != 0) { +		goto err; +	} + +	if (args.action == ACT_LIST) { +		rc = list_bootenv(env); +		if (rc != 0) { +			goto err; +		} +		goto out; +	} + +	rc = process_key_value(args.env_in, env); +	if (rc != 0) { +		goto err; +	} + +	if( args.x86 ) +		rc = write_bootenv(args.file_in, env); +	else +		rc = ubi_write_bootenv(EXAMPLE_UBI_DEVICE, boot_volno, env); +	if (rc != 0) { +		goto err; +	} +	if( args.both )		/* No side specified, update both */ +		rc = do_mirror(boot_volno); + + out: + err: +	bootenv_destroy(&env); +	bootenv_destroy(&(args.env_in)); +	return rc; +} diff --git a/ubi-utils/src/pfi2bin/pfi2bin.c b/ubi-utils/src/pfi2bin/pfi2bin.c new file mode 100644 index 0000000..6536c19 --- /dev/null +++ b/ubi-utils/src/pfi2bin/pfi2bin.c @@ -0,0 +1,678 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Oliver Lohmann + * + * Convert a PFI file (partial flash image) into a plain binary file. + * This tool can be used to prepare the data to be burned into flash + * chips in a manufacturing step where the flashes are written before + * being soldered onto the hardware. For NAND images another step is + * required to add the right OOB data to the binary image. + */ + +#include <stdlib.h> +#include <stdint.h> +#include <getopt.h> +#include <stdio.h> +#include <argp.h> +#include <string.h> +#include <assert.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 MAX_FNAME 255 +#define DEFAULT_ERASE_COUNT  0 /* Hmmm.... Perhaps */ +#define ERR_BUF_SIZE 1024 + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) + +static uint32_t crc32_table[256]; +static char err_buf[ERR_BUF_SIZE]; + +/* + * Data used to buffer raw blocks which have to be + * located at a specific point inside the generated RAW file + */ + +typedef enum action_t { +	ACT_NOTHING   = 0x00000000, +	ACT_RAW	   = 0x00000001, +} action_t; + +static const char copyright [] __attribute__((unused)) = +	"Licensed Materials - Property of IBM\n" +	"IBM Flexible Support Processor Licensed Material\n" +	"(c) Copyright IBM Corp 2006 All Rights Reserved.\n" +	"US Government Users Restricted Rights - Use, duplication\n" +	"or disclosure restricted by GSA ADP Schedule Contract\n" +	"with IBM Corp."; + +static error_t parse_opt (int key, char *arg, struct argp_state *state); + +const char *argp_program_version = PACKAGE_VERSION; +const char *argp_program_bug_address = PACKAGE_BUGREPORT; +static char doc[] = "\nVersion: " PACKAGE_VERSION "\n\tBuilt on " +	BUILD_CPU" "BUILD_OS" at "__DATE__" "__TIME__"\n" +	"\n" +	"pfi2bin - a tool to convert PFI files into binary images.\n"; + +static struct argp_option options[] = { +	/* COMMON */ +	{ name: NULL, key: 0, arg: NULL, flags: 0, +	  doc: "Common settings:", +	  group: OPTION_ARG_OPTIONAL}, + +	{ name: "verbose", key: 'v', arg: NULL,	   flags: 0, +	  doc: "Print more information.", +	  group: OPTION_ARG_OPTIONAL }, + +	{ name: "copyright", key: 'c', arg: NULL, flags: 0, +	  group: OPTION_ARG_OPTIONAL }, + + +	/* INPUT */ +	{ name: NULL, key: 0, arg: NULL, flags: 0, +	  doc: "Input:", +	  group: 4}, + +	{ name: "platform", key: 'j', arg: "pdd-file", flags: 0, +	  doc: "PDD information which contains the card settings.", +	  group: 4 }, + +	/* OUTPUT */ +	{ name: NULL, key: 0, arg: NULL, flags: 0, +	  doc: "Output:", +	  group: 5}, + +	{ name: "output", key: 'o', arg: "filename", flags: 0, +	  doc: "Outputfile, default: stdout.", +	  group: 5 }, + +	{ name: NULL, key: 0, arg: NULL, flags: 0, doc: NULL, group: 0 }, +}; + +typedef struct io { +	FILE* fp_pdd;	/* a FilePointer to the PDD data */ +	FILE* fp_pfi;	/* a FilePointer to the PFI input stream */ +	FILE* fp_out;	/* a FilePointer to the output stream */ +} *io_t; + +typedef struct myargs { +	/* common settings */ +	action_t action; +	int verbose; +	const char *f_in_pfi; +	const char *f_in_pdd; +	const char *f_out; + +	/* special stuff needed to get additional arguments */ +	char *arg1; +	char **options;			/* [STRING...] */ +} myargs; + +static struct argp argp = { +	options:     options, +	parser:	     parse_opt, +	args_doc:    "pfifile", +	doc:	     doc, +	children:    NULL, +	help_filter: NULL, +	argp_domain: NULL, +}; + +static error_t +parse_opt(int key, char *arg, struct argp_state *state) +{ +	myargs *args = state->input; + +	switch (key) { +		/* common settings */ +	case 'v': /* --verbose=<level> */ +		args->verbose = 1; +		break; + +	case 'c': /* --copyright */ +		fprintf(stderr, "%s\n", copyright); +		exit(0); +		break; + +	case 'j': /* --platform */ +		args->f_in_pdd = arg; +		break; + +	case 'o': /* --output */ +		args->f_out = arg; +		break; + +	case ARGP_KEY_ARG: +		args->f_in_pfi = arg; +		/* args->arg1 = arg; */ +		args->options = &state->argv[state->next]; +		state->next = state->argc; +		break; + +	case ARGP_KEY_END: +		if (args->action == ACT_NOTHING) { +			argp_usage(state); +			exit(1); +		} +		break; + +	default: +		return(ARGP_ERR_UNKNOWN); +	} + +	return 0; +} + + +static size_t +byte_to_blk(size_t byte, size_t blk_size) +{ +	return	(byte % blk_size) == 0 +		? byte / blk_size +		: byte / blk_size + 1; +} + + + + +/** + * @precondition  IO: File stream points to first byte of RAW data. + * @postcondition IO: File stream points to first byte of next + *		      or EOF. + */ +static int +memorize_raw_eb(pfi_raw_t pfi_raw, pdd_data_t pdd, list_t *raw_pebs, +		io_t io) +{ +	int rc = 0; +	uint32_t i; + +	size_t read, to_read, eb_num; +	size_t bytes_left; +	list_t pebs = *raw_pebs; +	peb_t	peb  = NULL; + +	long old_file_pos = ftell(io->fp_pfi); +	for (i = 0; i < pfi_raw->starts_size; i++) { +		bytes_left = pfi_raw->data_size; +		rc = fseek(io->fp_pfi, old_file_pos, SEEK_SET); +		if (rc != 0) +			goto err; + +		eb_num = byte_to_blk(pfi_raw->starts[i], pdd->eb_size); +		while (bytes_left) { +			to_read = MIN(bytes_left, pdd->eb_size); +			rc = peb_new(eb_num++, pdd->eb_size, &peb); +			if (rc != 0) +				goto err; +			read = fread(peb->data, 1, to_read, io->fp_pfi); +			if (read != to_read) { +				rc = -EIO; +				goto err; +			} +			pebs = append_elem(peb, pebs); +			bytes_left -= read; +		} + +	} +	*raw_pebs = pebs; +	return 0; +err: +	pebs = remove_all((free_func_t)&peb_free, pebs); +	return rc; +} + +static int +convert_ubi_volume(pfi_ubi_t ubi, pdd_data_t pdd, list_t raw_pebs, +		struct ubi_vol_tbl_record *vol_tab, +		size_t *ebs_written, io_t io) +{ +	int rc = 0; +	uint32_t i, j; +	peb_t raw_peb; +	peb_t cmp_peb; +	ubi_info_t u; +	size_t leb_total = 0; +	uint8_t vol_type; + +	switch (ubi->type) { +	case pfi_ubi_static: +		vol_type = UBI_VID_STATIC; break; +	case pfi_ubi_dynamic: +		vol_type = UBI_VID_DYNAMIC; break; +	default: +		vol_type = UBI_VID_DYNAMIC; +	} + +	rc = peb_new(0, 0, &cmp_peb); +	if (rc != 0) +		goto err; + +	long old_file_pos = ftell(io->fp_pfi); +	for (i = 0; i < ubi->ids_size; i++) { +		rc = fseek(io->fp_pfi, old_file_pos, SEEK_SET); +		if (rc != 0) +			goto err; +		rc = ubigen_create(&u, ubi->ids[i], vol_type, +				   pdd->eb_size, DEFAULT_ERASE_COUNT, +				   ubi->alignment, UBI_VERSION, +				   pdd->vid_hdr_offset, 0, ubi->data_size, +				   io->fp_pfi, io->fp_out); +		if (rc != 0) +			goto err; + +		rc = ubigen_get_leb_total(u, &leb_total); +		if (rc != 0) +			goto err; + +		j = 0; +		while(j < leb_total) { +			cmp_peb->num = *ebs_written; +			raw_peb = is_in((cmp_func_t)peb_cmp, cmp_peb, +					raw_pebs); +			if (raw_peb) { +				rc = peb_write(io->fp_out, raw_peb); +			} +			else { +				rc = ubigen_write_leb(u, NO_ERROR); +				j++; +			} +			if (rc != 0) +				goto err; +			(*ebs_written)++; +		} +		/* memorize volume table entry */ +		rc = ubigen_set_lvol_rec(u, ubi->size, +				ubi->names[i], +				(void*) &vol_tab[ubi->ids[i]]); +		if (rc != 0) +			goto err; +		ubigen_destroy(&u); +	} + +	peb_free(&cmp_peb); +	return 0; + +err: +	peb_free(&cmp_peb); +	ubigen_destroy(&u); +	return rc; +} + + +static FILE* +my_fmemopen (void *buf, size_t size, const char *opentype) +{ +    FILE* f; + +    assert(strcmp(opentype, "r") == 0); + +    f = tmpfile(); +    fwrite(buf, 1, size, f); +    rewind(f); + +    return f; +} + +/** + * @brief		Builds a UBI volume table from a volume entry list. + * @return 0		On success. + *	   else		Error. + */ +static int +write_ubi_volume_table(pdd_data_t pdd, list_t raw_pebs, +		struct ubi_vol_tbl_record *vol_tab, size_t vol_tab_size, +		size_t *ebs_written, io_t io) +{ +	int rc = 0; +	ubi_info_t u; +	peb_t raw_peb; +	peb_t cmp_peb; +	size_t leb_size, leb_total, j = 0; +	uint8_t *ptr = NULL; +	FILE* fp_leb = NULL; + +	rc = peb_new(0, 0, &cmp_peb); +	if (rc != 0) +		goto err; + +	/* @FIXME: Artem creates one volume with 2 LEBs. +	 * IMO 2 volumes would be more convenient. In order +	 * to get 2 reserved LEBs from ubigen, I have to +	 * introduce this stupid mechanism. Until no final +	 * decision of the VTAB structure is made... Good enough. +	 */ +	rc = ubigen_create(&u, UBI_LAYOUT_VOL_ID, UBI_VID_DYNAMIC, +			   pdd->eb_size, DEFAULT_ERASE_COUNT, +			   1, UBI_VERSION, +			   pdd->vid_hdr_offset, UBI_COMPAT_REJECT, +			   vol_tab_size, stdin, io->fp_out); +			   /* @FIXME stdin for fp_in is a hack */ +	if (rc != 0) +		goto err; +	rc = ubigen_get_leb_size(u, &leb_size); +	if (rc != 0) +		goto err; +	ubigen_destroy(&u); + +	ptr = (uint8_t*) malloc(leb_size * sizeof(uint8_t)); +	if (ptr == NULL) +		goto err; +	memset(ptr, 0xff, leb_size); +	memcpy(ptr, vol_tab, vol_tab_size); +	fp_leb = my_fmemopen(ptr, leb_size, "r"); + +	rc = ubigen_create(&u, UBI_LAYOUT_VOL_ID, UBI_VID_DYNAMIC, +			   pdd->eb_size, DEFAULT_ERASE_COUNT, +			   1, UBI_VERSION, pdd->vid_hdr_offset, +			   UBI_COMPAT_REJECT, leb_size * UBI_LAYOUT_VOLUME_EBS, +			   fp_leb, io->fp_out); +	if (rc != 0) +		goto err; +	rc = ubigen_get_leb_total(u, &leb_total); +	if (rc != 0) +		goto err; + +	long old_file_pos = ftell(fp_leb); +	while(j < leb_total) { +		rc = fseek(fp_leb, old_file_pos, SEEK_SET); +		if (rc != 0) +			goto err; + +		cmp_peb->num = *ebs_written; +		raw_peb = is_in((cmp_func_t)peb_cmp, cmp_peb, +				raw_pebs); +		if (raw_peb) { +			rc = peb_write(io->fp_out, raw_peb); +		} +		else { +			rc = ubigen_write_leb(u, NO_ERROR); +			j++; +		} + +		if (rc != 0) +			goto err; +		(*ebs_written)++; +	} + +err: +	free(ptr); +	peb_free(&cmp_peb); +	ubigen_destroy(&u); +	fclose(fp_leb); +	return rc; +} + +static int +write_remaining_raw_ebs(pdd_data_t pdd, list_t raw_blocks, size_t *ebs_written, +			FILE* fp_out) +{ +	int rc = 0; +	uint32_t j, delta; +	list_t ptr; +	peb_t empty_eb, peb; + +	/* create an empty 0xff EB (for padding) */ +	rc = peb_new(0, pdd->eb_size, &empty_eb); + +	foreach(peb, ptr, raw_blocks) { +		if (peb->num < *ebs_written) { +			continue; /* omit blocks which +				     are already passed */ +		} + +		if (peb->num < *ebs_written) { +			err_msg("eb_num: %d\n", peb->num); +			err_msg("Bug: This should never happen. %d %s", +				__LINE__, __FILE__); +			goto err; +		} + +		delta = peb->num - *ebs_written; +		if (((delta + *ebs_written) * pdd->eb_size) > pdd->flash_size) { +			err_msg("RAW block outside of flash_size."); +			goto err; +		} +		for (j = 0; j < delta; j++) { +			rc = peb_write(fp_out, empty_eb); +			if (rc != 0) +				goto err; +			(*ebs_written)++; +		} +		rc = peb_write(fp_out, peb); +		if (rc != 0) +			goto err; +		(*ebs_written)++; +	} + +err: +	peb_free(&empty_eb); +	return rc; +} + +static int +init_vol_tab(struct ubi_vol_tbl_record **vol_tab, size_t *vol_tab_size) +{ +	uint32_t crc; +	size_t i; +	struct ubi_vol_tbl_record* res = NULL; + +	*vol_tab_size = UBI_MAX_VOLUMES * UBI_VTBL_RECORD_SIZE; + +	res = (struct ubi_vol_tbl_record*) calloc(1, *vol_tab_size); +	if (vol_tab == NULL) { +		return -ENOMEM; +	} + +	for (i = 0; i < UBI_MAX_VOLUMES; i++) { +		crc = clc_crc32(crc32_table, UBI_CRC32_INIT, +			&(res[i]), UBI_VTBL_RECORD_SIZE_CRC); +		res[i].crc = cpu_to_ubi32(crc); +	} + +	*vol_tab = res; +	return 0; +} + +static int +create_raw(io_t io) +{ +	int rc = 0; +	size_t ebs_written = 0; /* eraseblocks written already... */ +	size_t vol_tab_size; +	list_t ptr; + +	list_t pfi_raws = mk_empty(); /* list of raw sections from a pfi */ +	list_t pfi_ubis = mk_empty(); /* list of ubi sections from a pfi */ +	list_t raw_pebs	 = mk_empty(); /* list of raw eraseblocks */ + +	struct ubi_vol_tbl_record *vol_tab = NULL; +	pdd_data_t pdd = NULL; + +	rc = init_vol_tab (&vol_tab, &vol_tab_size); +	if (rc != 0) { +		err_msg("Cannot initialize volume table."); +		goto err; +	} + +	rc = read_pdd_data(io->fp_pdd, &pdd, +			err_buf, ERR_BUF_SIZE); +	if (rc != 0) { +		err_msg("Cannot read necessary pdd_data: %s rc: %d", +				err_buf, rc); +		goto err; +	} + +	rc = read_pfi_headers(&pfi_raws, &pfi_ubis, io->fp_pfi, +			err_buf, ERR_BUF_SIZE); +	if (rc != 0) { +		err_msg("Cannot read pfi header: %s rc: %d", +				err_buf, rc); +		goto err; +	} + +	pfi_raw_t pfi_raw; +	foreach(pfi_raw, ptr, pfi_raws) { +		rc = memorize_raw_eb(pfi_raw, pdd, &raw_pebs, +			io); +		if (rc != 0) { +			err_msg("Cannot create raw_block in mem. rc: %d\n", +				rc); +			goto err; +		} +	} + +	pfi_ubi_t pfi_ubi; +	foreach(pfi_ubi, ptr, pfi_ubis) { +		rc = convert_ubi_volume(pfi_ubi, pdd, raw_pebs, +					vol_tab, &ebs_written, io); +		if (rc != 0) { +			err_msg("Cannot convert UBI volume. rc: %d\n", rc); +			goto err; +		} +	} + +	rc = write_ubi_volume_table(pdd, raw_pebs, vol_tab, vol_tab_size, +			&ebs_written, io); +	if (rc != 0) { +		err_msg("Cannot write UBI volume table. rc: %d\n", rc); +		goto err; +	} + +	rc  = write_remaining_raw_ebs(pdd, raw_pebs, &ebs_written, io->fp_out); +	if (rc != 0) +		goto err; + +	if (io->fp_out != stdout) +		info_msg("Physical eraseblocks written: %8d\n", ebs_written); +err: +	free(vol_tab); +	pfi_raws = remove_all((free_func_t)&free_pfi_raw, pfi_raws); +	pfi_ubis = remove_all((free_func_t)&free_pfi_ubi, pfi_ubis); +	raw_pebs = remove_all((free_func_t)&peb_free, raw_pebs); +	free_pdd_data(&pdd); +	return rc; +} + + +/* ------------------------------------------------------------------------- */ +static void +open_io_handle(myargs *args, io_t io) +{ +	/* set PDD input */ +	io->fp_pdd = fopen(args->f_in_pdd, "r"); +	if (io->fp_pdd == NULL) { +		err_sys("Cannot open: %s", args->f_in_pdd); +	} + +	/* set PFI input */ +	io->fp_pfi = fopen(args->f_in_pfi, "r"); +	if (io->fp_pfi == NULL) { +		err_sys("Cannot open PFI input file: %s", args->f_in_pfi); +	} + +	/* set output prefix */ +	if (strcmp(args->f_out,"") == 0) +		io->fp_out = stdout; +	else { +		io->fp_out = fopen(args->f_out, "wb"); +		if (io->fp_out == NULL) { +			err_sys("Cannot open output file: %s", args->f_out); +		} +	} +} + +static void +close_io_handle(io_t io) +{ +	if (fclose(io->fp_pdd) != 0) { +		err_sys("Cannot close PDD file."); +	} +	if (fclose(io->fp_pfi) != 0) { +		err_sys("Cannot close PFI file."); +	} +	if (io->fp_out != stdout) { +		if (fclose(io->fp_out) != 0) { +			err_sys("Cannot close output file."); +		} +	} + +	io->fp_pdd = NULL; +	io->fp_pfi = NULL; +	io->fp_out = NULL; +} + +int +main(int argc, char *argv[]) +{ +	int rc = 0; + +	ubigen_init(); +	init_crc32_table(crc32_table); + +	struct io io = {NULL, NULL, NULL}; +	myargs args = { +		.action = ACT_RAW, +		.verbose = 0, + +		.f_in_pfi = "", +		.f_in_pdd = "", +		.f_out = "", + +		/* arguments */ +		.arg1 = NULL, +		.options = NULL, +	}; + +	/* parse arguments */ +	argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &args); + +	if (strcmp(args.f_in_pfi, "") == 0) { +		err_quit("No PFI input file specified!"); +	} + +	if (strcmp(args.f_in_pdd, "") == 0) { +		err_quit("No PDD input file specified!"); +	} + +	open_io_handle(&args, &io); + +	info_msg("[ Creating RAW..."); +	rc = create_raw(&io); +	if (rc != 0) { +		err_msg("Creating RAW failed."); +		goto err; +	} + +err: +	close_io_handle(&io); +	if (rc != 0) { +		remove(args.f_out); +	} + +	return rc; +} diff --git a/ubi-utils/src/pfiflash/pfiflash.c b/ubi-utils/src/pfiflash/pfiflash.c new file mode 100644 index 0000000..18b3aa2 --- /dev/null +++ b/ubi-utils/src/pfiflash/pfiflash.c @@ -0,0 +1,243 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Oliver Lohmann + *         Frank Haverkamp + * + * Process a PFI (partial flash image) and write the data to the + * specified UBI volumes. This tool is intended to be used for system + * update using PFI files. + */ + +#include <unistd.h> +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <getopt.h> +#include <argp.h> +#include <unistd.h> +#include <errno.h> + +#include <pfiflash.h> +#include "error.h" +#include "config.h" + +const char *argp_program_version = PACKAGE_VERSION; +const char *argp_program_bug_address = PACKAGE_BUGREPORT; +static char doc[] = "\nVersion: " PACKAGE_VERSION "\n\tBuilt on " +	BUILD_CPU" "BUILD_OS" at "__DATE__" "__TIME__"\n" +	"\n" +	"pfiflash - a tool for updating a controller with PFI files.\n"; + +static const char copyright [] __attribute__((unused)) = +	"FIXME: insert license type."; /* FIXME */ + +static struct argp_option options[] = { +	/* Output options */ +	{ name: NULL, key: 0, arg: NULL, flags: 0, +	  doc: "Standard options:", +	  group: 1 }, + +	{ name: "copyright", key: 'c', arg: NULL, flags: 0, +	  doc: "Print copyright information.", +	  group: 1 }, + +	{ name: "verbose", key: 'v', arg: NULL, flags: 0, +	  doc: "Be verbose during program execution.", +	  group: 1 }, + +	{ name: "logfile", key: 'l', arg: "<file>", flags: 0, +	  doc: "Write a logfile to <file>.", +	  group: 1 }, + +	/* Output options */ +	{ name: NULL, key: 0, arg: NULL, flags: 0, +	  doc: "Process options:", +	  group: 2 }, + +	{ name: "complete", key: 'C', arg: NULL, flags: 0, +	  doc: "Execute a complete system update. Updates both sides.", +	  group: 2 }, + +	{ name: "side", key: 's', arg: "<seqnum>", flags: 0, +	  doc: "Select the side which shall be updated.", +	  group: 2 }, + +	{ name: "pdd-update", key: 'p', arg: "<type>", flags: 0, +	  doc: "Specify the pdd-update algorithm. <type> is either " +	  "'keep', 'merge' or 'overwrite'.", +	  group: 2 }, + +	{ name: NULL, key: 0, arg: NULL, flags: 0, doc: NULL, group: 0 }, +}; + +typedef struct myargs { +	int verbose; +	const char *logfile; + +	pdd_handling_t pdd_handling; +	int seqnum; +	int complete; + +	FILE* fp_in; + +	/* special stuff needed to get additional arguments */ +	char *arg1; +	char **options;		/* [STRING...] */ +} myargs; + +static pdd_handling_t +get_pdd_handling(const char* str) +{ +	if (strcmp(str, "keep") == 0) { +		return PDD_KEEP; +	} +	if (strcmp(str, "merge") == 0) { +		return PDD_MERGE; +	} +	if (strcmp(str, "overwrite") == 0) { +		return PDD_OVERWRITE; +	} + +	return -1; +} + +static int +get_update_seqnum(const char* str) +{ +	uint32_t i = strtoul(str, NULL, 0); + +	if ((i != 0) && (i != 1)) { +		return -1; +	} + +	return i; +} + + +static error_t +parse_opt(int key, char *arg, struct argp_state *state) +{ +	int err = 0; + +	myargs *args = state->input; + +	switch (key) { +		/* standard options */ +	case 'c': +		err_msg("%s\n", copyright); +		exit(0); +		break; +	case 'v': +		args->verbose = 1; +		break; +	case 'l': +		args->logfile = arg; +		break; +		/* process options */ +	case 'C': +		args->complete = 1; +		break; +	case 'p': +		args->pdd_handling = get_pdd_handling(arg); +		if (args->pdd_handling < 0) { +			err_quit("Unknown PDD handling: %s.\n" +				 "Please use either 'keep', 'merge' or" +				 "'overwrite'.\n'"); +		} +		break; +	case 's': +		args->seqnum = get_update_seqnum(arg); +		if (args->seqnum < 0) { +			err_quit("Unsupported side: %s.\n" +				 "Supported sides are '0' and '1'\n", arg); +		} +		break; + +	case ARGP_KEY_ARG: /* input file */ +		args->fp_in = fopen(arg, "r"); +		if ((args->fp_in) == NULL) { +			err_sys("Cannot open PFI file %s for input", arg); +		} +		args->arg1 = arg; +		args->options = &state->argv[state->next]; +		state->next = state->argc; +		break; +	case ARGP_KEY_END: +		if (err) { +			err_msg("\n"); +			argp_usage(state); +			exit(1); +		} +		break; +	default: +		return(ARGP_ERR_UNKNOWN); +	} + +	return 0; +} + +static struct argp argp = { +	options:     options, +	parser:      parse_opt, +	args_doc:    "[pfifile]", +	doc:	     doc, +	children:    NULL, +	help_filter: NULL, +	argp_domain: NULL, +}; + +int main (int argc, char** argv) +{ +	int rc = 0; +	char err_buf[PFIFLASH_MAX_ERR_BUF_SIZE]; +	memset(err_buf, '\0', PFIFLASH_MAX_ERR_BUF_SIZE); + +	myargs args = { +		.verbose    = 0, +		.seqnum	    = -1, +		.complete   = 0, +		.logfile    = "/tmp/pfiflash.log", +		.pdd_handling = PDD_KEEP, +		.fp_in	  = stdin, +	}; + +	argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &args); +	error_initlog(args.logfile); + +	if (!args.fp_in) { +		rc = -1; +		snprintf(err_buf, PFIFLASH_MAX_ERR_BUF_SIZE, +			 "No PFI input file specified!\n"); +		goto err; +	} + +	rc = pfiflash(args.fp_in, args.complete, args.seqnum, +		      args.pdd_handling, err_buf, PFIFLASH_MAX_ERR_BUF_SIZE); +	if (rc != 0) { +		goto err_fp; +	} + + err_fp: +	if (args.fp_in != stdin) +		fclose(args.fp_in); + err: +	if (rc != 0) +		err_msg("Error: %s\nrc: %d\n", err_buf, rc); +	return rc; +} diff --git a/ubi-utils/src/ubicrc32/ubicrc32.c b/ubi-utils/src/ubicrc32/ubicrc32.c new file mode 100644 index 0000000..fb4ef49 --- /dev/null +++ b/ubi-utils/src/ubicrc32/ubicrc32.c @@ -0,0 +1,143 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Oliver Lohmann + * + * Calculate CRC32 with UBI start value for a given binary image. + */ + +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <getopt.h> +#include <argp.h> +#include <unistd.h> +#include <errno.h> +#include <mtd/ubi-header.h> + +#include "config.h" +#include "crc32.h" + +#define BUFSIZE 4096 + +const char *argp_program_version = PACKAGE_VERSION; +const char *argp_program_bug_address = PACKAGE_BUGREPORT; +static char doc[] = "\nVersion: " PACKAGE_VERSION "\n\tBuilt on " +	BUILD_CPU" "BUILD_OS" at "__DATE__" "__TIME__"\n" +	"\n" +	"ubicrc32 - calculates the UBI CRC32 value and prints it to stdout.\n"; + +static const char copyright [] __attribute__((unused)) = +	"FIXME: insert license type"; /* FIXME */ + + +static struct argp_option options[] = { +	{ name: "copyright", key: 'c', arg: NULL,    flags: 0, +	  doc: "Print copyright information.", +	  group: 1 }, + +	{ name: NULL, key: 0, arg: NULL, flags: 0, doc: NULL, group: 0 }, +}; + +typedef struct myargs { +	FILE* fp_in; + +	char *arg1; +	char **options;			/* [STRING...] */ +} myargs; + +static error_t +parse_opt(int key, char *arg, struct argp_state *state) +{ +	int err = 0; + +	myargs *args = state->input; + +	switch (key) { +	case 'c': +		fprintf(stderr, "%s\n", copyright); +		exit(0); +		break; +	case ARGP_KEY_ARG: +		args->fp_in = fopen(arg, "rb"); +		if ((args->fp_in) == NULL) { +			fprintf(stderr, +			"Cannot open file %s for input\n", arg); +			exit(1); +		} +		args->arg1 = arg; +		args->options = &state->argv[state->next]; +		state->next = state->argc; +		break; +	case ARGP_KEY_END: +		if (err) { +			fprintf(stderr, "\n"); +			argp_usage(state); +			exit(1); +		} +		break; +	default: +		return(ARGP_ERR_UNKNOWN); +	} + +	return 0; +} + +static struct argp argp = { +	options:     options, +	parser:	     parse_opt, +	args_doc:    "[file]", +	doc:	     doc, +	children:    NULL, +	help_filter: NULL, +	argp_domain: NULL, +}; + +int +main(int argc, char **argv) { +	int rc = 0; +	uint32_t crc32_table[256]; +	uint8_t buf[BUFSIZE]; +	size_t read; +	uint32_t crc32; + +	myargs args = { +		.fp_in = stdin, +		.arg1 = NULL, +		.options = NULL, +	}; + +	argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &args); + +	init_crc32_table(crc32_table); +	crc32 = UBI_CRC32_INIT; +	while (!feof(args.fp_in)) { +		read = fread(buf, 1, BUFSIZE, args.fp_in); +		if (ferror(args.fp_in)) { +			fprintf(stderr, "I/O Error."); +			exit(EXIT_FAILURE); +		} +		crc32 = clc_crc32(crc32_table, crc32, buf, read); +	} + +	if (args.fp_in != stdin) { +		fclose(args.fp_in); +	} + +	fprintf(stdout, "0x%08x\n", crc32); +	return rc; +} diff --git a/ubi-utils/src/ubicrc32/ubicrc32.pl b/ubi-utils/src/ubicrc32/ubicrc32.pl new file mode 100755 index 0000000..add5f9d --- /dev/null +++ b/ubi-utils/src/ubicrc32/ubicrc32.pl @@ -0,0 +1,74 @@ +#!/usr/bin/perl -w + +# Subroutine crc32(): Calculates the CRC on a given string. + +{ +    my @table = (); + +    # @brief Calculate CRC32 for a given string. +    sub crc32 +    { +	unless (@table) { +	    # Initialize the CRC table +	    my $poly = 0xEDB88320; +	    @table = (); + +	    for my $i (0..255) { +		my $c = $i; + +		for my $j (0..7) { +		    $c = ($c & 1) ? (($c >> 1) ^ $poly) : ($c >> 1); +		} +		$table[$i] = $c; +	    } +	} +	my $s = shift;		# string to calculate the CRC for +	my $crc = shift;	# CRC start value + +	defined($crc) +	    or $crc = 0xffffffff; # Default CRC start value + +	for (my $i = 0; $i < length($s); $i++) { +	    $crc = $table[($crc ^ ord(substr($s, $i, 1))) & 0xff] +		^ ($crc >> 8); +	} +	return $crc; +    } +} + +sub crc32_on_file +{ +    my $file = shift; + +    my $crc32 = crc32(''); +    my $buf = ''; +    my $ret = 0; + +    while ($ret = read($file, $buf, 8192)) { +	$crc32 = crc32($buf, $crc32); +    } +    defined($ret) +	or return undef; +    printf("0x%x\n", $crc32); +} + + +# Main routine: Calculate the CRCs on the given files and print the +# results. + +{ +    if (@ARGV) { +	while (my $path = shift) { +	    my $file; +	    open $file, "<", $path +		or die "Error opening '$path'.\n"; +	     +	    &crc32_on_file($file) +		or die "Error reading from '$path'.\n"; +	    close $file; +	} +    } else { +	&crc32_on_file(\*STDIN) +	    or die "Error reading from stdin.\n"; +    } +} diff --git a/ubi-utils/src/ubigen/ubigen_main.c b/ubi-utils/src/ubigen/ubigen_main.c new file mode 100644 index 0000000..8a464dd --- /dev/null +++ b/ubi-utils/src/ubigen/ubigen_main.c @@ -0,0 +1,369 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Oliver Lohmann + * + * Tool to add UBI headers to binary images. + * + * 1.0 Initial version + * 1.1 Different CRC32 start value + */ + +#include <stdlib.h> +#include <stdint.h> +#include <stdio.h> +#include <getopt.h> +#include <argp.h> +#include <unistd.h> +#include <sys/stat.h> +#include <mtd/ubi-header.h> + +#include "ubigen.h" +#include "config.h" + +typedef enum action_t { +	ACT_NORMAL	     = 0x00000001, +	ACT_BROKEN_UPDATE    = 0x00000002, +} action_t; + + +const char *argp_program_version = PACKAGE_VERSION; +const char *argp_program_bug_address = PACKAGE_BUGREPORT; +static char doc[] = "\nVersion: " PACKAGE_VERSION "\n\tBuilt on " +	BUILD_CPU" "BUILD_OS" at "__DATE__" "__TIME__"\n" +	"\n" +	"ubigen - a tool for adding UBI information to a binary input file.\n"; + +static const char copyright [] __attribute__((unused)) = +	"FIXME: insert license type"; /* FIXME */ + +#define CHECK_ENDP(option, endp) do {			\ +	if (*endp) {					\ +		fprintf(stderr,				\ +			"Parse error option \'%s\'. "	\ +			"No correct numeric value.\n"	\ +			, option);			\ +		exit(EXIT_FAILURE);			\ +	}						\ +} while(0) + +static struct argp_option options[] = { +	/* COMMON */ +	{ name: NULL, key: 0, arg: NULL, flags: 0, +	  doc: "Common settings:", +	  group: 1}, + +	{ name: "copyright", key: 'c', arg: NULL,    flags: 0, +	  doc: "Print copyright information.", +	  group: 1 }, + +	{ name: "verbose", key: 'v', arg: NULL,	   flags: 0, +	  doc: "Print more progress information.", +	  group: 1 }, + +	{ name: "debug", key: 'd', arg: NULL, flags: 0, +	  group: 1 }, + + +	/* INPUT */ +	{ name: NULL, key: 0, arg: NULL, flags: 0, +	  doc: "UBI Settings:", +	  group: 4}, + +	{ name: "alignment", key: 'A', arg: "<num>", flags: 0, +	  doc: "Set the alignment size to <num> (default 1).\n" +	       "Values can be specified as bytes, 'ki' or 'Mi'.", +	  group: 4 }, + +	{ name: "blocksize", key: 'B', arg: "<num>", flags: 0, +	  doc: "Set the eraseblock size to <num> (default 128 KiB).\n" +	       "Values can be specified as bytes, 'ki' or 'Mi'.", +	  group: 4 }, + +	{ name: "erasecount", key: 'E', arg: "<num>", flags: 0, +	  doc: "Set the erase count to <num> (default 0)", +	  group: 4 }, + +	{ name: "setver", key: 'X', arg: "<num>", flags: 0, +	  doc: "Set UBI version number to <num> (default 1)", +	  group: 4 }, + +	{ name: "id", key: 'I', arg: "<num>", flags: 0, +	  doc: "The UBI volume id.", +	  group: 4 }, + + +	{ name: "offset", key: 'O', arg: "<num>", flags: 0, +	  doc: "Offset from start of an erase block to the UBI volume header.", +	  group: 4 }, + +	{ name: "type", key: 'T', arg: "<num>", flags: 0, +	  doc: "The UBI volume type:\n1 = dynamic, 2 = static", +	  group: 4 }, + +	/* INPUT/OUTPUT */ +	{ name: NULL, key: 0, arg: NULL, flags: 0, +	  doc: "Input/Output:", +	  group: 5 }, + +	{ name: "infile", key: 'i', arg: "<filename>", flags: 0, +	  doc: "Read input from file.", +	  group: 5 }, + +	{ name: "outfile", key: 'o', arg: "<filename>", flags: 0, +	  doc: "Write output to file (default is stdout).", +	  group: 5 }, + +	/* Special options */ +	{ name: NULL, key: 0, arg: NULL, flags: 0, +	  doc: "Special options:", +	  group: 6 }, + +	{ name: "broken-update", key: 'U', arg: "<leb>", flags: 0, +	  doc: "Create an ubi image which simulates a broken update.\n" +	       "<leb> specifies the logical eraseblock number to update.\n", +	  group: 6 }, + +	{ name: NULL, key: 0, arg: NULL, flags: 0, doc: NULL, group: 0 }, +}; + +typedef struct myargs { +	/* common settings */ +	action_t action; +	int verbose; + +	int32_t id; +	uint8_t type; +	uint32_t eb_size; +	uint64_t ec; +	uint8_t version; +	uint32_t hdr_offset; +	uint32_t update_block; +	uint32_t alignment; + +	FILE* fp_in; +	FILE* fp_out; + +	/* special stuff needed to get additional arguments */ +	char *arg1; +	char **options;			/* [STRING...] */ +} myargs; + + +static int ustrtoul(const char *cp, char **endp, unsigned int base) +{ +	unsigned long result = strtoul(cp, endp, base); + +	switch (**endp) { +	case 'G': +		result *= 1024; +	case 'M': +		result *= 1024; +	case 'k': +	case 'K': +		result *= 1024; +	/* "Ki", "ki", "Mi" or "Gi" are to be used. */ +		if ((*endp)[1] == 'i') +			(*endp) += 2; +	} +	return result; +} + +static error_t +parse_opt(int key, char *arg, struct argp_state *state) +{ +	int err = 0; +	char* endp; + +	myargs *args = state->input; + +	switch (key) { +	case 'c': +		fprintf(stderr, "%s\n", copyright); +		exit(0); +		break; +	case 'o': /* output */ +		args->fp_out = fopen(arg, "wb"); +		if ((args->fp_out) == NULL) { +			fprintf(stderr, "Cannot open file %s for output\n", +					arg); +			exit(1); +		} +		break; +	case 'i': /* input */ +		args->fp_in = fopen(arg, "rb"); +		if ((args->fp_in) == NULL) { +			fprintf(stderr, "Cannot open file %s for input\n", +					arg); +			exit(1); +		} +		break; +	case 'v': /* verbose */ +		args->verbose = 1; +		break; + +	case 'B': /* eb_size */ +		args->eb_size = (uint32_t) ustrtoul(arg, &endp, 0); +		CHECK_ENDP("B", endp); +		break; +	case 'E': /* erasecount */ +		args->ec = (uint64_t) strtoul(arg, &endp, 0); +		CHECK_ENDP("E", endp); +		break; +	case 'I': /* id */ +		args->id = (uint16_t) strtoul(arg, &endp, 0); +		CHECK_ENDP("I", endp); +		break; +	case 'T': /* type */ +		args->type =  (uint16_t) strtoul(arg, &endp, 0); +		CHECK_ENDP("T", endp); +		break; +	case 'X': /* versionnr */ +		args->version =	 (uint8_t) strtoul(arg, &endp, 0); +		CHECK_ENDP("X", endp); +		break; +	case 'O': /* offset for volume hdr */ +		args->hdr_offset = +			(uint32_t) strtoul(arg, &endp, 0); +		CHECK_ENDP("O", endp); +		break; + +	case 'U': /* broken update */ +		args->action = ACT_BROKEN_UPDATE; +		args->update_block = +			(uint32_t) strtoul(arg, &endp, 0); +		CHECK_ENDP("U", endp); +		break; + +	case ARGP_KEY_ARG: +		if (!args->fp_in) { +			args->fp_in = fopen(arg, "rb"); +			if ((args->fp_in) == NULL) { +				fprintf(stderr, +				"Cannot open file %s for input\n", arg); +				exit(1); +			} +		} +		args->arg1 = arg; +		args->options = &state->argv[state->next]; +		state->next = state->argc; +		break; +	case ARGP_KEY_END: + +		if (args->id < 0) { +			err = 1; +			fprintf(stderr, +				"Please specify an UBI Volume ID.\n"); +		} +		if (args->type == 0) { +			err = 1; +			fprintf(stderr, +				"Please specify an UBI Volume type.\n"); +		} +		if (err) { +			fprintf(stderr, "\n"); +			argp_usage(state); +			exit(1); +		} +		break; +	default: +		return(ARGP_ERR_UNKNOWN); +	} + +	return 0; +} + +static struct argp argp = { +	options:     options, +	parser:	     parse_opt, +	args_doc:    0, +	doc:	     doc, +	children:    NULL, +	help_filter: NULL, +	argp_domain: NULL, +}; + + +int +main(int argc, char **argv) +{ +	int rc = 0; +	ubi_info_t u; +	struct stat file_info; +	off_t input_len = 0; /* only used in static volumes */ + +	myargs args = { +		.action = ACT_NORMAL, +		.verbose = 0, + +		.id = -1, +		.type = 0, +		.eb_size = 0, +		.update_block = 0, +		.ec = 0, +		.version = 0, +		.hdr_offset = (DEFAULT_PAGESIZE) - (UBI_VID_HDR_SIZE), +		.alignment = 1, + +		.fp_in = NULL, +		.fp_out = stdout, +		/* arguments */ +		.arg1 = NULL, +		.options = NULL, +	}; + +	ubigen_init(); /* Init CRC32 table in ubigen */ + +	/* parse arguments */ +	argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &args); + +	if (fstat(fileno(args.fp_in), &file_info) != 0) { +		fprintf(stderr, "Cannot fetch file size " +				"from input file.\n"); +	} +	input_len = file_info.st_size; + +	rc = ubigen_create(&u, (uint32_t)args.id, args.type, +			args.eb_size, args.ec, args.alignment, +			args.version, args.hdr_offset, 0 ,input_len, +			args.fp_in, args.fp_out); + +	if  (rc != 0) { +		fprintf(stderr, "Cannot create UBI info handler rc: %d\n", rc); +		exit(EXIT_FAILURE); +	} + +	if (!args.fp_in || !args.fp_out) { +		fprintf(stderr, "Input/Output error.\n"); +		exit(EXIT_FAILURE); + +	} + +	if (args.action & ACT_NORMAL) { +		rc = ubigen_write_complete(u); +	} +	else if (args.action & ACT_BROKEN_UPDATE) { +		rc = ubigen_write_broken_update(u, args.update_block); +	} +	if  (rc != 0) { +		fprintf(stderr, "Error converting input data.\n"); +		exit(EXIT_FAILURE); +	} + +	rc = ubigen_destroy(&u); +	return rc; +} diff --git a/ubi-utils/src/ubiinfo/ubiflash.h b/ubi-utils/src/ubiinfo/ubiflash.h new file mode 100644 index 0000000..6883879 --- /dev/null +++ b/ubi-utils/src/ubiinfo/ubiflash.h @@ -0,0 +1,185 @@ +#ifndef _UBI_FLASH_H +#define _UBI_FLASH_H +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * FLASH related data structures and constants for UBI. + * UBI scan analysis. + * + * IPL Initial Program Loader + * SPL Secondary Program Loader + */ + +#include <stdint.h> +#include <asm/byteorder.h> +#include <mtd/ubi-header.h> + +#define UBI_BLOCK_IDENT_MAX  16 + +/* Block status information constants */ +enum blockstat { +	/* IO Error */ +	STAT_IO_FAILED	= 1,	/* 0xffffffff */ +	/* Block is bad */ +	STAT_BLOCK_BAD	= 2,	/* 0xfffffffe */ +	/* ECC unrecoverable error */ +	STAT_ECC_ERROR	= 3,	/* 0xfffffffd */ +	/* CRC checksum failed */ +	STAT_CRC_ERROR	= 4,	/* 0xfffffffc */ +	/* Magic number not available */ +	STAT_NO_MAGIC	= 5,	/* 0xfffffffb */ +	/* No image available */ +	STAT_NO_IMAGE	= 6, +	/* Image is invalid */ +	STAT_INVALID_IMAGE	= 7, +	/* Image is defect */ +	STAT_DEFECT_IMAGE	= 8, +}; + +/* + * Flash types + */ +enum flashtypes { +	FLASH_TYPE_NAND = 1, +	FLASH_TYPE_NOR, +}; + +/* Nand read buffer size: 2KiB + 64byte spare */ +#define NAND_READ_BUF_SIZE	(2048 + 64) + +/* Size of the CRC table */ +#define CRC32_TABLE_SIZE	256 + +/* Image is not available marker for image offs */ +#define UBI_IMAGE_NOT_AVAILABLE	0xFFFFFFFF + +/* Increment this number, whenever you change the structure */ +#define UBI_SCAN_INFO_VERSION	2 + +/* Time measurement as far as the code size allows us to do this */ +#define UBI_TIMESTAMPS		16 + +/** + * struct ubi_scan_info - RAM table filled by IPL scan + * + * @version:		Version of the structure + * @bootstatus:		Boot status of the current boot + * @flashtype:		Flash type (NAND/NOR) + * @flashid:		ID of the flash chip + * @flashmfr:		Manufacturer ID of the flash chip + * @flashsize:		Size of the FLASH + * @blocksize:		Eraseblock size + * @blockshift:		Shift count to calc block number from offset + * @nrblocks:		Number of erase blocks on flash + * @pagesize:		Pagesize (NAND) + * @blockinfo:		Pointer to an array of block status information + *			filled by FLASH scan + * @images:		Pointer to FLASH block translation table sorted + *			by image type and load order + * @imageblocks:	Number of blocks found per image + * @imageoffs:		Offset per imagetype to the first + *			block in the translation table + * @imagedups		duplicate blocks (max. one per volume) + * @imagelen:		Length of the loaded image + * @crc32_table:	CRC32 table buffer + * @page_buf:		Page buffer for NAND FLASH + */ +struct ubi_scan_info { +	int			version; +	unsigned int		bootstatus; +	unsigned int		flashtype; +	unsigned int		flashid; +	unsigned int		flashmfr; +	unsigned int		flashsize; +	unsigned int		blocksize; +	unsigned int		blockshift; +	unsigned int		nrblocks; +	unsigned int		pagesize; + +	struct ubi_vid_hdr	*blockinfo; +	struct ubi_vid_hdr	**images; +	unsigned int		imageblocks[UBI_BLOCK_IDENT_MAX]; +	unsigned int		imageoffs[UBI_BLOCK_IDENT_MAX]; +	struct ubi_vid_hdr	*imagedups[UBI_BLOCK_IDENT_MAX]; +	unsigned int		imagelen; +	uint32_t		crc32_table[CRC32_TABLE_SIZE]; +	uint8_t			page_buf[NAND_READ_BUF_SIZE]; +	unsigned int		times[UBI_TIMESTAMPS]; +}; + +/* External function definition */ +extern int flash_read(void *buf, unsigned int offs, int len); +extern int flash_read_slice(struct ubi_scan_info *fi, void *buf, +			    unsigned int offs, int len); +extern void ipl_main(struct ubi_scan_info *fi); + +#ifndef CFG_EXAMPLE_IPL +extern int ipl_scan(struct ubi_scan_info *fi); +extern int ipl_load(struct ubi_scan_info *fi, int nr, uint8_t *loadaddr); + +#define IPL_STATIC + +#else +#define IPL_STATIC static +#endif + +/** + * get_boot_status - get the boot status register + * + * Shift the lower 16 bit into the upper 16 bit and return + * the result. + */ +uint32_t get_boot_status(void); + +/** + * set_boot_status - Set the boot status register + * + * @status:	The status value to set + * + */ +void set_boot_status(uint32_t status); + +static inline unsigned int ubi_vid_offset(struct ubi_scan_info *fi) +{ +	if (fi->flashtype == FLASH_TYPE_NOR) +		return UBI_EC_HDR_SIZE; +	else +		return fi->pagesize - UBI_VID_HDR_SIZE; +} + +static inline unsigned int ubi_data_offset(struct ubi_scan_info *fi) +{ +	if (fi->flashtype == FLASH_TYPE_NOR) +		return UBI_EC_HDR_SIZE + UBI_VID_HDR_SIZE; +	else +		return fi->pagesize; +} + +/** + * IPL checkpoints + */ +#define CHKP_HWINIT		0x3030 +#define CHKP_IPLSCAN_FAILED	0x3034 +#define CHKP_SPL_START		0x3037 +#define CHKP_SPLLOAD_STATUS	0x3130 + +extern void checkpoint(uint32_t cpoint); +extern void switch_watchdog(void); + +#endif diff --git a/ubi-utils/src/ubiinfo/ubiinfo.c b/ubi-utils/src/ubiinfo/ubiinfo.c new file mode 100644 index 0000000..6f7443b --- /dev/null +++ b/ubi-utils/src/ubiinfo/ubiinfo.c @@ -0,0 +1,406 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * Print out information about the UBI table this IPL is using.  This + * can be used afterwards to analyze misbehavior of the IPL code.  The + * input this program requires is the last 1 MiB DDRAM of our system + * where the scanning table is placed into. + * + * Author: Frank Haverkamp <haver@vnet.ibm.com> + */ + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <time.h> +#include <argp.h> +#include <getopt.h> +#include <stdint.h> +#include <sys/time.h> +#include <netinet/in.h> + +#define __unused __attribute__((unused)) + +/* This should hopefully be constant and the same in all + * configurations. + */ +#define CFG_IPLSIZE	512 +#define CFG_SPLCODE	512 +#define MEMTOP		0x06600000 /* Sunray 102 MiB */ +#define MEMSIZE		0x00100000 /* 1 MiB */ +#define CODE_SIZE	(64 * 1024) + +/* FIXME Except of the memory size this should be defined via + * parameters + * + * CFG_MEMTOP_BAMBOO  0x02000000 + * CFG_MEMTOP_SUNRAY  0x06600000 + */ + +#include "ubiipl.h" +#include "ubiflash.h" + +#define MIN(x,y) ((x)<(y)?(x):(y)) + +#define ERR_RET(rc) {						   \ +		fprintf(stderr, "%s:%d failed rc=%d\n", __func__,	\ +			__LINE__, (rc));				\ +		return (rc);					    \ +	} + +#define VERSION "1.3" + +static error_t parse_opt (int key, char *arg, struct argp_state *state); +const char *argp_program_version = VERSION; +const char *argp_program_bug_address = "<haver@vnet.ibm.com>"; + +static char doc[] = "\nVersion: " VERSION "\n\t" +	" at "__DATE__" "__TIME__"\n" +	"\n" +	"Test program\n"; + +static struct argp_option options[] = { +	/* common settings */ +	{ .name = "verbose", +	  .key = 'v', +	  .arg = "<level>", +	  .flags = 0, +	  .doc = "Set verbosity level to <level>", +	  .group = OPTION_ARG_OPTIONAL }, + +	{ .name = "memtop", +	  .key = 'm', +	  .arg = "<memtop>", +	  .flags = 0, +	  .doc = "Set top of memory, 102 MiB for Sunray and 16 MiB for Bamboo", +	  .group = OPTION_ARG_OPTIONAL }, + +	{ .name = NULL, +	  .key = 0, +	  .arg = NULL, +	  .flags = 0, +	  .doc = NULL, +	  .group = 0 }, +}; + +typedef struct test_args { +	int verbose; +	unsigned long memtop; +	char *arg1; +	char **options; +} test_args; + +static struct test_args g_args = { +	.memtop = MEMTOP, +	.verbose = 0, +	.arg1 = NULL, +	.options = NULL, +}; + +static struct argp argp = { +	options:     options, +	parser:	     parse_opt, +	args_doc:    "[last_1MiB_memory.bin]", +	doc:	 doc, +	children:    NULL, +	help_filter: NULL, +	argp_domain: NULL, +}; + +static int verbose = 0; + +/** + * @brief Parse the arguments passed into the test case. + * + * @param key	    The parameter. + * @param arg	    Argument passed to parameter. + * @param state	  Location to put information on parameters. + * + * @return error_t + */ +static error_t +parse_opt(int key, char *arg, struct argp_state *state) +{ +	/* Get the `input' argument from `argp_parse', which we +	   know is a pointer to our arguments structure. */ +	test_args *args = state->input; + +	switch (key) { +		/* common settings */ +	case 'v': /* --verbose=<level> */ +		verbose = args->verbose = strtoul(arg, (char **)NULL, 0); +		break; + +	case 'm': /* --memtop */ +		args->memtop = strtoul(arg, (char **)NULL, 0); +		break; + +	case ARGP_KEY_NO_ARGS: +		/* argp_usage(state); */ +		break; + +	case ARGP_KEY_ARG: +		args->arg1 = arg; +		/* Now we consume all the rest of the arguments. +		   `state->next' is the index in `state->argv' of the +		   next argument to be parsed, which is the first STRING +		   we're interested in, so we can just use +		   `&state->argv[state->next]' as the value for +		   arguments->strings. + +		   _In addition_, by setting `state->next' to the end +		   of the arguments, we can force argp to stop parsing +		   here and return. */ + +		args->options = &state->argv[state->next]; +		state->next = state->argc; +		break; + +	case ARGP_KEY_END: +		/* print out message if no arguments are given but PFI +		   write should be done */ +		break; + +	default: +		return(ARGP_ERR_UNKNOWN); +	} +	return 0; +} + +static void +hexdump(const char *buf, int len) +{ +	char line[16]; +	char str[256]; +	char dummy[256]; +	int j = 0; + +	while (len > 0) { +		int i; + +		strcpy(str, " "); + +		for (j = 0; j < MIN(16, len); j++) +			line[j] = *buf++; + +		for (i = 0; i < j; i++) { +			if (!(i & 3)) { +				sprintf(dummy, " %.2x", line[i] & 0xff); +				strcat(str, dummy); +			} else { +				sprintf(dummy, "%.2x", line[i] & 0xff); +				strcat(str, dummy); +			} +		} + +		/* Print empty space */ +		for (; i < 16; i++) +			if (!(i & 1)) +				strcat(str, "	"); +			else +				strcat(str, "  "); + +		strcat(str, "  "); +		for (i = 0; i < j; i++) { +			if (isprint(line[i])) { +				sprintf(dummy, "%c", line[i]); +				strcat(str, dummy); +			} else { +				strcat(str, "."); +			} +		} +		printf("%s\n", str); +		len -= 16; +	} +} + +static void +print_status_help(void) +{ +	printf("Error Codes from IPL\n"); +	printf("   IO Error	     %d\n", STAT_IO_FAILED); +	printf("   Block is bad	     %d\n", STAT_BLOCK_BAD); +	printf("   ECC unrec error   %d\n", STAT_ECC_ERROR); +	printf("   CRC csum failed   %d\n", STAT_CRC_ERROR); +	printf("   Magic not avail   %d\n", STAT_NO_MAGIC); +	printf("   No image avail    %d\n", STAT_NO_IMAGE); +	printf("   Image is invalid  %d\n", STAT_INVALID_IMAGE); +	printf("   Image is defect   %d\n\n", STAT_DEFECT_IMAGE); + +} + +static void +print_ubi_scan_info(struct ubi_scan_info *fi) +{ +	int i; + +	printf("ubi_scan_info\n"); +	printf("    version	 %08x\n", ntohl(fi->version)); +	printf("    bootstatus	 %08x\n", ntohl(fi->bootstatus)); +	printf("    flashtype	 %08x\n", ntohl(fi->flashtype)); +	printf("    flashid	 %08x\n", ntohl(fi->flashid)); +	printf("    flashmfgr	 %08x\n", ntohl(fi->flashmfr)); +	printf("    flashsize	 %d bytes (%dM)\n", +	       ntohl(fi->flashsize), ntohl(fi->flashsize) / (1024 * 1024)); +	printf("    blocksize	 %d bytes\n", ntohl(fi->blocksize)); +	printf("    blockshift	 %d\n", ntohl(fi->blockshift)); +	printf("    nrblocks	 %d\n", ntohl(fi->nrblocks)); +	printf("    pagesize	 %d\n", ntohl(fi->pagesize)); +	printf("    imagelen	 %d\n", ntohl(fi->imagelen)); +	printf("    blockinfo	 %08x\n", ntohl((int)fi->blockinfo)); + +	printf("  nr	imageblocks  imageoffs\n"); +	for (i = 0; i < UBI_BLOCK_IDENT_MAX; i++) +		printf("    [%2d]   %08x   %08x\n", i, +		       ntohl(fi->imageblocks[i]), +		       ntohl(fi->imageoffs[i])); + +	for (i = 0; i < UBI_TIMESTAMPS; i++) { +		if (!ntohl(fi->times[i])) +			continue; +		printf("time[%3d] = %08x %.3f sec\n", i, ntohl(fi->times[i]), +		       (double)ntohl(fi->times[i]) / 500000000.0); +	} + +	printf("crc32_table\n"); +	hexdump((char *)&fi->crc32_table, sizeof(fi->crc32_table)); +	printf("\npage_buf\n"); +	hexdump((char *)&fi->page_buf, sizeof(fi->page_buf)); + +	printf("\n"); + +} + +static void +print_ubi_block_info(struct ubi_scan_info *fi, +		     struct ubi_vid_hdr *bi, int nr) +{ +	int i; +	int unknown = 0; + +	printf("\nBINFO\n"); + +	for (i = 0; i < nr; i++) { +		if ((int)ubi32_to_cpu(bi[i].magic) != UBI_VID_HDR_MAGIC) { +			printf("block=%d %08x\n", +			       i, i * ntohl(fi->blocksize)); +#if 0 +			printf("."); +			if ((unknown & 0x3f) == 0x3f) +				printf("\n"); +			unknown++; +#else +			hexdump((char *)&bi[i], +				sizeof(struct ubi_vid_hdr)); +#endif +		} else { +			if (unknown) +				printf("\n"); +			printf("block=%d %08x\n" +			       "    leb_ver=0x%x data_size=%d " +			       "lnum=%d used_ebs=0x%x\n" +			       "    data_crc=%08x hdr_crc=%08x\n", +			       i, i * ntohl(fi->blocksize), +			       ubi32_to_cpu(bi[i].leb_ver), +			       ubi32_to_cpu(bi[i].data_size), +			       ubi32_to_cpu(bi[i].lnum), +			       ubi32_to_cpu(bi[i].used_ebs), +			       ubi32_to_cpu(bi[i].data_crc), +			       ubi32_to_cpu(bi[i].hdr_crc)); +			hexdump((char *)&bi[i], +				sizeof(struct ubi_vid_hdr)); +			unknown = 0; +		} +	} +} + +static int do_read(unsigned int memtop, char *buf, int buf_len __unused) +{ +	unsigned long finfo_addr; +	unsigned long binfo_addr; +	unsigned long images_addr; +	unsigned long nrblocks; +	unsigned long bi_size; +	unsigned long images_size; +	struct ubi_scan_info *fi; +	struct ubi_vid_hdr *bi; +	char *images; +	unsigned long memaddr = memtop - MEMSIZE; + +	print_status_help(); + +	/* Read and print FINFO */ +	finfo_addr = MEMSIZE - CFG_IPLSIZE * 1024; + +	printf("read info at addr %08lx\n", finfo_addr); +	fi = (struct ubi_scan_info *)(buf + finfo_addr); + +	binfo_addr  = ntohl((unsigned long)fi->blockinfo) - memaddr; +	images_addr = ntohl((unsigned long)fi->images) - memaddr; +	nrblocks    = ntohl(fi->nrblocks); + +	printf("BINFO %08lx\n", binfo_addr); + +	bi_size = nrblocks * sizeof(struct ubi_vid_hdr); +	images_size = nrblocks * sizeof(unsigned int); + +	printf("FINFO\n"); +	print_ubi_scan_info(fi); +	/* hexdump((char *)fi, sizeof(*fi)); */ + +	/* Read and print BINFO */ +	bi = (struct ubi_vid_hdr *)(buf + binfo_addr); +	print_ubi_block_info(fi, bi, nrblocks); + +	/* Read and print IMAGES */ +	images = buf + images_addr; +	printf("\nIMAGES\n"); +	hexdump(images, images_size); + +	return 0; +} + +int main(int argc, char *argv[]) +{ +	char buf[MEMSIZE]; +	FILE *fp; +	int rc; + +	argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &g_args); + +	if (!g_args.arg1) { +		fprintf(stderr, "Please specify a file " +			"name for memory dump!\n"); +		exit(EXIT_FAILURE); +	} + +	memset(buf, 0xAB, sizeof(buf)); + +	fp = fopen(g_args.arg1, "r"); +	if (!fp) +		exit(EXIT_FAILURE); +	rc = fread(buf, 1, sizeof(buf), fp); +	if (rc != sizeof(buf)) +		exit(EXIT_FAILURE); +	fclose(fp); +	do_read(g_args.memtop, buf, sizeof(buf)); + +	exit(EXIT_SUCCESS); +} diff --git a/ubi-utils/src/ubiinfo/ubiipl.h b/ubi-utils/src/ubiinfo/ubiipl.h new file mode 100644 index 0000000..3a8b900 --- /dev/null +++ b/ubi-utils/src/ubiinfo/ubiipl.h @@ -0,0 +1,87 @@ +#ifndef _UBI_IPL_H +#define _UBI_IPL_H +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * Constants calculated from the CFG_XXX defines + * + * Declaration of the loader function which is invoked by the + * assembler part of the IPL + */ + +/* Size of IPL - is 4K for NAND and can also be 4K for NOR */ +#define IPL_SIZE	   4096 + +/* Needed in asm code to upload the data, needed in C-code for CRC32 */ +#define IPL_RAMADDR	   (CFG_MEMTOP - IPL_SIZE) + +#if !defined(__ASSEMBLY__) + +#include <stdint.h> +#include <mtd/ubi-header.h> + +/* Address of the flash info structure */ +#define FINFO_ADDR (struct ubi_scan_info *) (CFG_MEMTOP - CFG_IPLSIZE * 1024) + +/* Size of the flash info structure */ +#define FINFO_SIZE sizeof(struct ubi_scan_info) + +/* Blockinfo array address */ +#define BINFO_ADDR (struct ubi_vid_hdr *) ((void *)FINFO_ADDR + FINFO_SIZE) + +/* Number of erase blocks */ +#define NR_ERASE_BLOCKS	((CFG_FLASHSIZE * 1024) / CFG_BLOCKSIZE) + +/* Blockinfo size */ +#define BINFO_SIZE (NR_ERASE_BLOCKS * sizeof(struct ubi_vid_hdr)) + +/* Images array address */ +#define IMAGES_ADDR (struct ubi_vid_hdr **) ((void *)BINFO_ADDR + BINFO_SIZE) + +/* Images array size */ +#define IMAGES_SIZE (NR_ERASE_BLOCKS * sizeof(unsigned int)) + +/* Total size of flash info + blockinfo + images */ +#define INFO_SIZE ((FINFO_SIZE + BINFO_SIZE + IMAGES_SIZE) / sizeof(uint32_t)) + +/* Load address of the SPL */ +#define SPL_ADDR (void *) ((void *)FINFO_ADDR - CFG_SPLCODE * 1024) + +#define IPL_SIZE_CRC32	   (IPL_SIZE - sizeof(uint32_t)) +#define IPL_RAMADDR_CRC32  ((void *)(IPL_RAMADDR + sizeof(uint32_t))) + +/* + * Linker script magic to ensure that load_spl() is linked to the + * right place + */ +#define __crc32	 __attribute__((__section__(".crc32"))) +#define __entry	 __attribute__((__section__(".entry.text"))) +#define __unused __attribute__((unused)) + +#define MIN(x,y) ((x)<(y)?(x):(y)) + +#define stop_on_error(x) \ +	{ while (1); } + +void __entry load_spl(void); +void hardware_init(void); + +#endif /* __ASSEMBLY__ */ + +#endif diff --git a/ubi-utils/src/ubimirror/ubimirror.c b/ubi-utils/src/ubimirror/ubimirror.c new file mode 100644 index 0000000..e43ba10 --- /dev/null +++ b/ubi-utils/src/ubimirror/ubimirror.c @@ -0,0 +1,206 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Oliver Lohmann + */ + +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <getopt.h> +#include <argp.h> +#include <unistd.h> +#include <errno.h> +#include <mtd/ubi-header.h> + +#include "config.h" +#include "error.h" +#include "example_ubi.h" +#include "ubimirror.h" + +typedef enum action_t { +	ACT_NORMAL = 0, +	ACT_ARGP_ABORT, +	ACT_ARGP_ERR, +} action_t; + +#define ABORT_ARGP do {			\ +	state->next = state->argc;	\ +	args->action = ACT_ARGP_ABORT;	\ +} while (0) + +#define ERR_ARGP do {			\ +	state->next = state->argc;	\ +	args->action = ACT_ARGP_ERR;	\ +} while (0) + +#define VOL_ARGS_MAX 2 + + +const char *argp_program_version = PACKAGE_VERSION; +const char *argp_program_bug_address = PACKAGE_BUGREPORT; +static char doc[] = "\nVersion: " PACKAGE_VERSION "\n\tBuilt on " +	BUILD_CPU" "BUILD_OS" at "__DATE__" "__TIME__"\n" +	"\n" +	"ubimirror - mirrors ubi volumes.\n"; + +static const char copyright [] __attribute__((unused)) = +	"(C) IBM Coorporation 2007"; + + +static struct argp_option options[] = { +	{ name: "copyright", key: 'c', arg: NULL,    flags: 0, +	  doc: "Print copyright information.", +	  group: 1 }, + +	{ name: "side", key: 's', arg: "<seqnum>",    flags: 0, +	  doc: "Use the side <seqnum> as source.", +	  group: 1 }, + +	{ name: NULL, key: 0, arg: NULL, flags: 0, doc: NULL, group: 0 }, +}; + +typedef struct myargs { +	action_t action; +	int side; +	int vol_no;			/* index of current volume */ +	/* @FIXME replace by bootenv_list, makes live easier */ +	/* @FIXME remove the constraint of two entries in the array */ +	const char* vol[VOL_ARGS_MAX];	/* comma separated list of src/dst +					   volumes */ +	char *arg1; +	char **options;		/* [STRING...] */ +} myargs; + +static int +get_update_side(const char* str) +{ +	uint32_t i = strtoul(str, NULL, 0); + +	if ((i != 0) && (i != 1)) { +		return -1; +	} +	return i; +} + + +static error_t +parse_opt(int key, char *arg, struct argp_state *state) +{ +	int err = 0; + +	myargs *args = state->input; + +	switch (key) { +	case 'c': +		err_msg("%s\n", copyright); +		ABORT_ARGP; +		break; +	case 's': +		args->side = get_update_side(arg); +		if (args->side < 0) { +			err_msg("Unsupported seqnum: %s.\n" +				 "Supported seqnums are '0' and '1'\n", arg); +			ERR_ARGP; +		} +		break; +	case ARGP_KEY_ARG: +		/* only two entries allowed */ +		if (args->vol_no >= VOL_ARGS_MAX) { +			err_msg("\n"); +			argp_usage(state); +			ERR_ARGP; +		} +		args->vol[(args->vol_no)++] = arg; +		break; +	case ARGP_KEY_END: +		if (err) { +			err_msg("\n"); +			argp_usage(state); +			ERR_ARGP; +		} +		break; +	default: +		return(ARGP_ERR_UNKNOWN); +	} + +	return 0; +} + +static struct argp argp = { +	options:     options, +	parser:	     parse_opt, +	args_doc:    "<source> <destination>", +	doc:	     doc, +	children:    NULL, +	help_filter: NULL, +	argp_domain: NULL, +}; + + +int +main(int argc, char **argv) { +	int rc = 0; +	unsigned int ids[VOL_ARGS_MAX]; +	char err_buf[1024]; + +	myargs args = { +		.action = ACT_NORMAL, +		.side = -1, +		.vol_no = 0, +		.vol = {"", ""}, +		.options = NULL, +	}; + +	argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &args); +	if (args.action == ACT_ARGP_ERR) { +		rc = 127; +		goto err; +	} +	if (args.action == ACT_ARGP_ABORT) { +		rc = 126; +		goto out; +	} +	if (args.vol_no < VOL_ARGS_MAX) { +		fprintf(stderr, "missing volume number for %s\n", +			args.vol_no == 0 ? "source and target" : "target"); +		rc = 125; +		goto out; +	} +	for( rc = 0; rc < args.vol_no; ++rc){ +		char *endp; +		ids[rc] = strtoul(args.vol[rc], &endp, 0); +		if( *endp != '\0' ){ +			fprintf(stderr, "invalid volume number %s\n", +					args.vol[rc]); +			rc = 125; +			goto out; +		} +	} +	rc = ubimirror(EXAMPLE_UBI_DEVICE, args.side, ids, args.vol_no, +		       err_buf, sizeof(err_buf)); +	if( rc ){ +		err_buf[sizeof err_buf - 1] = '\0'; +		fprintf(stderr, err_buf); +		if( rc < 0 ) +			rc = -rc; +	} + out: + err: +	return rc; +} diff --git a/ubi-utils/src/ubimkvol/ubimkvol.c b/ubi-utils/src/ubimkvol/ubimkvol.c new file mode 100644 index 0000000..f929252 --- /dev/null +++ b/ubi-utils/src/ubimkvol/ubimkvol.c @@ -0,0 +1,252 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * An utility to create UBI volumes. + * + * Author: Artem B. Bityutskiy <dedekind@oktetlabs.ru> + * + * 1.0 Initial release + * 1.1 Does not support erase blocks anymore. This is replaced by + *     the number of bytes. + */ + +#include <stdio.h> +#include <stdint.h> +#include <getopt.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include <libubi.h> + +#define VERSION "1.1" + +static void usage(void); +static int param_sanity_check(ubi_lib_t lib); +static int parse_options(int argc, char * const argv[]); + +/* + * The variables below  are set by command line arguments. + */ +static int vol_type = UBI_DYNAMIC_VOLUME; +static int devn = -1; +static long long bytes = 0; +static int alignment = 1; +static int vol_id = UBI_VOL_NUM_AUTO; +static char *name = NULL; +static int nlen = 0; + +int main(int argc, char * const argv[]) +{ +	int err; +	ubi_lib_t lib; + +	err = parse_options(argc, argv); +	if (err) { +		fprintf(stderr, "Wrong options ...\n"); +		return err == 1 ? 0 : -1; +	} + +	if (devn == -1) { +		fprintf(stderr, "Device number was not specified\n"); +		fprintf(stderr, "Use -h option for help\n"); +		return -1; +	} + +	err = ubi_open(&lib); +	if (err) { +		perror("Cannot open libubi"); +		return -1; +	} + +	err = param_sanity_check(lib); +	if (err) { +		perror("Input parameters check"); +		fprintf(stderr, "Use -h option for help\n"); +		goto out_libubi; +	} + +	err = ubi_mkvol(lib, devn, vol_id, vol_type, bytes, alignment, name); +	if (err < 0) { +		perror("Cannot create volume"); +		fprintf(stderr, "  err=%d\n", err); +		goto out_libubi; +	} + +	/* printf("Created volume %d, %lld bytes, type %s, name %s\n", +	   vol_id, bytes, vol_type == UBI_DYNAMIC_VOLUME ? +	   "dynamic" : "static", name); */ + +	vol_id = err; +	ubi_close(&lib); +	return 0; + +out_libubi: +	ubi_close(&lib); +	return -1; +} + +/* 'getopt()' option string */ +static const char *optstring = "ht:s:n:N:d:a:"; + +static int parse_options(int argc, char * const argv[]) +{ +	int opt = 0; + +	while (opt != -1) { +		char *endp; + +		opt = getopt(argc, argv, optstring); + +		switch (opt) { +		case 'h': +			usage(); +			return 1; +		case 't': +			if (!strcmp(optarg, "dynamic")) +				vol_type = UBI_DYNAMIC_VOLUME; +			else if (!strcmp(optarg, "static")) +				vol_type = UBI_STATIC_VOLUME; +			else { +				fprintf(stderr, "Bad volume type: \"%s\"\n", +					optarg); +				goto out; +			} +			break; +		case 's': +			bytes = strtoull(optarg, &endp, 0); +			if (endp == optarg || bytes < 0) { +				fprintf(stderr, "Bad volume size: \"%s\"\n", +					optarg); +				goto out; +			} +			if (endp != '\0') { +				if (strcmp(endp, "KiB") == 0) +					bytes *= 1024; +				else if (strcmp(endp, "MiB") == 0) +					bytes *= 1024*1024; +			} +			break; +		case 'a': +			alignment = strtoul(optarg, &endp, 0); +			if (*endp != '\0' || endp == optarg || +			    alignment <= 0) { +				fprintf(stderr, "Bad volume alignment: " +					"\"%s\"\n", optarg); +				goto out; +			} +			break; +		case 'd': +			devn = strtoul(optarg, &endp, 0); +			if (*endp != '\0' || endp == optarg || devn < 0) { +				fprintf(stderr, "Bad UBI device number: " +					"\"%s\"\n", optarg); +				goto out; +			} +			break; +		case 'n': +			vol_id = strtoul(optarg, &endp, 0); +			if (*endp != '\0' || endp == optarg || +			    (vol_id < 0 && vol_id != UBI_DYNAMIC_VOLUME)) { +				fprintf(stderr, "Bad volume ID: " +					"\"%s\"\n", optarg); +				goto out; +			} +			break; +		case 'N': +			name = optarg; +			nlen = strlen(name); +			break; + +		case ':': +			fprintf(stderr, "Parameter is missing\n"); +			goto out; +		case '?': +			fprintf(stderr, "Unknown parameter\n"); +			goto out; +		case -1: +			break; +		default: +			fprintf(stderr, "Internal error\n"); +			goto out; +		} +	} + +	return 0; + + out: +	errno = EINVAL; +	return -1; +} + +static int param_sanity_check(ubi_lib_t lib) +{ +	int err, len; +	struct ubi_info ubi; + +	if (bytes == 0) { +		fprintf(stderr, "Volume size was not specified\n"); +		goto out; +	} + +	if (name == NULL) { +		fprintf(stderr, "Volume name was not specified\n"); +		goto out; +	} + +	err = ubi_get_info(lib, &ubi); +	if (err) +		return -1; + +	if (devn >= ubi.dev_count) { +		fprintf(stderr, "Device %d does not exist\n", devn); +		goto out; +	} + +	len = strlen(name); +	if (len > ubi.nlen_max) { +		fprintf(stderr, "Too long name (%d symbols), max is %d\n", +			len, ubi.nlen_max); +		goto out; +	} + +	return 0; + +out: +	errno = EINVAL; +	return -1; +} + +static void usage(void) +{ +	printf("Usage: ubi_mkvol OPTIONS\n" +	       "Version: " VERSION "\n" +	       "The command line options:\n" +	       "\t-h - this help message\n" +	       "\t-d - UBI device number\n" +	       "\t-t TYPE  - volume type (dynamic, static) " +	       "(default is dynamic)\n" +	       "\t-n VOLID - volume ID to assign to the new volume. If not" +	       "specified, \n" +	       "\t           the volume ID will be assigned automatically\n" +	       "\t-s BYTES - volume size in bytes, " +	       "kilobytes (KiB) or megabytes (MiB)\n" +	       "\t-N NAME  - volume name\n" +	       "\t-a ALIGNMENT - volume alignment (default is 1)\n"); +} diff --git a/ubi-utils/src/ubirmvol/ubirmvol.c b/ubi-utils/src/ubirmvol/ubirmvol.c new file mode 100644 index 0000000..fc5ada5 --- /dev/null +++ b/ubi-utils/src/ubirmvol/ubirmvol.c @@ -0,0 +1,172 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * An utility to create UBI volumes. + * + * Autor: Artem B. Bityutskiy <dedekind@oktetlabs.ru> + */ + +#include <stdio.h> +#include <stdint.h> +#include <getopt.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <libubi.h> + +static void usage(void); +static int param_sanity_check(ubi_lib_t lib); +static int parse_options(int argc, char * const argv[]); + +/* + * The below variables are set by command line options. + */ +static int vol_id = -1; +static int devn = -1; + +int main(int argc, char * const argv[]) +{ +	int err, old_errno; +	ubi_lib_t lib; + +	err = parse_options(argc, argv); +	if (err) +		return err == 1 ? 0 : -1; + +	if (devn == -1) { +		fprintf(stderr, "Device number was not specified\n"); +		fprintf(stderr, "Use -h option for help\n"); +		return -1; +	} + +	err = ubi_open(&lib); +	if (err) { +		perror("Cannot open libubi"); +		return -1; +	} + +	err = param_sanity_check(lib); +	if (err) { +		perror("Input parameters check"); +		fprintf(stderr, "Use -h option for help\n"); +		goto out_libubi; +	} + +	err = ubi_rmvol(lib, devn, vol_id); +	old_errno = errno; +	if (err < 0) { +		perror("Cannot remove volume"); +		fprintf(stderr, "    err=%d errno=%d\n", err, old_errno); +		goto out_libubi; +	} + +	return 0; + +out_libubi: +	ubi_close(&lib); +	return -1; +} + +/* 'getopt()' option string */ +static const char *optstring = "hd:n:"; + +static int parse_options(int argc, char * const argv[]) +{ +	int opt = 0; + +	while (opt != -1) { +		char *endp; + +		opt = getopt(argc, argv, optstring); + +		switch (opt) { +		case 'h': +			usage(); +			return 1; +		case 'n': +			vol_id = strtoul(optarg, &endp, 0); +			if (*endp != '\0' || endp == optarg || vol_id < 0) { +				fprintf(stderr, "Bad volume " +					"number: \"%s\"\n", optarg); +				goto out; +			} +			break; +		case 'd': +			devn = strtoul(optarg, &endp, 0); +			if (*endp != '\0' || endp == optarg || devn < 0) { +				fprintf(stderr, "Bad UBI device " +					"number: \"%s\"\n", optarg); +				goto out; +			} +			break; +		case ':': +			fprintf(stderr, "Parameter is missing\n"); +			goto out; +		case '?': +			fprintf(stderr, "Unknown parameter\n"); +			goto out; +		case -1: +			break; +		default: +			fprintf(stderr, "Internal error\n"); +			goto out; +		} +	} + +	return 0; + +out: +	errno = EINVAL; +	return -1; +} + +static int param_sanity_check(ubi_lib_t lib) +{ +	int err; +	struct ubi_info ubi; + +	if (vol_id == -1) { +		fprintf(stderr, "Volume ID was not specified\n"); +		goto out; +	} + +	err = ubi_get_info(lib, &ubi); +	if (err) +		return -1; + +	if (devn >= ubi.dev_count) { +		fprintf(stderr, "Device %d does not exist\n", devn); +		goto out; +	} + +	return 0; + +out: +	errno = EINVAL; +	return -1; +} + +static void usage(void) +{ +	printf("Usage: ubi_rmvol OPTIONS\n" +	       "Command line options:\n" +	       "\t-h - this help message\n" +	       "\t-d - UBI device number\n" +	       "\t-n VOLNUM - volume number to remove\n"); +} diff --git a/ubi-utils/src/ubiwritevol/ubiwritevol.c b/ubi-utils/src/ubiwritevol/ubiwritevol.c new file mode 100644 index 0000000..8fdbe37 --- /dev/null +++ b/ubi-utils/src/ubiwritevol/ubiwritevol.c @@ -0,0 +1,352 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Frank Haverkamp + * + * An utility to update UBI volumes. + */ + +#include <config.h> + +#include <argp.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdint.h> +#include <getopt.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include <libubi.h> + +#define MAXPATH		1024 +#define BUFSIZE		128 * 1024 +#define MIN(x,y)	((x)<(y)?(x):(y)) + +struct args { +	int device; +	int volume; +	int truncate; +	int broken_update; +	int bufsize; + +	/* special stuff needed to get additional arguments */ +	char *arg1; +	char **options;		/* [STRING...] */ +}; + +static struct args myargs = { +	.device = -1, +	.volume = -1, +	.truncate = 0, +	.broken_update = 0, +	.bufsize = BUFSIZE, +	.arg1 = NULL, +	.options = NULL, +}; + +static int verbose = 0; + +static error_t parse_opt (int key, char *arg, struct argp_state *state); + +const char *argp_program_bug_address = "<haver@vnet.ibm.com>"; + +static char doc[] = "\nVersion: " VERSION "\n\t" +	HOST_OS" "HOST_CPU" at "__DATE__" "__TIME__"\n" +	"\nWrite to UBI Volume.\n"; + +static struct argp_option options[] = { +	{ .name = "device", +	  .key = 'd', +	  .arg = "<device number>", +	  .flags = 0, +	  .doc = "UBI device", +	  .group = OPTION_ARG_OPTIONAL }, + +	{ .name = "volume", +	  .key = 'n', +	  .arg = "<volume id>", +	  .flags = 0, +	  .doc = "UBI volume id", +	  .group = OPTION_ARG_OPTIONAL }, + +	{ .name = "truncate", +	  .key = 't', +	  .arg = NULL, +	  .flags = 0, +	  .doc = "truncate volume", +	  .group = OPTION_ARG_OPTIONAL }, + +	{ .name = "broken-update", +	  .key = 'B', +	  .arg = NULL, +	  .flags = 0, +	  .doc = "broken update, this is for testing", +	  .group = OPTION_ARG_OPTIONAL }, + +	{ .name = NULL, .key = 0, .arg = NULL, .flags = 0, +	  .doc = NULL, .group = 0 }, +}; + +static struct argp argp = { +	.options = options, +	.parser = parse_opt, +	.args_doc = 0, +	.doc =	doc, +	.children = NULL, +	.help_filter = NULL, +	.argp_domain = NULL, +}; + +/* + * @brief Parse the arguments passed into the test case. + * + * @param key            The parameter. + * @param arg            Argument passed to parameter. + * @param state          Location to put information on parameters. + * + * @return error + * + * Get the `input' argument from `argp_parse', which we know is a + * pointer to our arguments structure. + */ +static error_t +parse_opt(int key, char *arg, struct argp_state *state) +{ +	struct args *args = state->input; + +	switch (key) { +	case 'v': /* --verbose=<level> */ +		verbose = strtoul(arg, (char **)NULL, 0); +		break; + +	case 'd': /* --device=<device number> */ +		args->device = strtol(arg, (char **)NULL, 0); +		break; + +	case 'b': /* --bufsize=<bufsize> */ +		args->bufsize = strtol(arg, (char **)NULL, 0); +		if (args->bufsize <= 0) +			args->bufsize = BUFSIZE; +		break; + +	case 't': /* --truncate */ +		args->truncate = 1; +		break; + +	case 'B': /* --broken-update */ +		args->broken_update = 1; +		break; + +	case 'n': /* --volume=<volume id> */ +		args->volume = strtol(arg, (char **)NULL, 0); +		break; + +	case ARGP_KEY_NO_ARGS: +		/* argp_usage(state); */ +		break; + +	case ARGP_KEY_ARG: +		args->arg1 = arg; +		/* Now we consume all the rest of the arguments. +                   `state->next' is the index in `state->argv' of the +                   next argument to be parsed, which is the first STRING +                   we're interested in, so we can just use +                   `&state->argv[state->next]' as the value for +                   arguments->strings. + +                   _In addition_, by setting `state->next' to the end +                   of the arguments, we can force argp to stop parsing here and +                   return. */ + +		args->options = &state->argv[state->next]; +		state->next = state->argc; +		break; + +	case ARGP_KEY_END: +		/* argp_usage(state); */ +		break; + +	default: +		return(ARGP_ERR_UNKNOWN); +	} + +	return 0; +} + +/** + * @bytes bytes must be always 0, if not 0 this is a testcase for a + * broken volume update where data is promissed to be written, but for + * some reason nothing is written. The volume is unusable after this. + */ +static int +ubi_truncate_volume(struct args *args, int64_t bytes) +{ +	int rc, ofd; +	char path[MAXPATH]; +	int old_errno; + +	snprintf(path, MAXPATH-1, "/dev/ubi%d_%d", args->device, args->volume); +	path[MAXPATH-1] = '\0'; + +	ofd = open(path, O_RDWR); +	if (ofd < 0) { +		fprintf(stderr, "Cannot open volume %s\n", path); +		exit(EXIT_FAILURE); +	} +	rc = ioctl(ofd, UBI_IOCVOLUP, &bytes); +	old_errno = errno; +	if (rc < 0) { +		perror("UBI volume update ioctl"); +		fprintf(stderr, "    rc=%d errno=%d\n", rc, old_errno); +		exit(EXIT_FAILURE); +	} +	close(ofd); +	return 0; +} + +static ssize_t ubi_write(int fd, const void *buf, size_t count) +{ +	int rc; +	int len = count; + +	while (len) { +		rc = write(fd, buf, len); +		if (rc == -1) { +			if (errno == EINTR) +				continue; /* try again */ +			perror("write error"); +			return rc; +		} + +		len -= rc; +		buf += rc; +	} +	return count; +} + +static int +ubi_update_volume(struct args *args) +{ +	int rc, ofd; +	FILE *ifp = NULL; +	struct stat st; +	int size = 0; +	char *fname = args->arg1; +	char path[MAXPATH]; +	char *buf; +	int64_t bytes = 0; +	int old_errno; + +	buf = malloc(args->bufsize); +	if (!buf) { +		perror("Out of memory"); +		exit(EXIT_FAILURE); +	} + +	if (fname == NULL) { +		fprintf(stderr, "Please specify an existing file.\n"); +		exit(EXIT_FAILURE); +	} + +	rc = stat(fname, &st); +	if (rc < 0) { +		fprintf(stderr, "Cannot stat input file %s\n", fname); +		exit(EXIT_FAILURE); +	} +	bytes = size = st.st_size; + +	ifp = fopen(fname, "r"); +	if (!ifp) +		exit(EXIT_FAILURE); + +	snprintf(path, MAXPATH-1, "/dev/ubi%d_%d", args->device, args->volume); +	path[MAXPATH-1] = '\0'; + +	ofd = open(path, O_RDWR); +	if (ofd < 0) { +		fprintf(stderr, "Cannot open UBI volume %s\n", path); +		exit(EXIT_FAILURE); +	} + +	rc = ioctl(ofd, UBI_IOCVOLUP, &bytes); +	old_errno = errno; +	if (rc < 0) { +		perror("UBI volume update ioctl"); +		fprintf(stderr, "    rc=%d errno=%d\n", rc, old_errno); +		exit(EXIT_FAILURE); +	} + +	while (size > 0) { +		ssize_t tocopy = MIN(args->bufsize, size); + +		rc = fread(buf, tocopy, 1, ifp); +		if (rc != 1) { +			perror("Could not read everything."); +			exit(EXIT_FAILURE); +		} + +		rc = ubi_write(ofd, buf, tocopy); +		old_errno = errno; +		if (rc != tocopy) { +			perror("Could not write to device"); +			fprintf(stderr, "    rc=%d errno=%d\n", rc, old_errno); +			exit(EXIT_FAILURE); +		} +		size -= tocopy; +	} + +	free(buf); +	fclose(ifp); +	rc = close(ofd); +	if (rc != 0) { +		perror("UBI volume close failed"); +		exit(EXIT_FAILURE); +	} +	return 0; +} + +int +main(int argc, char *argv[]) +{ +	int rc; + +	argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &myargs); + +	if (myargs.truncate) { +		rc = ubi_truncate_volume(&myargs, 0LL); +		if (rc < 0) +			exit(EXIT_FAILURE); +		exit(EXIT_SUCCESS); +	} +	if (myargs.broken_update) { +		rc = ubi_truncate_volume(&myargs, 1LL); +		if (rc < 0) +			exit(EXIT_FAILURE); +		exit(EXIT_SUCCESS); +	} +	rc = ubi_update_volume(&myargs); +	if (rc < 0) +		exit(EXIT_FAILURE); + +	exit(EXIT_SUCCESS); +} diff --git a/ubi-utils/src/unubi/unubi.c b/ubi-utils/src/unubi/unubi.c new file mode 100644 index 0000000..9cb1354 --- /dev/null +++ b/ubi-utils/src/unubi/unubi.c @@ -0,0 +1,391 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Frank Haverkamp + * + * An utility to decompose UBI images. Not yet finished ... + */ + +#include <config.h> +#include <argp.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdint.h> +#include <getopt.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include "crc32.h" +#include <mtd/ubi-header.h> + +#define MAXPATH		1024 +#define MIN(x,y)	((x)<(y)?(x):(y)) + +static uint32_t crc32_table[256]; + +struct args { +	const char *output_dir; +	uint32_t hdr_offs; +	uint32_t data_offs; +	uint32_t blocksize; + +	/* special stuff needed to get additional arguments */ +	char *arg1; +	char **options;		/* [STRING...] */ +}; + +static struct args myargs = { +	.output_dir = "unubi", +	.hdr_offs = 64, +	.data_offs = 128, +	.blocksize = 128 * 1024, +	.arg1 = NULL, +	.options = NULL, +}; + +static error_t parse_opt (int key, char *arg, struct argp_state *state); + +const char *argp_program_bug_address = "<haver@vnet.ibm.com>"; + +static char doc[] = "\nVersion: " VERSION "\n\t" +	HOST_OS" "HOST_CPU" at "__DATE__" "__TIME__"\n" +	"\nWrite to UBI Volume.\n"; + +static struct argp_option options[] = { +	{ .name = "dir", +	  .key = 'd', +	  .arg = "<output-dir>", +	  .flags = 0, +	  .doc = "output directory", +	  .group = OPTION_ARG_OPTIONAL }, + +	{ .name = "blocksize", +	  .key = 'b', +	  .arg = "<blocksize>", +	  .flags = 0, +	  .doc = "blocksize", +	  .group = OPTION_ARG_OPTIONAL }, + +	{ .name = "data-offs", +	  .key = 'x', +	  .arg = "<data-offs>", +	  .flags = 0, +	  .doc = "data offset", +	  .group = OPTION_ARG_OPTIONAL }, + +	{ .name = "hdr-offs", +	  .key = 'x', +	  .arg = "<hdr-offs>", +	  .flags = 0, +	  .doc = "hdr offset", +	  .group = OPTION_ARG_OPTIONAL }, + +	{ .name = NULL, .key = 0, .arg = NULL, .flags = 0, +	  .doc = NULL, .group = 0 }, +}; + +static struct argp argp = { +	.options = options, +	.parser = parse_opt, +	.args_doc = "image-file", +	.doc =	doc, +	.children = NULL, +	.help_filter = NULL, +	.argp_domain = NULL, +}; + +/* + * str_to_num - Convert string into number and cope with endings like + *              k, K, kib, KiB for kilobyte + *              m, M, mib, MiB for megabyte + */ +uint32_t str_to_num(char *str) +{ +	char *s = str; +	ulong num = strtoul(s, &s, 0); + +	if (*s != '\0') { +		if (strcmp(s, "KiB") == 0) +			num *= 1024; +		else if (strcmp(s, "MiB") == 0) +			num *= 1024*1024; +		else { +			fprintf(stderr, "WARNING: Wrong number format " +				"\"%s\", check your paramters!\n", str); +		} +	} +	return num; +} + +/* + * @brief Parse the arguments passed into the test case. + * + * @param key            The parameter. + * @param arg            Argument passed to parameter. + * @param state          Location to put information on parameters. + * + * @return error + * + * Get the `input' argument from `argp_parse', which we know is a + * pointer to our arguments structure. + */ +static error_t +parse_opt(int key, char *arg, struct argp_state *state) +{ +	struct args *args = state->input; + +	switch (key) { +	case 'b': /* --blocksize<blocksize> */ +		args->blocksize = str_to_num(arg); +		break; + +	case 'x': /* --data-offs=<data-offs> */ +		args->data_offs = str_to_num(arg); +		break; + +	case 'y': /* --hdr-offs=<hdr-offs> */ +		args->hdr_offs = str_to_num(arg); +		break; + +	case 'd': /* --dir=<output-dir> */ +		args->output_dir = arg; +		break; + +	case ARGP_KEY_NO_ARGS: +		/* argp_usage(state); */ +		break; + +	case ARGP_KEY_ARG: +		args->arg1 = arg; +		/* Now we consume all the rest of the arguments. +                   `state->next' is the index in `state->argv' of the +                   next argument to be parsed, which is the first STRING +                   we're interested in, so we can just use +                   `&state->argv[state->next]' as the value for +                   arguments->strings. + +                   _In addition_, by setting `state->next' to the end +                   of the arguments, we can force argp to stop parsing +                   here and return. */ + +		args->options = &state->argv[state->next]; +		state->next = state->argc; +		break; + +	case ARGP_KEY_END: +		/* argp_usage(state); */ +		break; + +	default: +		return(ARGP_ERR_UNKNOWN); +	} + +	return 0; +} + +static inline void +hexdump(FILE *fp, const void *p, size_t size) +{ +	int k; +	const uint8_t *buf = p; + +	for (k = 0; k < size; k++) { +		fprintf(fp, "%02x ", buf[k]); +		if ((k & 15) == 15) +			fprintf(fp, "\n"); +	} +} + +/* + * This was put together in 1.5 hours and this is exactly how it looks + * like! FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME + * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME + * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME + * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME + * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME + * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME + * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME + * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME + * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME + * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME + * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME + * FIXME FIXME FIXME FIXME FIXME FIXME! + */ +static int extract_volume(struct args *args, const uint8_t *buf, +			  int len, int volume, FILE *fp) +{ +	int i, rc; +	int nrblocks = len / args->blocksize; +	int max_lnum = -1, lnum = 0; +	const uint8_t *ptr; +	uint8_t **vol_tab; +	int *vol_len; + +	vol_tab = calloc(nrblocks, sizeof(uint8_t *)); +	vol_len = calloc(nrblocks, sizeof(int)); + +	if (!buf || !vol_tab || !vol_len) +		exit(EXIT_FAILURE); + +	for (i = 0, ptr = buf; i < nrblocks; i++, ptr += args->blocksize) { +		uint32_t crc; +		struct ubi_ec_hdr *ec_hdr = (struct ubi_ec_hdr *)ptr; +		struct ubi_vid_hdr *vid_hdr = NULL; +		uint8_t *data; + +		/* default */ +		vol_len[lnum] = args->blocksize - (2 * 1024); + +		/* Check UBI EC header */ +		crc = clc_crc32(crc32_table, UBI_CRC32_INIT, ec_hdr, +				UBI_EC_HDR_SIZE_CRC); +		if (crc != ubi32_to_cpu(ec_hdr->hdr_crc)) +			continue; + +		vid_hdr = (struct ubi_vid_hdr *) +			(ptr + ubi32_to_cpu(ec_hdr->vid_hdr_offset)); +		data = (uint8_t *)(ptr + ubi32_to_cpu(ec_hdr->data_offset)); + +		crc = clc_crc32(crc32_table, UBI_CRC32_INIT, vid_hdr, +				UBI_VID_HDR_SIZE_CRC); +		if (crc != ubi32_to_cpu(vid_hdr->hdr_crc)) +			continue; + +		if (volume == ubi32_to_cpu(vid_hdr->vol_id)) { + +			printf("****** block %4d volume %2d **********\n", +			       i, volume); + +			hexdump(stdout, ptr, 64); + +			printf("--- vid_hdr\n"); +			hexdump(stdout, vid_hdr, 64); + +			printf("--- data\n"); +			hexdump(stdout, data, 64); + +			lnum = ubi32_to_cpu(vid_hdr->lnum); +			vol_tab[lnum] = data; +			if (max_lnum < lnum) +				max_lnum = lnum; +			if (vid_hdr->vol_type == UBI_VID_STATIC) +				vol_len[lnum] = +					ubi32_to_cpu(vid_hdr->data_size); + +		} +	} + +	for (lnum = 0; lnum <= max_lnum; lnum++) { +		if (vol_tab[lnum]) { +			rc = fwrite(vol_tab[lnum], 1, vol_len[lnum], fp); +			if (ferror(fp) || (vol_len[lnum] != rc)) { +				perror("could not write file"); +				exit(EXIT_FAILURE); +			} +		} else { +			/* Fill up empty areas by 0xff, for static +			 * volumes this means they are broken! +			 */ +			for (i = 0; i < vol_len[lnum]; i++) { +				if (fputc(0xff, fp) == EOF) { +					perror("could not write char"); +					exit(EXIT_FAILURE); +				} +			} +		} +	} + +	free(vol_tab); +	free(vol_len); +	return 0; +} + +int +main(int argc, char *argv[]) +{ +	int len, rc; +	FILE *fp; +	struct stat file_info; +	uint8_t *buf; +	int i; + +	init_crc32_table(crc32_table); + +	argp_parse(&argp, argc, argv, ARGP_IN_ORDER, 0, &myargs); + +	if (!myargs.arg1) { +		fprintf(stderr, "Please specify input file!\n"); +		exit(EXIT_FAILURE); +	} + +	fp = fopen(myargs.arg1, "r"); +	if (!fp) { +		perror("Cannot open file"); +		exit(EXIT_FAILURE); +	} +	if (fstat(fileno(fp), &file_info) != 0) { +		fprintf(stderr, "Cannot fetch file size " +			"from input file.\n"); +	} +	len = file_info.st_size; +	buf = malloc(len); +	if (!buf) { +		perror("out of memory!"); +		exit(EXIT_FAILURE); +	} +	rc = fread(buf, 1, len, fp); +	if (ferror(fp) || (len != rc)) { +		perror("could not read file"); +		exit(EXIT_FAILURE); +	} +	if (!myargs.output_dir) { +		fprintf(stderr, "No output directory specified!\n"); +		exit(EXIT_FAILURE); +	} + +	rc = mkdir(myargs.output_dir, 0777); +	if (rc && errno != EEXIST) { +		perror("Cannot create output directory"); +		exit(EXIT_FAILURE); +	} +	for (i = 0; i < 32; i++) { +		char fname[1024]; +		FILE *fpout; + +		printf("######### VOLUME %d ############################\n", +		       i); + +		sprintf(fname, "%s/ubivol_%d.bin", myargs.output_dir, i); +		fpout = fopen(fname, "w+"); +		if (!fpout) { +			perror("Cannot open file"); +			exit(EXIT_FAILURE); +		} +		extract_volume(&myargs, buf, len, i, fpout); +		fclose(fpout); +	} + +	fclose(fp); +	free(buf); +	exit(EXIT_SUCCESS); +}  | 
