diff options
Diffstat (limited to 'ubi-utils/src/pfi2bin')
| -rw-r--r-- | ubi-utils/src/pfi2bin/pfi2bin.c | 678 | 
1 files changed, 678 insertions, 0 deletions
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; +}  | 
