From 86dd9696d3a1acb036d65909d93d73455b8aed3a Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Tue, 19 Feb 2008 14:54:17 +0200 Subject: Revert ubi-tools changes Restore ubi tools to their "original" state, which means to the state they were before I stareted cleaning them up. Instead, create a "new-utils" subdirectory and move my work there. Signed-off-by: Artem Bityutskiy --- ubi-utils/src/bin2nand.c | 344 ++++++++++ ubi-utils/src/bootenv.c | 1032 ++++++++++++++++++++++++++++ ubi-utils/src/bootenv.h | 434 ++++++++++++ ubi-utils/src/common.c | 176 ----- ubi-utils/src/common.h | 71 -- ubi-utils/src/config.h | 28 + ubi-utils/src/crc32.c | 156 ++--- ubi-utils/src/crc32.h | 47 +- ubi-utils/src/dictionary.c | 405 ----------- ubi-utils/src/dictionary.h | 174 ----- ubi-utils/src/eb_chain.c | 281 ++++++++ ubi-utils/src/error.c | 240 +++++++ ubi-utils/src/error.h | 84 +++ ubi-utils/src/example_ubi.h | 28 + ubi-utils/src/hashmap.c | 412 ++++++++++++ ubi-utils/src/hashmap.h | 49 ++ ubi-utils/src/libiniparser.c | 646 ------------------ ubi-utils/src/libpfiflash.c | 1325 ++++++++++++++++++++++++++++++++++++ ubi-utils/src/libubi.c | 1444 +++++++++++++++++----------------------- ubi-utils/src/libubi_int.h | 100 ++- ubi-utils/src/libubigen.c | 660 +++++++++++------- ubi-utils/src/libubimirror.c | 237 +++++++ ubi-utils/src/list.c | 149 +++++ ubi-utils/src/list.h | 56 ++ ubi-utils/src/mkbootenv.c | 168 +++++ ubi-utils/src/nand2bin.c | 493 ++++++++++++++ ubi-utils/src/nandcorr.c | 95 +++ ubi-utils/src/nandecc.c | 159 +++++ ubi-utils/src/nandecc.h | 29 + ubi-utils/src/pddcustomize.c | 509 ++++++++++++++ ubi-utils/src/peb.c | 116 ++++ ubi-utils/src/peb.h | 41 ++ ubi-utils/src/pfi.c | 458 +++++++++++++ ubi-utils/src/pfi.h | 244 +++++++ ubi-utils/src/pfi2bin.c | 682 +++++++++++++++++++ ubi-utils/src/pfiflash.c | 264 ++++++++ ubi-utils/src/pfiflash.h | 76 +++ ubi-utils/src/pfiflash_error.h | 75 +++ ubi-utils/src/reader.c | 482 ++++++++++++++ ubi-utils/src/reader.h | 87 +++ ubi-utils/src/ubiattach.c | 205 ------ ubi-utils/src/ubicrc32.c | 166 ++--- ubi-utils/src/ubidetach.c | 181 ----- ubi-utils/src/ubigen.c | 359 ++++++++++ ubi-utils/src/ubigen.h | 149 +++++ ubi-utils/src/ubimirror.c | 213 ++++++ ubi-utils/src/ubimirror.h | 66 ++ ubi-utils/src/ubimkvol.c | 424 +++++++----- ubi-utils/src/ubinfo.c | 409 ------------ ubi-utils/src/ubinize.c | 549 --------------- ubi-utils/src/ubirmvol.c | 223 ++++--- ubi-utils/src/ubiupdate.c | 295 -------- ubi-utils/src/ubiupdatevol.c | 336 ++++++++++ ubi-utils/src/unubi.c | 1024 ++++++++++++++++++++++++++++ ubi-utils/src/unubi_analyze.c | 463 +++++++++++++ ubi-utils/src/unubi_analyze.h | 87 +++ 56 files changed, 13017 insertions(+), 4688 deletions(-) create mode 100644 ubi-utils/src/bin2nand.c create mode 100644 ubi-utils/src/bootenv.c create mode 100644 ubi-utils/src/bootenv.h delete mode 100644 ubi-utils/src/common.c delete mode 100644 ubi-utils/src/common.h create mode 100644 ubi-utils/src/config.h delete mode 100644 ubi-utils/src/dictionary.c delete mode 100644 ubi-utils/src/dictionary.h create mode 100644 ubi-utils/src/eb_chain.c create mode 100644 ubi-utils/src/error.c create mode 100644 ubi-utils/src/error.h create mode 100644 ubi-utils/src/example_ubi.h create mode 100644 ubi-utils/src/hashmap.c create mode 100644 ubi-utils/src/hashmap.h delete mode 100644 ubi-utils/src/libiniparser.c create mode 100644 ubi-utils/src/libpfiflash.c create mode 100644 ubi-utils/src/libubimirror.c create mode 100644 ubi-utils/src/list.c create mode 100644 ubi-utils/src/list.h create mode 100644 ubi-utils/src/mkbootenv.c create mode 100644 ubi-utils/src/nand2bin.c create mode 100644 ubi-utils/src/nandcorr.c create mode 100644 ubi-utils/src/nandecc.c create mode 100644 ubi-utils/src/nandecc.h create mode 100644 ubi-utils/src/pddcustomize.c create mode 100644 ubi-utils/src/peb.c create mode 100644 ubi-utils/src/peb.h create mode 100644 ubi-utils/src/pfi.c create mode 100644 ubi-utils/src/pfi.h create mode 100644 ubi-utils/src/pfi2bin.c create mode 100644 ubi-utils/src/pfiflash.c create mode 100644 ubi-utils/src/pfiflash.h create mode 100644 ubi-utils/src/pfiflash_error.h create mode 100644 ubi-utils/src/reader.c create mode 100644 ubi-utils/src/reader.h delete mode 100644 ubi-utils/src/ubiattach.c delete mode 100644 ubi-utils/src/ubidetach.c create mode 100644 ubi-utils/src/ubigen.c create mode 100644 ubi-utils/src/ubigen.h create mode 100644 ubi-utils/src/ubimirror.c create mode 100644 ubi-utils/src/ubimirror.h delete mode 100644 ubi-utils/src/ubinfo.c delete mode 100644 ubi-utils/src/ubinize.c delete mode 100644 ubi-utils/src/ubiupdate.c create mode 100644 ubi-utils/src/ubiupdatevol.c create mode 100644 ubi-utils/src/unubi.c create mode 100644 ubi-utils/src/unubi_analyze.c create mode 100644 ubi-utils/src/unubi_analyze.h (limited to 'ubi-utils/src') diff --git a/ubi-utils/src/bin2nand.c b/ubi-utils/src/bin2nand.c new file mode 100644 index 0000000..c7c7ccc --- /dev/null +++ b/ubi-utils/src/bin2nand.c @@ -0,0 +1,344 @@ +/* + * Copyright (c) International Business Machines Corp., 2007 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: 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) + * 1.4 Removed argp because we want to use uClibc. + * 1.5 Minor cleanup + * 1.6 written variable not initialized (-j did not work) (haver) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "error.h" +#include "config.h" +#include "nandecc.h" + +#define PROGRAM_VERSION "1.6" + +#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 + +static char doc[] = "\nVersion: " PROGRAM_VERSION "\n" + "bin2nand - a tool for adding OOB information to a " + "binary input file.\n"; + +static const char *optionsstr = +" -c, --copyright Print copyright informatoin.\n" +" -j, --padding= Padding in Byte/Mi/ki. Default = no padding\n" +" -p, --pagesize= Pagesize in Byte/Mi/ki. Default = 2048\n" +" -o, --output= Output filename. Interleaved Data/OOB if\n" +" output-oob not specified.\n" +" -q, --output-oob= Write OOB data in separate file.\n" +" -?, --help Give this help list\n" +" --usage Give a short usage message\n" +" -V, --version Print program version\n"; + +static const char *usage = +"Usage: bin2nand [-c?V] [-j ] [-p ] [-o ] [-q ]\n" +" [--copyright] [--padding=] [--pagesize=]\n" +" [--output=] [--output-oob=] [--help] [--usage]\n" +" [--version]\n"; + +struct option long_options[] = { + { .name = "copyright", .has_arg = 0, .flag = NULL, .val = 'c' }, + { .name = "padding", .has_arg = 1, .flag = NULL, .val = 'j' }, + { .name = "pagesize", .has_arg = 1, .flag = NULL, .val = 'p' }, + { .name = "output", .has_arg = 1, .flag = NULL, .val = 'o' }, + { .name = "output-oob", .has_arg = 1, .flag = NULL, .val = 'q' }, + { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' }, + { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 }, + { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, + { NULL, 0, NULL, 0} +}; + +static const char copyright [] __attribute__((unused)) = + "Copyright IBM Corp. 2006"; + +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 int +parse_opt(int argc, char **argv, myargs *args) +{ + char* endp; + + while (1) { + int key; + + key = getopt_long(argc, argv, "cj:p:o:q:?V", long_options, NULL); + if (key == -1) + break; + + switch (key) { + case 'p': /* pagesize */ + args->pagesize = (size_t) + ustrtoull(optarg, &endp, 0); + CHECK_ENDP("p", endp); + break; + case 'j': /* padding */ + args->padding = (size_t) + ustrtoull(optarg, &endp, 0); + CHECK_ENDP("j", endp); + break; + case 'o': /* output */ + args->file_out_data = optarg; + break; + case 'q': /* output oob */ + args->file_out_oob = optarg; + break; + case '?': /* help */ + printf("%s", doc); + printf("%s", optionsstr); + exit(0); + break; + case 'V': + printf("%s\n", PROGRAM_VERSION); + exit(0); + break; + case 'c': + printf("%s\n", copyright); + exit(0); + default: + printf("%s", usage); + exit(-1); + } + } + + if (optind < argc) { + args->fp_in = fopen(argv[optind++], "rb"); + if ((args->fp_in) == NULL) { + err_quit("Cannot open file %s for input\n", + argv[optind++]); + } + } + + return 0; +} + +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) { + i = fwrite(oobbuf, 1, oobsize, fp_oob); + if (ferror(fp_oob)) { + err_msg("IO error\n"); + return -EIO; + } + } + else { + i = 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 = 0, read; + myargs args = { + .action = ACT_NORMAL, + .pagesize = PAGESIZE, + .padding = PADDING, + .fp_in = NULL, + .file_out_data = NULL, + .file_out_oob = NULL, + }; + + FILE* fp_out_data = stdout; + FILE* fp_out_oob = NULL; + + parse_opt(argc, argv, &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 (args.file_out_data) { + 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 (args.file_out_oob) { + 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); + if (args.file_out_data) + remove(args.file_out_data); + if (args.file_out_oob) + remove(args.file_out_oob); + } + return rc; +} diff --git a/ubi-utils/src/bootenv.c b/ubi-utils/src/bootenv.c new file mode 100644 index 0000000..a6dd4de --- /dev/null +++ b/ubi-utils/src/bootenv.c @@ -0,0 +1,1032 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Oliver Lohmann + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hashmap.h" +#include "error.h" + +#include +#include "crc32.h" + +#define ubi_unused __attribute__((unused)) + +#define BOOTENV_MAXLINE 512 /* max line size of a bootenv.txt file */ + +/* Structures */ +struct bootenv { + hashmap_t map; ///< Pointer to hashmap which holds data structure. +}; + +struct bootenv_list { + hashmap_t head; ///< Pointer to list which holds the data structure. +}; + +/** + * @brief Remove the '\n' from a given line. + * @param line Input/Output line. + * @param size Size of the line. + * @param fp File Pointer. + * @return 0 + * @return or error + */ +static int +remove_lf(char *line, size_t size, FILE* fp) +{ + size_t i; + + for (i = 0; i < size; i++) { + if (line[i] == '\n') { + line[i] = '\0'; + return 0; + } + } + + if (!feof(fp)) { + return BOOTENV_EINVAL; + } + + return 0; +} + +/** + * @brief Determine if a line contains only WS. + * @param line The line to process. + * @param size Size of input line. + * @return 1 Yes, only WS. + * @return 0 No, contains data. + */ +static int +is_ws(const char *line, size_t size) +{ + size_t i = 0; + + while (i < size) { + switch (line[i]) { + case '\n': + return 1; + case '#': + return 1; + case ' ': + i++; + continue; + case '\t': + i++; + continue; + default: /* any other char -> no cmnt */ + return 0; + } + } + + return 0; +} + + +/* ------------------------------------------------------------------------- */ + +/** + * @brief Build a list from a comma seperated value string. + * @param list Pointer to hashmap structure which shall store + * the list. + * @param value Comma seperated value string. + * @return 0 + * @return or error. + */ +static int +build_list_definition(hashmap_t list, const char *value) +{ + int rc = 0; + char *str = NULL; + char *ptr = NULL; + size_t len, i, j; + + /* str: val1,val2 , val4,...,valN */ + len = strlen(value); + str = (char*) malloc((len+1) * sizeof(char)); + + /* 1. reformat string: remove spaces */ + for (i = 0, j = 0; i < len; i++) { + if (value[i] == ' ') + continue; + + str[j] = value[i]; + j++; + } + str[j] = '\0'; + + /* str: val1,val2,val4,...,valN\0*/ + /* 2. replace ',' seperator with '\0' */ + len = strlen(str); + for (i = 0; i < len; i++) { + if (str[i] == ',') { + str[i] = '\0'; + } + } + + /* str: val1\0val2\0val4\0...\0valN\0*/ + /* 3. insert definitions into a hash map, using it like a list */ + i = j = 0; + ptr = str; + while (((i = strlen(ptr)) > 0) && (j < len)) { + rc = hashmap_add(list, ptr, ""); + if (rc != 0) { + free(str); + return rc; + } + j += i+1; + if (j < len) + ptr += i+1; + } + + free(str); + return rc; +} + +/** + * @brief Extract a key value pair and add it to a hashmap + * @param str Input string which contains a key value pair. + * @param env The updated handle which contains the new pair. + * @return 0 + * @return or error + * @note The input string format is: "key=value" + */ +static int +extract_pair(const char *str, bootenv_t env) +{ + int rc = 0; + char *key = NULL; + char *val = NULL; + + key = strdup(str); + if (key == NULL) + return -ENOMEM; + + val = strstr(key, "="); + if (val == NULL) { + rc = BOOTENV_EBADENTRY; + goto err; + } + + *val = '\0'; /* split strings */ + val++; + + rc = bootenv_set(env, key, val); + + err: + free(key); + return rc; +} + +int +bootenv_destroy(bootenv_t* env) +{ + int rc = 0; + + if (env == NULL || *env == NULL) + return -EINVAL; + + bootenv_t tmp = *env; + + rc = hashmap_free(tmp->map); + if (rc != 0) + return rc; + + free(tmp); + return rc; +} + +int +bootenv_create(bootenv_t* env) +{ + bootenv_t res; + res = (bootenv_t) calloc(1, sizeof(struct bootenv)); + + if (res == NULL) + return -ENOMEM; + + res->map = hashmap_new(); + + if (res->map == NULL) { + free(res); + return -ENOMEM; + } + + *env = res; + + return 0; +} + + +/** + * @brief Read a formatted buffer and scan it for valid bootenv + * key/value pairs. Add those pairs into a hashmap. + * @param env Hashmap which shall be used to hold the data. + * @param buf Formatted buffer. + * @param size Size of the buffer. + * @return 0 + * @return or error + */ +static int +rd_buffer(bootenv_t env, const char *buf, size_t size) +{ + const char *curr = buf; /* ptr to current key/value pair */ + uint32_t i, j; /* current length, chars processed */ + + if (buf[size - 1] != '\0') /* must end in '\0' */ + return BOOTENV_EFMT; + + for (j = 0; j < size; j += i, curr += i) { + /* strlen returns the size of the string upto + but not including the null terminator; + adding 1 to account for '\0' */ + i = strlen(curr) + 1; + + if (i == 1) + return 0; /* no string found */ + + if (extract_pair(curr, env) != 0) + return BOOTENV_EINVAL; + } + + return 0; +} + + +int +bootenv_read_crc(FILE* fp, bootenv_t env, size_t size, uint32_t* ret_crc) +{ + int rc; + char *buf = NULL; + size_t i = 0; + uint32_t crc32_table[256]; + + if ((fp == NULL) || (env == NULL)) + return -EINVAL; + + /* allocate temp buffer */ + buf = (char*) calloc(1, size * sizeof(char)); + if (buf == NULL) + return -ENOMEM; + + /* FIXME Andreas, please review this I removed size-1 and + * replaced it by just size, I saw the kernel image starting + * with a 0x0060.... and not with the 0x60.... what it should + * be. Is this a tools problem or is it a problem here where + * fp is moved not to the right place due to the former size-1 + * here. + */ + while((i < size) && (!feof(fp))) { + int c = fgetc(fp); + if (c == EOF) { + /* FIXME isn't this dangerous, to update + the boot envs with incomplete data? */ + buf[i++] = '\0'; + break; /* we have enough */ + } + if (ferror(fp)) { + rc = -EIO; + goto err; + } + + buf[i++] = (char)c; + } + + /* calculate crc to return */ + if (ret_crc != NULL) { + init_crc32_table(crc32_table); + *ret_crc = clc_crc32(crc32_table, UBI_CRC32_INIT, buf, size); + } + + /* transfer to hashmap */ + rc = rd_buffer(env, buf, size); + +err: + free(buf); + return rc; +} + + +/** + * If we have a single file containing the boot-parameter size should + * be specified either as the size of the file or as BOOTENV_MAXSIZE. + * If the bootparameter are in the middle of a file we need the exact + * length of the data. + */ +int +bootenv_read(FILE* fp, bootenv_t env, size_t size) +{ + return bootenv_read_crc(fp, env, size, NULL); +} + + +int +bootenv_read_txt(FILE* fp, bootenv_t env) +{ + int rc = 0; + char *buf = NULL; + char *line = NULL; + char *lstart = NULL; + char *curr = NULL; + size_t len; + size_t size; + + if ((fp == NULL) || (env == NULL)) + return -EINVAL; + + size = BOOTENV_MAXSIZE; + + /* allocate temp buffers */ + buf = (char*) calloc(1, size * sizeof(char)); + lstart = line = (char*) calloc(1, size * sizeof(char)); + if ((buf == NULL) || (line == NULL)) { + rc = -ENOMEM; + goto err; + } + + curr = buf; + while ((line = fgets(line, size, fp)) != NULL) { + if (is_ws(line, size)) { + continue; + } + rc = remove_lf(line, BOOTENV_MAXSIZE, fp); + if (rc != 0) { + goto err; + } + + /* copy new line to binary buffer */ + len = strlen(line); + if (len > size) { + rc = -EFBIG; + goto err; + } + size -= len; /* track remaining space */ + + memcpy(curr, line, len); + curr += len + 1; /* for \0 seperator */ + } + + rc = rd_buffer(env, buf, BOOTENV_MAXSIZE); +err: + if (buf != NULL) + free(buf); + if (lstart != NULL) + free(lstart); + return rc; +} + +static int +fill_output_buffer(bootenv_t env, char *buf, size_t buf_size_max ubi_unused, + size_t *written) +{ + int rc = 0; + size_t keys_size, i; + size_t wr = 0; + const char **keys = NULL; + const char *val = NULL; + + rc = bootenv_get_key_vector(env, &keys_size, 1, &keys); + if (rc != 0) + goto err; + + for (i = 0; i < keys_size; i++) { + if (wr > BOOTENV_MAXSIZE) { + rc = -ENOSPC; + goto err; + } + + rc = bootenv_get(env, keys[i], &val); + if (rc != 0) + goto err; + + wr += snprintf(buf + wr, BOOTENV_MAXSIZE - wr, + "%s=%s", keys[i], val); + wr++; /* for \0 */ + } + + *written = wr; + +err: + if (keys != NULL) + free(keys); + + return rc; +} + +int +bootenv_write_crc(FILE* fp, bootenv_t env, uint32_t* ret_crc) +{ + int rc = 0; + size_t size = 0; + char *buf = NULL; + uint32_t crc32_table[256]; + + if ((fp == NULL) || (env == NULL)) + return -EINVAL; + + buf = (char*) calloc(1, BOOTENV_MAXSIZE * sizeof(char)); + if (buf == NULL) + return -ENOMEM; + + + rc = fill_output_buffer(env, buf, BOOTENV_MAXSIZE, &size); + if (rc != 0) + goto err; + + /* calculate crc to return */ + if (ret_crc != NULL) { + init_crc32_table(crc32_table); + *ret_crc = clc_crc32(crc32_table, UBI_CRC32_INIT, buf, size); + } + + if (fwrite(buf, size, 1, fp) != 1) { + rc = -EIO; + goto err; + } + +err: + if (buf != NULL) + free(buf); + return rc; +} + +int +bootenv_write(FILE* fp, bootenv_t env) +{ + return bootenv_write_crc(fp, env, NULL); +} + +int +bootenv_compare(bootenv_t first, bootenv_t second) +{ + int rc; + size_t written_first, written_second; + char *buf_first, *buf_second; + + if (first == NULL || second == NULL) + return -EINVAL; + + buf_first = malloc(BOOTENV_MAXSIZE); + if (!buf_first) + return -ENOMEM; + buf_second = malloc(BOOTENV_MAXSIZE); + if (!buf_second) { + rc = -ENOMEM; + goto err; + } + + rc = fill_output_buffer(first, buf_first, BOOTENV_MAXSIZE, + &written_first); + if (rc < 0) + goto err; + rc = fill_output_buffer(second, buf_second, BOOTENV_MAXSIZE, + &written_second); + if (rc < 0) + goto err; + + if (written_first != written_second) { + rc = 1; + goto err; + } + + rc = memcmp(buf_first, buf_second, written_first); + if (rc != 0) { + rc = 2; + goto err; + } + +err: + if (buf_first) + free(buf_first); + if (buf_second) + free(buf_second); + + return rc; +} + +int +bootenv_size(bootenv_t env, size_t *size) +{ + int rc = 0; + char *buf = NULL; + + if (env == NULL) + return -EINVAL; + + buf = (char*) calloc(1, BOOTENV_MAXSIZE * sizeof(char)); + if (buf == NULL) + return -ENOMEM; + + rc = fill_output_buffer(env, buf, BOOTENV_MAXSIZE, size); + if (rc != 0) + goto err; + +err: + if (buf != NULL) + free(buf); + return rc; +} + +int +bootenv_write_txt(FILE* fp, bootenv_t env) +{ + int rc = 0; + size_t size, wr, i; + const char **keys = NULL; + const char *key = NULL; + const char *val = NULL; + + if ((fp == NULL) || (env == NULL)) + return -EINVAL; + + rc = bootenv_get_key_vector(env, &size, 1, &keys); + if (rc != 0) + goto err; + + for (i = 0; i < size; i++) { + key = keys[i]; + rc = bootenv_get(env, key, &val); + if (rc != 0) + goto err; + + wr = fprintf(fp, "%s=%s\n", key, val); + if (wr != strlen(key) + strlen(val) + 2) { + rc = -EIO; + goto err; + } + } + +err: + if (keys != NULL) + free(keys); + return rc; +} + +int +bootenv_valid(bootenv_t env ubi_unused) +{ + /* @FIXME No sanity check implemented. */ + return 0; +} + +int +bootenv_copy_bootenv(bootenv_t in, bootenv_t *out) +{ + int rc = 0; + const char *tmp = NULL; + const char **keys = NULL; + size_t vec_size, i; + + if ((in == NULL) || (out == NULL)) + return -EINVAL; + + /* purge output var for sure... */ + rc = bootenv_destroy(out); + if (rc != 0) + return rc; + + /* create the new map */ + rc = bootenv_create(out); + if (rc != 0) + goto err; + + /* get the key list from the input map */ + rc = bootenv_get_key_vector(in, &vec_size, 0, &keys); + if (rc != 0) + goto err; + + if (vec_size != hashmap_size(in->map)) { + rc = BOOTENV_ECOPY; + goto err; + } + + /* make a deep copy of the hashmap */ + for (i = 0; i < vec_size; i++) { + rc = bootenv_get(in, keys[i], &tmp); + if (rc != 0) + goto err; + + rc = bootenv_set(*out, keys[i], tmp); + if (rc != 0) + goto err; + } + +err: + if (keys != NULL) + free(keys); + + return rc; +} + +/* ------------------------------------------------------------------------- */ + + +int +bootenv_pdd_keep(bootenv_t env_old, bootenv_t env_new, bootenv_t *env_res, + int *warnings, char *err_buf ubi_unused, + size_t err_buf_size ubi_unused) +{ + bootenv_list_t l_old = NULL; + bootenv_list_t l_new = NULL; + const char *pdd_old = NULL; + const char *pdd_new = NULL; + const char *tmp = NULL; + const char **vec_old = NULL; + const char **vec_new = NULL; + const char **pdd_up_vec = NULL; + size_t vec_old_size, vec_new_size, pdd_up_vec_size, i; + int rc = 0; + + if ((env_old == NULL) || (env_new == NULL) || (env_res == NULL)) + return -EINVAL; + + /* get the pdd strings, e.g.: + * pdd_old=a,b,c + * pdd_new=a,c,d,e */ + rc = bootenv_get(env_old, "pdd", &pdd_old); + if (rc != 0) + goto err; + rc = bootenv_get(env_new, "pdd", &pdd_new); + if (rc != 0) + goto err; + + /* put it into a list and then convert it to an vector */ + rc = bootenv_list_create(&l_old); + if (rc != 0) + goto err; + rc = bootenv_list_create(&l_new); + if (rc != 0) + goto err; + + rc = bootenv_list_import(l_old, pdd_old); + if (rc != 0) + goto err; + + rc = bootenv_list_import(l_new, pdd_new); + if (rc != 0) + goto err; + + rc = bootenv_list_to_vector(l_old, &vec_old_size, &vec_old); + if (rc != 0) + goto err; + + rc = bootenv_list_to_vector(l_new, &vec_new_size, &vec_new); + if (rc != 0) + goto err; + + rc = bootenv_copy_bootenv(env_new, env_res); + if (rc != 0) + goto err; + + /* calculate the update vector between the old and new pdd */ + pdd_up_vec = hashmap_get_update_key_vector(vec_old, vec_old_size, + vec_new, vec_new_size, &pdd_up_vec_size); + + if (pdd_up_vec == NULL) { + rc = -ENOMEM; + goto err; + } + + if (pdd_up_vec_size != 0) { + /* need to warn the user about the unset of + * some pdd/bootenv values */ + *warnings = BOOTENV_WPDD_STRING_DIFFERS; + + /* remove all entries in the new bootenv load */ + for (i = 0; i < pdd_up_vec_size; i++) { + bootenv_unset(*env_res, pdd_up_vec[i]); + } + } + + /* generate the keep array and copy old pdd values to new bootenv */ + for (i = 0; i < vec_old_size; i++) { + rc = bootenv_get(env_old, vec_old[i], &tmp); + if (rc != 0) { + rc = BOOTENV_EPDDINVAL; + goto err; + } + rc = bootenv_set(*env_res, vec_old[i], tmp); + if (rc != 0) { + goto err; + } + } + /* put the old pdd string into the result map */ + rc = bootenv_set(*env_res, "pdd", pdd_old); + if (rc != 0) { + goto err; + } + + +err: + if (vec_old != NULL) + free(vec_old); + if (vec_new != NULL) + free(vec_new); + if (pdd_up_vec != NULL) + free(pdd_up_vec); + + bootenv_list_destroy(&l_old); + bootenv_list_destroy(&l_new); + return rc; +} + + +int +bootenv_pdd_overwrite(bootenv_t env_old, bootenv_t env_new, + bootenv_t *env_res, int *warnings ubi_unused, + char *err_buf ubi_unused, size_t err_buf_size ubi_unused) +{ + if ((env_old == NULL) || (env_new == NULL) || (env_res == NULL)) + return -EINVAL; + + return bootenv_copy_bootenv(env_new, env_res); +} + +int +bootenv_pdd_merge(bootenv_t env_old, bootenv_t env_new, bootenv_t *env_res, + int *warnings ubi_unused, char *err_buf, size_t err_buf_size) +{ + if ((env_old == NULL) || (env_new == NULL) || (env_res == NULL)) + return -EINVAL; + + snprintf(err_buf, err_buf_size, "The PDD merge operation is not " + "implemented. Contact: "); + + 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/bootenv.h b/ubi-utils/src/bootenv.h new file mode 100644 index 0000000..8fecdbf --- /dev/null +++ b/ubi-utils/src/bootenv.h @@ -0,0 +1,434 @@ +#ifndef __BOOTENV_H__ +#define __BOOTENV_H__ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include /* FILE */ +#include +#include + +/* DOXYGEN DOCUMENTATION */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file bootenv.h + * @author oliloh@de.ibm.com + * @version 1.3 + * + * 1.3 Some renaming + */ + +/** + * @mainpage Usage + * + * @section intro Introduction + * This library provides all functionality to handle with the so-called + * platform description data (PDD) and the bootparameters defined in + * U-Boot. It is able to apply the defined PDD operations in PDD update + * scenarios. For more information about the PDD and bootparameter + * environment "bootenv" confer the PDD documentation. + * + * @section ret Return codes + * This library defines some return codes which will be delivered classified + * as warnings or errors. See the "Defines" section for details and numeric + * values. + * + * @section benv Bootenv format description + * There are two different input formats: + * - text files + * - binary files + * + * @subsection txt Text Files + * Text files have to be specified like: + * @verbatim key1=value1,value2,value7\n key2=value55,value1\n key4=value1\n@endverbatim + * + * @subsection bin Binary files + * Binary files have to be specified like: + * @verbatimkey1=value1,value2,value7\0key2=value55,value1\0... @endverbatim + * You can confer the U-Boot documentation for more details. + * + * @section benvlists Bootenv lists format description. + * Values referenced in the preceeding subsection can be + * defined like lists: + * @verbatim value1,value2,value3 @endverbatim + * There are some situation where a conversion of a comma + * seperated list can be useful, e.g. to get a list + * of defined PDD entries. + */ + +#define BOOTENV_MAXSIZE (1024 * 100) /* max 100kiB space for bootenv */ + +/** + * @def BOOTENV_ECRC + * @brief Given binary file is to large. + * @def BOOTENV_EFMT + * @brief Given bootenv section has an invalid format + * @def BOOTENV_EBADENTRY + * @brief Bad entry in the bootenv section. + * @def BOOTENV_EINVAL + * @brief Invalid bootenv defintion. + * @def BOOTENV_ENOPDD + * @brief Given bootenv sectoin has no PDD defintion string (pdd=...). + * @def BOOTENV_EPDDINVAL + * @brief Given bootenv section has an invalid PDD defintion. + * @def BOOTENV_ENOTIMPL + * @brief Functionality not implemented. + * @def BOOTENV_ECOPY + * @brief Bootenv memory copy error + * @def BOOTENV_ENOTFOUND + * @brief Given key has has no value. + * @def BOOTENV_EMAX + * @brief Highest error value. + */ +#define BOOTENV_ETOOBIG 1 +#define BOOTENV_EFMT 2 +#define BOOTENV_EBADENTRY 3 +#define BOOTENV_EINVAL 4 +#define BOOTENV_ENOPDD 5 +#define BOOTENV_EPDDINVAL 6 +#define BOOTENV_ENOTIMPL 7 +#define BOOTENV_ECOPY 8 +#define BOOTENV_ENOTFOUND 9 +#define BOOTENV_EMAX 10 + +/** + * @def BOOTENV_W + * @brief A warning which is handled internally as an error + * but can be recovered by manual effort. + * @def BOOTENV_WPDD_STRING_DIFFERS + * @brief The PDD strings of old and new PDD differ and + * can cause update problems, because new PDD values + * are removed from the bootenv section completely. + */ +#define BOOTENV_W 20 +#define BOOTENV_WPDD_STRING_DIFFERS 21 +#define BOOTENV_WMAX 22 /* highest warning value */ + + +typedef struct bootenv *bootenv_t; + /**< A bootenv library handle. */ + +typedef struct bootenv_list *bootenv_list_t; + /**< A handle for a value list. */ + +typedef int(*pdd_func_t)(bootenv_t, bootenv_t, bootenv_t*, + int*, char*, size_t); + + +/** + * @brief Get a new handle. + * @return 0 + * @return or error + * */ +int bootenv_create(bootenv_t *env); + +/** + * @brief Cleanup structure. + * @param env Bootenv structure which shall be destroyed. + * @return 0 + * @return or error + */ +int bootenv_destroy(bootenv_t *env); + +/** + * @brief Copy a bootenv handle. + * @param in The input bootenv. + * @param out The copied output bootenv. Discards old data. + * @return 0 + * @return or error + */ +int bootenv_copy_bootenv(bootenv_t in, bootenv_t *out); + +/** + * @brief Looks for a value inside the bootenv data. + * @param env Handle to a bootenv structure. + * @param key The key. + * @return NULL key not found + * @return !NULL ptr to value + */ +int bootenv_get(bootenv_t env, const char *key, const char **value); + + +/** + * @brief Looks for a value inside the bootenv data and converts it to num. + * @param env Handle to a bootenv structure. + * @param key The key. + * @param value A pointer to the resulting numerical value + * @return NULL key not found + * @return !NULL ptr to value + */ +int bootenv_get_num(bootenv_t env, const char *key, uint32_t *value); + +/** + * @brief Set a bootenv value by key. + * @param env Handle to a bootenv structure. + * @param key Key. + * @param value Value to set. + * @return 0 + * @return or error + */ +int bootenv_set(bootenv_t env, const char *key, const char *value); + +/** + * @brief Remove the given key (and its value) from a bootenv structure. + * @param env Handle to a bootenv structure. + * @param key Key. + * @return 0 + * @return or error + */ +int bootenv_unset(bootenv_t env, const char *key); + + +/** + * @brief Get a vector of all keys which are currently set + * within a bootenv handle. + * @param env Handle to a bootenv structure. + * @param size The size of the allocated array structure. + * @param sort Flag, if set the vector is sorted ascending. + * @return NULL on error. + * @return !NULL a pointer to the first element the allocated vector. + * @warning Free the allocate memory yourself! + */ +int bootenv_get_key_vector(bootenv_t env, size_t *size, int sort, + const char ***vector); + +/** + * @brief Calculate the size in bytes which are necessary to write the + * current bootenv section in a *binary file. + * @param env bootenv handle. + * @param size The size in bytes of the bootenv handle. + * @return 0 + * @return or ERROR. + */ +int bootenv_size(bootenv_t env, size_t *size); + +/** + * @brief Read a binary bootenv file. + * @param fp File pointer to input stream. + * @param env bootenv handle. + * @param size maximum data size. + * @return 0 + * @return or ERROR. + */ +int bootenv_read(FILE* fp, bootenv_t env, size_t size); + +/** + * @param ret_crc return value of crc of read data + */ +int bootenv_read_crc(FILE* fp, bootenv_t env, size_t size, uint32_t *ret_crc); + +/** + * @brief Read bootenv data from an text/ascii file. + * @param fp File pointer to ascii PDD file. + * @param env bootenv handle + * @return 0 + * @return or ERROR. + */ +int bootenv_read_txt(FILE* fp, bootenv_t env); + +/** + * @brief Write a bootenv structure to the given location (binary). + * @param fp Filepointer to binary file. + * @param env Bootenv structure which shall be written. + * @return 0 + * @return or error + */ +int bootenv_write(FILE* fp, bootenv_t env); + +/** + * @param ret_crc return value of crc of read data + */ +int bootenv_write_crc(FILE* fp, bootenv_t env, uint32_t* ret_crc); + +/** + * @brief Write a bootenv structure to the given location (text). + * @param fp Filepointer to text file. + * @param env Bootenv structure which shall be written. + * @return 0 + * @return or error + */ +int bootenv_write_txt(FILE* fp, bootenv_t env); + +/** + * @brief Compare bootenvs using memcmp(). + * @param first First bootenv. + * @param second Second bootenv. + * @return 0 if bootenvs are equal + * @return < 0 if error + * @return > 0 if unequal + */ +int bootenv_compare(bootenv_t first, bootenv_t second); + +/** + * @brief Prototype for a PDD handling funtion + */ + +/** + * @brief The PDD keep operation. + * @param env_old The old bootenv structure. + * @param env_new The new bootenv structure. + * @param env_res The result of PDD keep. + * @param warnings A flag which marks any warnings. + * @return 0 + * @return or error + * @note For a complete documentation about the algorithm confer the + * PDD documentation. + */ +int bootenv_pdd_keep(bootenv_t env_old, bootenv_t env_new, + bootenv_t *env_res, int *warnings, + char *err_buf, size_t err_buf_size); + + +/** + * @brief The PDD merge operation. + * @param env_old The old bootenv structure. + * @param env_new The new bootenv structure. + * @param env_res The result of merge-pdd. + * @param warnings A flag which marks any warnings. + * @return 0 + * @return or error + * @note For a complete documentation about the algorithm confer the + * PDD documentation. + */ +int bootenv_pdd_merge(bootenv_t env_old, bootenv_t env_new, + bootenv_t *env_res, int *warnings, + char *err_buf, size_t err_buf_size); + +/** + * @brief The PDD overwrite operation. + * @param env_old The old bootenv structure. + * @param env_new The new bootenv structure. + * @param env_res The result of overwrite-pdd. + * @param warnings A flag which marks any warnings. + * @return 0 + * @return or error + * @note For a complete documentation about the algorithm confer the + * PDD documentation. + */ +int bootenv_pdd_overwrite(bootenv_t env_new, + bootenv_t env_old, bootenv_t *env_res, int *warnings, + char *err_buf, size_t err_buf_size); + +/** + * @brief Dump a bootenv structure to stdout. (Debug) + * @param env Handle to a bootenv structure. + * @return 0 + * @return or error + */ +int bootenv_dump(bootenv_t env); + +/** + * @brief Validate a bootenv structure. + * @param env Handle to a bootenv structure. + * @return 0 + * @return or error + */ +int bootenv_valid(bootenv_t env); + +/** + * @brief Create a new bootenv list structure. + * @return NULL on error + * @return or a new list handle. + * @note This structure is used to store values in a list. + * A useful addition when handling PDD strings. + */ +int bootenv_list_create(bootenv_list_t *list); + +/** + * @brief Destroy a bootenv list structure + * @param list Handle to a bootenv list structure. + * @return 0 + * @return or error + */ +int bootenv_list_destroy(bootenv_list_t *list); + +/** + * @brief Import a list from a comma seperated string + * @param list Handle to a bootenv list structure. + * @param str Comma seperated string list. + * @return 0 + * @return or error + */ +int bootenv_list_import(bootenv_list_t list, const char *str); + +/** + * @brief Export a list to a string of comma seperated values. + * @param list Handle to a bootenv list structure. + * @return NULL one error + * @return or pointer to a newly allocated string. + * @warning Free the allocated memory by yourself! + */ +int bootenv_list_export(bootenv_list_t list, char **string); + +/** + * @brief Add an item to the list. + * @param list A handle of a list structure. + * @param item An item. + * @return 0 + * @return or error + */ +int bootenv_list_add(bootenv_list_t list, const char *item); + +/** + * @brief Remove an item from the list. + * @param list A handle of a list structure. + * @param item An item. + * @return 0 + * @return or error + */ +int bootenv_list_remove(bootenv_list_t list, const char *item); + +/** + * @brief Check if a given item is in a given list. + * @param list A handle of a list structure. + * @param item An item. + * @return 1 Item is in list. + * @return 0 Item is not in list. + */ +int bootenv_list_is_in(bootenv_list_t list, const char *item); + + +/** + * @brief Convert a list into a vector of all values inside the list. + * @param list Handle to a bootenv structure. + * @param size The size of the allocated vector structure. + * @return 0 + * @return or error + * @warning Free the allocate memory yourself! + */ +int bootenv_list_to_vector(bootenv_list_t list, size_t *size, + const char ***vector); + +/** + * @brief Convert a list into a vector of all values inside the list. + * @param list Handle to a bootenv structure. + * @param size The size of the allocated vector structure. + * @return 0 + * @return or error + * @warning Free the allocate memory yourself! + */ +int bootenv_list_to_num_vector(bootenv_list_t list, size_t *size, + uint32_t **vector); + +#ifdef __cplusplus +} +#endif +#endif /*__BOOTENV_H__ */ diff --git a/ubi-utils/src/common.c b/ubi-utils/src/common.c deleted file mode 100644 index fec640d..0000000 --- a/ubi-utils/src/common.c +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright (C) 2007, 2008 Nokia Corporation - * - * 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. - */ - -/* - * This file contains various common stuff used by UBI utilities. - * - * Authors: Artem Bityutskiy - * Adrian Hunter - */ - -#include -#include -#include -#include - -/** - * get_multiplier - convert size specifier to an integer multiplier. - * @str: the size specifier string - * - * This function parses the @str size specifier, which may be one of - * 'KiB', 'MiB', or 'GiB' into an integer multiplier. Returns positive - * size multiplier in case of success and %-1 in case of failure. - */ -static int get_multiplier(const char *str) -{ - if (!str) - return 1; - - /* Remove spaces before the specifier */ - while (*str == ' ' || *str == '\t') - str += 1; - - if (!strcmp(str, "KiB")) - return 1024; - if (!strcmp(str, "MiB")) - return 1024 * 1024; - if (!strcmp(str, "GiB")) - return 1024 * 1024 * 1024; - - return -1; -} - -/** - * ubiutils_get_bytes - convert a string containing amount of bytes into an - * integer - * @str: string to convert - * - * This function parses @str which may have an onee of 'KiB', 'MiB', or 'GiB' - * size specifiers. Returns positive amount of bytes in case of success and %-1 - * in case of failure. - */ -long long ubiutils_get_bytes(const char *str) -{ - char *endp; - long long bytes = strtoull(str, &endp, 0); - - if (endp == str || bytes < 0) { - fprintf(stderr, "incorrect amount of bytes: \"%s\"", str); - return -1; - } - - if (*endp != '\0') { - int mult = get_multiplier(endp); - - if (mult == -1) { - fprintf(stderr, "bad size specifier: \"%s\" - " - "should be 'KiB', 'MiB' or 'GiB'", endp); - return -1; - } - bytes *= mult; - } - - return bytes; -} - -/** - * ubiutils_print_bytes - print bytes. - * @bytes: variable to print - * @bracket: whether brackets have to be put or not - * - * This is a helper function which prints amount of bytes in a human-readable - * form, i.e., it prints the exact amount of bytes following by the approximate - * amount of Kilobytes, Megabytes, or Gigabytes, depending on how big @bytes - * is. - */ -void ubiutils_print_bytes(long long bytes, int bracket) -{ - const char *p; - - if (bracket) - p = " ("; - else - p = ", "; - - printf("%lld bytes", bytes); - - if (bytes > 1024 * 1024 * 1024) - printf("%s%.1f GiB", p, (double)bytes / (1024 * 1024 * 1024)); - else if (bytes > 1024 * 1024) - printf("%s%.1f MiB", p, (double)bytes / (1024 * 1024)); - else if (bytes > 1024 && bytes != 0) - printf("%s%.1f KiB", p, (double)bytes / 1024); - else - return; - - if (bracket) - printf(")"); -} - -/** - * ubiutils_print_text - print text and fold it. - * @stream: file stream to print to - * @text: text to print - * @width: maximum allowed text width - * - * Print text and fold it so that each line would not have more then @width - * characters. - */ -void ubiutils_print_text(FILE *stream, const char *text, int width) -{ - int pos, bpos = 0; - const char *p; - char line[1024]; - - if (width > 1023) { - fprintf(stream, "%s\n", text); - return; - } - p = text; - pos = 0; - while (p[pos]) { - while (!isspace(p[pos])) { - line[pos] = p[pos]; - if (!p[pos]) - break; - ++pos; - if (pos == width) { - line[pos] = '\0'; - fprintf(stream, "%s\n", line); - p += pos; - pos = 0; - } - } - while (pos < width) { - line[pos] = p[pos]; - if (!p[pos]) { - bpos = pos; - break; - } - if (isspace(p[pos])) - bpos = pos; - ++pos; - } - line[bpos] = '\0'; - fprintf(stream, "%s\n", line); - p += bpos; - pos = 0; - while (p[pos] && isspace(p[pos])) - ++p; - } -} diff --git a/ubi-utils/src/common.h b/ubi-utils/src/common.h deleted file mode 100644 index a3fc51f..0000000 --- a/ubi-utils/src/common.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) Artem Bityutskiy, 2007 - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See - * the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef __UBI_UTILS_COMMON_H__ -#define __UBI_UTILS_COMMON_H__ - -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#define MIN(a ,b) ((a) < (b) ? (a) : (b)) -#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) - -/* Verbose messages */ -#define verbose(verbose, fmt, ...) do { \ - if (verbose) \ - printf(PROGRAM_NAME ": " fmt "\n", ##__VA_ARGS__); \ -} while(0) - -/* Normal messages */ -#define normsg(fmt, ...) do { \ - printf(PROGRAM_NAME ": " fmt "\n", ##__VA_ARGS__); \ -} while(0) - -/* Error messages */ -#define errmsg(fmt, ...) ({ \ - fprintf(stderr, PROGRAM_NAME " error: " fmt "\n", ##__VA_ARGS__); \ - -1; \ -}) - -/* System error messages */ -#define sys_errmsg(fmt, ...) ({ \ - int _err = errno; \ - fprintf(stderr, PROGRAM_NAME " error: " fmt "\n", ##__VA_ARGS__); \ - fprintf(stderr, "error %d (%s)\n", _err, strerror(_err)); \ - -1; \ -}) - -/* Warnings */ -#define warnmsg(fmt, ...) do { \ - fprintf(stderr, PROGRAM_NAME " warning: " fmt "\n", ##__VA_ARGS__); \ -} while(0) - -long long ubiutils_get_bytes(const char *str); -void ubiutils_print_bytes(long long bytes, int bracket); -void ubiutils_print_text(FILE *stream, const char *txt, int len); - -#ifdef __cplusplus -} -#endif - -#endif /* !__UBI_UTILS_COMMON_H__ */ diff --git a/ubi-utils/src/config.h b/ubi-utils/src/config.h new file mode 100644 index 0000000..55e60f3 --- /dev/null +++ b/ubi-utils/src/config.h @@ -0,0 +1,28 @@ +#ifndef __CONFIG_H__ +#define __CONFIG_H__ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Frank Haverkamp + */ + +#define PACKAGE_BUGREPORT \ + "haver@vnet.ibm.com, dedekind@linutronix.de, or tglx@linutronix.de" + +#define ubi_unused __attribute__((unused)) + +#endif diff --git a/ubi-utils/src/crc32.c b/ubi-utils/src/crc32.c index 6b1e50c..666e217 100644 --- a/ubi-utils/src/crc32.c +++ b/ubi-utils/src/crc32.c @@ -1,95 +1,83 @@ /* - * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or - * code or tables extracted from it, as desired without restriction. + * Copyright (c) International Business Machines Corp., 2006 * - * First, the polynomial itself and its table of feedback terms. The - * polynomial is - * X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 + * 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. * - * Note that we take it "backwards" and put the highest-order term in - * the lowest-order bit. The X^32 term is "implied"; the LSB is the - * X^31 term, etc. The X^0 term (usually shown as "+1") results in - * the MSB being 1 + * 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. * - * Note that the usual hardware shift register implementation, which - * is what we're using (we're merely optimizing it by doing eight-bit - * chunks at a time) shifts bits into the lowest-order term. In our - * implementation, that means shifting towards the right. Why do we - * do it this way? Because the calculated CRC must be transmitted in - * order from highest-order term to lowest-order term. UARTs transmit - * characters in order from LSB to MSB. By storing the CRC this way - * we hand it to the UART in the order low-byte to high-byte; the UART - * sends each low-bit to hight-bit; and the result is transmission bit - * by bit from highest- to lowest-order term without requiring any bit - * shuffling on our part. Reception works similarly + * 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. * - * The feedback terms table consists of 256, 32-bit entries. Notes + * Author: Thomas Gleixner + */ + +/* + * CRC32 functions * - * The table can be generated at runtime if desired; code to do so - * is shown later. It might not be obvious, but the feedback - * terms simply represent the results of eight shift/xor opera - * tions for all combinations of data and CRC register values + * 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. * - * The values must be right-shifted by eight bits by the "updcrc - * logic; the shift must be unsigned (bring in zeroes). On some - * hardware you could probably optimize the shift in assembler by - * using byte-swap instructions - * polynomial $edb88320 */ #include +#include + +/* 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; -const uint32_t crc32_table[256] = { - 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, - 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, - 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, - 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, - 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, - 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, - 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, - 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, - 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, - 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, - 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, - 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, - 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, - 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, - 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, - 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, - 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, - 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, - 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, - 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, - 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, - 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, - 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, - 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, - 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, - 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, - 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, - 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, - 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, - 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, - 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, - 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, - 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, - 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, - 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, - 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, - 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, - 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, - 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, - 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, - 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, - 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, - 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, - 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, - 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, - 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, - 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, - 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, - 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, - 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, - 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, - 0x2d02ef8dL -}; + while(--len >= 0) + crc = table[(crc ^ *p++) & 0xff] ^ (crc >> 8); + return crc; +} diff --git a/ubi-utils/src/crc32.h b/ubi-utils/src/crc32.h index ee3145b..31362b0 100644 --- a/ubi-utils/src/crc32.h +++ b/ubi-utils/src/crc32.h @@ -1,19 +1,36 @@ -#ifndef CRC32_H -#define CRC32_H +#ifndef __CRC32_H__ +#define __CRC32_H__ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +/* + * Author: Thomas Gleixner + * + * CRC32 functions + * + * Can be compiled as seperate object, but is included into the ipl source + * so gcc can inline the functions. We optimize for size so the omission of + * the function frame is helpful. + * + */ #include -extern const uint32_t crc32_table[256]; +void init_crc32_table(uint32_t *table); +uint32_t clc_crc32(uint32_t *table, uint32_t crc, void *buf, int len); -/* Return a 32-bit CRC of the contents of the buffer. */ - - static inline uint32_t -crc32(uint32_t val, const void *ss, int len) -{ - const unsigned char *s = ss; - while (--len >= 0) - val = crc32_table[(val ^ *s++) & 0xff] ^ (val >> 8); - return val; -} - -#endif +#endif /* __CRC32_H__ */ diff --git a/ubi-utils/src/dictionary.c b/ubi-utils/src/dictionary.c deleted file mode 100644 index b7c9ebf..0000000 --- a/ubi-utils/src/dictionary.c +++ /dev/null @@ -1,405 +0,0 @@ -/*-------------------------------------------------------------------------*/ -/** - @file dictionary.c - @author N. Devillard - @date Sep 2007 - @version $Revision: 1.27 $ - @brief Implements a dictionary for string variables. - - This module implements a simple dictionary object, i.e. a list - of string/string associations. This object is useful to store e.g. - informations retrieved from a configuration file (ini files). -*/ -/*--------------------------------------------------------------------------*/ - -/* - $Id: dictionary.c,v 1.27 2007-11-23 21:39:18 ndevilla Exp $ - $Revision: 1.27 $ -*/ -/*--------------------------------------------------------------------------- - Includes - ---------------------------------------------------------------------------*/ -#include "dictionary.h" - -#include -#include -#include -#include - -/** Maximum value size for integers and doubles. */ -#define MAXVALSZ 1024 - -/** Minimal allocated number of entries in a dictionary */ -#define DICTMINSZ 128 - -/** Invalid key token */ -#define DICT_INVALID_KEY ((char*)-1) - -/*--------------------------------------------------------------------------- - Private functions - ---------------------------------------------------------------------------*/ - -/* Doubles the allocated size associated to a pointer */ -/* 'size' is the current allocated size. */ -static void * mem_double(void * ptr, int size) -{ - void * newptr ; - - newptr = calloc(2*size, 1); - if (newptr==NULL) { - return NULL ; - } - memcpy(newptr, ptr, size); - free(ptr); - return newptr ; -} - -/*-------------------------------------------------------------------------*/ -/** - @brief Duplicate a string - @param s String to duplicate - @return Pointer to a newly allocated string, to be freed with free() - - This is a replacement for strdup(). This implementation is provided - for systems that do not have it. - */ -/*--------------------------------------------------------------------------*/ -static char * xstrdup(char * s) -{ - char * t ; - if (!s) - return NULL ; - t = malloc(strlen(s)+1) ; - if (t) { - strcpy(t,s); - } - return t ; -} - -/*--------------------------------------------------------------------------- - Function codes - ---------------------------------------------------------------------------*/ -/*-------------------------------------------------------------------------*/ -/** - @brief Compute the hash key for a string. - @param key Character string to use for key. - @return 1 unsigned int on at least 32 bits. - - This hash function has been taken from an Article in Dr Dobbs Journal. - This is normally a collision-free function, distributing keys evenly. - The key is stored anyway in the struct so that collision can be avoided - by comparing the key itself in last resort. - */ -/*--------------------------------------------------------------------------*/ -unsigned dictionary_hash(char * key) -{ - int len ; - unsigned hash ; - int i ; - - len = strlen(key); - for (hash=0, i=0 ; i>6) ; - } - hash += (hash <<3); - hash ^= (hash >>11); - hash += (hash <<15); - return hash ; -} - -/*-------------------------------------------------------------------------*/ -/** - @brief Create a new dictionary object. - @param size Optional initial size of the dictionary. - @return 1 newly allocated dictionary objet. - - This function allocates a new dictionary object of given size and returns - it. If you do not know in advance (roughly) the number of entries in the - dictionary, give size=0. - */ -/*--------------------------------------------------------------------------*/ -dictionary * dictionary_new(int size) -{ - dictionary * d ; - - /* If no size was specified, allocate space for DICTMINSZ */ - if (sizesize = size ; - d->val = (char **)calloc(size, sizeof(char*)); - d->key = (char **)calloc(size, sizeof(char*)); - d->hash = (unsigned int *)calloc(size, sizeof(unsigned)); - return d ; -} - -/*-------------------------------------------------------------------------*/ -/** - @brief Delete a dictionary object - @param d dictionary object to deallocate. - @return void - - Deallocate a dictionary object and all memory associated to it. - */ -/*--------------------------------------------------------------------------*/ -void dictionary_del(dictionary * d) -{ - int i ; - - if (d==NULL) return ; - for (i=0 ; isize ; i++) { - if (d->key[i]!=NULL) - free(d->key[i]); - if (d->val[i]!=NULL) - free(d->val[i]); - } - free(d->val); - free(d->key); - free(d->hash); - free(d); - return ; -} - -/*-------------------------------------------------------------------------*/ -/** - @brief Get a value from a dictionary. - @param d dictionary object to search. - @param key Key to look for in the dictionary. - @param def Default value to return if key not found. - @return 1 pointer to internally allocated character string. - - This function locates a key in a dictionary and returns a pointer to its - value, or the passed 'def' pointer if no such key can be found in - dictionary. The returned character pointer points to data internal to the - dictionary object, you should not try to free it or modify it. - */ -/*--------------------------------------------------------------------------*/ -char * dictionary_get(dictionary * d, char * key, char * def) -{ - unsigned hash ; - int i ; - - hash = dictionary_hash(key); - for (i=0 ; isize ; i++) { - if (d->key[i]==NULL) - continue ; - /* Compare hash */ - if (hash==d->hash[i]) { - /* Compare string, to avoid hash collisions */ - if (!strcmp(key, d->key[i])) { - return d->val[i] ; - } - } - } - return def ; -} - -/*-------------------------------------------------------------------------*/ -/** - @brief Set a value in a dictionary. - @param d dictionary object to modify. - @param key Key to modify or add. - @param val Value to add. - @return int 0 if Ok, anything else otherwise - - If the given key is found in the dictionary, the associated value is - replaced by the provided one. If the key cannot be found in the - dictionary, it is added to it. - - It is Ok to provide a NULL value for val, but NULL values for the dictionary - or the key are considered as errors: the function will return immediately - in such a case. - - Notice that if you dictionary_set a variable to NULL, a call to - dictionary_get will return a NULL value: the variable will be found, and - its value (NULL) is returned. In other words, setting the variable - content to NULL is equivalent to deleting the variable from the - dictionary. It is not possible (in this implementation) to have a key in - the dictionary without value. - - This function returns non-zero in case of failure. - */ -/*--------------------------------------------------------------------------*/ -int dictionary_set(dictionary * d, char * key, char * val) -{ - int i ; - unsigned hash ; - - if (d==NULL || key==NULL) return -1 ; - - /* Compute hash for this key */ - hash = dictionary_hash(key) ; - /* Find if value is already in dictionary */ - if (d->n>0) { - for (i=0 ; isize ; i++) { - if (d->key[i]==NULL) - continue ; - if (hash==d->hash[i]) { /* Same hash value */ - if (!strcmp(key, d->key[i])) { /* Same key */ - /* Found a value: modify and return */ - if (d->val[i]!=NULL) - free(d->val[i]); - d->val[i] = val ? xstrdup(val) : NULL ; - /* Value has been modified: return */ - return 0 ; - } - } - } - } - /* Add a new value */ - /* See if dictionary needs to grow */ - if (d->n==d->size) { - - /* Reached maximum size: reallocate dictionary */ - d->val = (char **)mem_double(d->val, d->size * sizeof(char*)) ; - d->key = (char **)mem_double(d->key, d->size * sizeof(char*)) ; - d->hash = (unsigned int *)mem_double(d->hash, d->size * sizeof(unsigned)) ; - if ((d->val==NULL) || (d->key==NULL) || (d->hash==NULL)) { - /* Cannot grow dictionary */ - return -1 ; - } - /* Double size */ - d->size *= 2 ; - } - - /* Insert key in the first empty slot */ - for (i=0 ; isize ; i++) { - if (d->key[i]==NULL) { - /* Add key here */ - break ; - } - } - /* Copy key */ - d->key[i] = xstrdup(key); - d->val[i] = val ? xstrdup(val) : NULL ; - d->hash[i] = hash; - d->n ++ ; - return 0 ; -} - -/*-------------------------------------------------------------------------*/ -/** - @brief Delete a key in a dictionary - @param d dictionary object to modify. - @param key Key to remove. - @return void - - This function deletes a key in a dictionary. Nothing is done if the - key cannot be found. - */ -/*--------------------------------------------------------------------------*/ -void dictionary_unset(dictionary * d, char * key) -{ - unsigned hash ; - int i ; - - if (key == NULL) { - return; - } - - hash = dictionary_hash(key); - for (i=0 ; isize ; i++) { - if (d->key[i]==NULL) - continue ; - /* Compare hash */ - if (hash==d->hash[i]) { - /* Compare string, to avoid hash collisions */ - if (!strcmp(key, d->key[i])) { - /* Found key */ - break ; - } - } - } - if (i>=d->size) - /* Key not found */ - return ; - - free(d->key[i]); - d->key[i] = NULL ; - if (d->val[i]!=NULL) { - free(d->val[i]); - d->val[i] = NULL ; - } - d->hash[i] = 0 ; - d->n -- ; - return ; -} - -/*-------------------------------------------------------------------------*/ -/** - @brief Dump a dictionary to an opened file pointer. - @param d Dictionary to dump - @param f Opened file pointer. - @return void - - Dumps a dictionary onto an opened file pointer. Key pairs are printed out - as @c [Key]=[Value], one per line. It is Ok to provide stdout or stderr as - output file pointers. - */ -/*--------------------------------------------------------------------------*/ -void dictionary_dump(dictionary * d, FILE * out) -{ - int i ; - - if (d==NULL || out==NULL) return ; - if (d->n<1) { - fprintf(out, "empty dictionary\n"); - return ; - } - for (i=0 ; isize ; i++) { - if (d->key[i]) { - fprintf(out, "%20s\t[%s]\n", - d->key[i], - d->val[i] ? d->val[i] : "UNDEF"); - } - } - return ; -} - - -/* Test code */ -#ifdef TESTDIC -#define NVALS 20000 -int main(int argc, char *argv[]) -{ - dictionary * d ; - char * val ; - int i ; - char cval[90] ; - - /* Allocate dictionary */ - printf("allocating...\n"); - d = dictionary_new(0); - - /* Set values in dictionary */ - printf("setting %d values...\n", NVALS); - for (i=0 ; in != 0) { - printf("error deleting values\n"); - } - printf("deallocating...\n"); - dictionary_del(d); - return 0 ; -} -#endif -/* vim: set ts=4 et sw=4 tw=75 */ diff --git a/ubi-utils/src/dictionary.h b/ubi-utils/src/dictionary.h deleted file mode 100644 index c7d1790..0000000 --- a/ubi-utils/src/dictionary.h +++ /dev/null @@ -1,174 +0,0 @@ - -/*-------------------------------------------------------------------------*/ -/** - @file dictionary.h - @author N. Devillard - @date Sep 2007 - @version $Revision: 1.12 $ - @brief Implements a dictionary for string variables. - - This module implements a simple dictionary object, i.e. a list - of string/string associations. This object is useful to store e.g. - informations retrieved from a configuration file (ini files). -*/ -/*--------------------------------------------------------------------------*/ - -/* - $Id: dictionary.h,v 1.12 2007-11-23 21:37:00 ndevilla Exp $ - $Author: ndevilla $ - $Date: 2007-11-23 21:37:00 $ - $Revision: 1.12 $ -*/ - -#ifndef _DICTIONARY_H_ -#define _DICTIONARY_H_ - -/*--------------------------------------------------------------------------- - Includes - ---------------------------------------------------------------------------*/ - -#include -#include -#include -#include - -/*--------------------------------------------------------------------------- - New types - ---------------------------------------------------------------------------*/ - - -/*-------------------------------------------------------------------------*/ -/** - @brief Dictionary object - - This object contains a list of string/string associations. Each - association is identified by a unique string key. Looking up values - in the dictionary is speeded up by the use of a (hopefully collision-free) - hash function. - */ -/*-------------------------------------------------------------------------*/ -typedef struct _dictionary_ { - int n ; /** Number of entries in dictionary */ - int size ; /** Storage size */ - char ** val ; /** List of string values */ - char ** key ; /** List of string keys */ - unsigned * hash ; /** List of hash values for keys */ -} dictionary ; - - -/*--------------------------------------------------------------------------- - Function prototypes - ---------------------------------------------------------------------------*/ - -/*-------------------------------------------------------------------------*/ -/** - @brief Compute the hash key for a string. - @param key Character string to use for key. - @return 1 unsigned int on at least 32 bits. - - This hash function has been taken from an Article in Dr Dobbs Journal. - This is normally a collision-free function, distributing keys evenly. - The key is stored anyway in the struct so that collision can be avoided - by comparing the key itself in last resort. - */ -/*--------------------------------------------------------------------------*/ -unsigned dictionary_hash(char * key); - -/*-------------------------------------------------------------------------*/ -/** - @brief Create a new dictionary object. - @param size Optional initial size of the dictionary. - @return 1 newly allocated dictionary objet. - - This function allocates a new dictionary object of given size and returns - it. If you do not know in advance (roughly) the number of entries in the - dictionary, give size=0. - */ -/*--------------------------------------------------------------------------*/ -dictionary * dictionary_new(int size); - -/*-------------------------------------------------------------------------*/ -/** - @brief Delete a dictionary object - @param d dictionary object to deallocate. - @return void - - Deallocate a dictionary object and all memory associated to it. - */ -/*--------------------------------------------------------------------------*/ -void dictionary_del(dictionary * vd); - -/*-------------------------------------------------------------------------*/ -/** - @brief Get a value from a dictionary. - @param d dictionary object to search. - @param key Key to look for in the dictionary. - @param def Default value to return if key not found. - @return 1 pointer to internally allocated character string. - - This function locates a key in a dictionary and returns a pointer to its - value, or the passed 'def' pointer if no such key can be found in - dictionary. The returned character pointer points to data internal to the - dictionary object, you should not try to free it or modify it. - */ -/*--------------------------------------------------------------------------*/ -char * dictionary_get(dictionary * d, char * key, char * def); - - -/*-------------------------------------------------------------------------*/ -/** - @brief Set a value in a dictionary. - @param d dictionary object to modify. - @param key Key to modify or add. - @param val Value to add. - @return int 0 if Ok, anything else otherwise - - If the given key is found in the dictionary, the associated value is - replaced by the provided one. If the key cannot be found in the - dictionary, it is added to it. - - It is Ok to provide a NULL value for val, but NULL values for the dictionary - or the key are considered as errors: the function will return immediately - in such a case. - - Notice that if you dictionary_set a variable to NULL, a call to - dictionary_get will return a NULL value: the variable will be found, and - its value (NULL) is returned. In other words, setting the variable - content to NULL is equivalent to deleting the variable from the - dictionary. It is not possible (in this implementation) to have a key in - the dictionary without value. - - This function returns non-zero in case of failure. - */ -/*--------------------------------------------------------------------------*/ -int dictionary_set(dictionary * vd, char * key, char * val); - -/*-------------------------------------------------------------------------*/ -/** - @brief Delete a key in a dictionary - @param d dictionary object to modify. - @param key Key to remove. - @return void - - This function deletes a key in a dictionary. Nothing is done if the - key cannot be found. - */ -/*--------------------------------------------------------------------------*/ -void dictionary_unset(dictionary * d, char * key); - - -/*-------------------------------------------------------------------------*/ -/** - @brief Dump a dictionary to an opened file pointer. - @param d Dictionary to dump - @param f Opened file pointer. - @return void - - Dumps a dictionary onto an opened file pointer. Key pairs are printed out - as @c [Key]=[Value], one per line. It is Ok to provide stdout or stderr as - output file pointers. - */ -/*--------------------------------------------------------------------------*/ -void dictionary_dump(dictionary * d, FILE * out); - -#endif diff --git a/ubi-utils/src/eb_chain.c b/ubi-utils/src/eb_chain.c new file mode 100644 index 0000000..4647cf4 --- /dev/null +++ b/ubi-utils/src/eb_chain.c @@ -0,0 +1,281 @@ +/* + * Copyright (c) International Business Machines Corp., 2006, 2007 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * Author: Drake Dowsett, dowsett@de.ibm.com + * Contact: Andreas Arnez, arnez@de.ibm.com + */ + +/* see eb_chain.h */ + +#include +#include +#include +#include +#include "unubi_analyze.h" +#include "crc32.h" + +#define COPY(dst, src) \ + do { \ + dst = malloc(sizeof(*dst)); \ + if (dst == NULL) \ + return -ENOMEM; \ + memcpy(dst, src, sizeof(*dst)); \ + } while (0) + + +/** + * inserts an eb_info into the chain starting at head, then searching + * linearly for the correct position; + * new should contain valid vid and ec headers and the data_crc should + * already have been checked before insertion, otherwise the chain + * could be have un an undesired manner; + * returns -ENOMEM if alloc fails, otherwise SHOULD always return 0, + * if not, the code reached the last line and returned -EAGAIN, + * meaning there is a bug or a case not being handled here; + **/ +int +eb_chain_insert(struct eb_info **head, struct eb_info *new) +{ + uint32_t vol, num, ver; + uint32_t new_vol, new_num, new_ver; + struct eb_info *prev, *cur, *hist, *ins; + struct eb_info **prev_ptr; + + if ((head == NULL) || (new == NULL)) + return 0; + + if (*head == NULL) { + COPY(*head, new); + (*head)->next = NULL; + return 0; + } + + new_vol = be32_to_cpu(new->vid.vol_id); + new_num = be32_to_cpu(new->vid.lnum); + new_ver = be32_to_cpu(new->vid.leb_ver); + + /** TRAVERSE HORIZONTALY **/ + + cur = *head; + prev = NULL; + + /* traverse until vol_id/lnum align */ + vol = be32_to_cpu(cur->vid.vol_id); + num = be32_to_cpu(cur->vid.lnum); + while ((new_vol > vol) || ((new_vol == vol) && (new_num > num))) { + /* insert new at end of chain */ + if (cur->next == NULL) { + COPY(ins, new); + ins->next = NULL; + cur->next = ins; + return 0; + } + + prev = cur; + cur = cur->next; + vol = be32_to_cpu(cur->vid.vol_id); + num = be32_to_cpu(cur->vid.lnum); + } + + if (prev == NULL) + prev_ptr = head; + else + prev_ptr = &(prev->next); + + /* insert new into the middle of chain */ + if ((new_vol != vol) || (new_num != num)) { + COPY(ins, new); + ins->next = cur; + *prev_ptr = ins; + return 0; + } + + /** TRAVERSE VERTICALY **/ + + hist = cur; + prev = NULL; + + /* traverse until versions align */ + ver = be32_to_cpu(cur->vid.leb_ver); + while (new_ver < ver) { + /* insert new at bottom of history */ + if (hist->older == NULL) { + COPY(ins, new); + ins->next = NULL; + ins->older = NULL; + hist->older = ins; + return 0; + } + + prev = hist; + hist = hist->older; + ver = be32_to_cpu(hist->vid.leb_ver); + } + + if (prev == NULL) { + /* replace active version */ + COPY(ins, new); + ins->next = hist->next; + *prev_ptr = ins; + + /* place cur in vertical histroy */ + ins->older = hist; + hist->next = NULL; + return 0; + } + + /* insert between versions, beneath active version */ + COPY(ins, new); + ins->next = NULL; + ins->older = prev->older; + prev->older = ins; + return 0; +} + + +/** + * sets the pointer at pos to the position of the first entry in the chain + * with of vol_id and, if given, with the same lnum as *lnum; + * if there is no entry in the chain, then *pos is NULL on return; + * always returns 0; + **/ +int +eb_chain_position(struct eb_info **head, uint32_t vol_id, uint32_t *lnum, + struct eb_info **pos) +{ + uint32_t vol, num; + struct eb_info *cur; + + if ((head == NULL) || (*head == NULL) || (pos == NULL)) + return 0; + + *pos = NULL; + + cur = *head; + while (cur != NULL) { + vol = be32_to_cpu(cur->vid.vol_id); + num = be32_to_cpu(cur->vid.lnum); + + if ((vol_id == vol) && ((lnum == NULL) || (*lnum == num))) { + *pos = cur; + return 0; + } + + cur = cur->next; + } + + return 0; +} + + +/** + * prints to stream, the vol_id, lnum and leb_ver for each entry in the + * chain, starting at head; + * this is intended for debuging purposes; + * always returns 0; + * + * FIXME I do not like the double list traversion ... + **/ +int +eb_chain_print(FILE* stream, struct eb_info *head) +{ + struct eb_info *cur; + + if (stream == NULL) + stream = stdout; + + if (head == NULL) { + fprintf(stream, "EMPTY\n"); + return 0; + } + /* 012345678012345678012345678012301230123 0123 01234567 0123457 01234567*/ + fprintf(stream, "VOL_ID LNUM LEB_VER EC VID DAT PBLK PADDR DSIZE EC\n"); + cur = head; + while (cur != NULL) { + struct eb_info *hist; + + fprintf(stream, "%08x %-8u %08x %-4s%-4s", + be32_to_cpu(cur->vid.vol_id), + be32_to_cpu(cur->vid.lnum), + be32_to_cpu(cur->vid.leb_ver), + cur->ec_crc_ok ? "ok":"bad", + cur->vid_crc_ok ? "ok":"bad"); + if (cur->vid.vol_type == UBI_VID_STATIC) + fprintf(stream, "%-4s", cur->data_crc_ok ? "ok":"bad"); + else fprintf(stream, "%-4s", cur->data_crc_ok ? "ok":"ign"); + fprintf(stream, " %-4d %08x %-8u %-8llu\n", cur->phys_block, + cur->phys_addr, be32_to_cpu(cur->vid.data_size), + be64_to_cpu(cur->ec.ec)); + + hist = cur->older; + while (hist != NULL) { + fprintf(stream, "%08x %-8u %08x %-4s%-4s", + be32_to_cpu(hist->vid.vol_id), + be32_to_cpu(hist->vid.lnum), + be32_to_cpu(hist->vid.leb_ver), + hist->ec_crc_ok ? "ok":"bad", + hist->vid_crc_ok ? "ok":"bad"); + if (hist->vid.vol_type == UBI_VID_STATIC) + fprintf(stream, "%-4s", hist->data_crc_ok ? "ok":"bad"); + else fprintf(stream, "%-4s", hist->data_crc_ok ? "ok":"ign"); + fprintf(stream, " %-4d %08x %-8u %-8llu (*)\n", + hist->phys_block, hist->phys_addr, + be32_to_cpu(hist->vid.data_size), + be64_to_cpu(hist->ec.ec)); + + hist = hist->older; + } + cur = cur->next; + } + + return 0; +} + + +/** + * frees the memory of the entire chain, starting at head; + * head will be NULL on return; + * always returns 0; + **/ +int +eb_chain_destroy(struct eb_info **head) +{ + if (head == NULL) + return 0; + + while (*head != NULL) { + struct eb_info *cur; + struct eb_info *hist; + + cur = *head; + *head = (*head)->next; + + hist = cur->older; + while (hist != NULL) { + struct eb_info *temp; + + temp = hist; + hist = hist->older; + free(temp); + } + free(cur); + } + return 0; +} + diff --git a/ubi-utils/src/error.c b/ubi-utils/src/error.c new file mode 100644 index 0000000..4aaedad --- /dev/null +++ b/ubi-utils/src/error.c @@ -0,0 +1,240 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include "error.h" + +#define MAXLINE 4096 +#define MAXWIDTH 80 + +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; + + if (!fp_out) + return -ENXIO; + + 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) +{ + if (!logfile) + return; + + 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 */ +} + +/** + * If a logfile is used we must not print on stderr and stdout + * anymore. Since pfilfash might be used in a server context, it is + * even dangerous to write to those descriptors. + */ +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); + return; /* exit when logging completes */ + } + + if (fpout == stderr) { + /* perform line wrap when outputting to stderr */ + int word_len, post_len, chars; + char *buf_ptr; + const char *frmt = "%*s%n %n"; + + chars = 0; + buf_ptr = buf; + while (sscanf(buf_ptr, frmt, &word_len, &post_len) != EOF) { + int i; + char word[word_len + 1]; + char post[post_len + 1]; + + strncpy(word, buf_ptr, word_len); + word[word_len] = '\0'; + buf_ptr += word_len; + post_len -= word_len; + + if (chars + word_len > MAXWIDTH) { + fputc('\n', fpout); + chars = 0; + } + fputs(word, fpout); + chars += word_len; + + if (post_len > 0) { + strncpy(post, buf_ptr, post_len); + post[post_len] = '\0'; + buf_ptr += post_len; + } + for (i = 0; i < post_len; i++) { + int inc = 1, chars_new; + + if (post[i] == '\t') + inc = 8; + if (post[i] == '\n') { + inc = 0; + chars_new = 0; + } else + chars_new = chars + inc; + + if (chars_new > MAXWIDTH) { + fputc('\n', fpout); + chars_new = inc; + } + fputc(post[i], fpout); + chars = chars_new; + } + } + } + else + fputs(buf, fpout); + fflush(fpout); + if (fpout != stderr) + fclose(fpout); + + return; +} diff --git a/ubi-utils/src/error.h b/ubi-utils/src/error.h new file mode 100644 index 0000000..05d8078 --- /dev/null +++ b/ubi-utils/src/error.h @@ -0,0 +1,84 @@ +#ifndef __ERROR_H__ +#define __ERROR_H__ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include + +void error_initlog(const char *logfile); +int read_procfile(FILE *fp_out, const char *procfile); + +void __err_ret(const char *fmt, ...); +void __err_sys(const char *fmt, ...); +void __err_msg(const char *fmt, ...); +void __err_quit(const char *fmt, ...); +void __err_dump(const char *fmt, ...); + +void info_msg(const char *fmt, ...); + +#ifdef DEBUG +#define __loc_msg(str) do { \ + __err_msg("[%s. FILE: %s FUNC: %s LINE: %d]\n", \ + str, __FILE__, __FUNCTION__, __LINE__); \ +} while (0) +#else +#define __loc_msg(str) +#endif + + +#define err_dump(fmt, ...) do { \ + __loc_msg("ErrDump"); \ + __err_dump(fmt, ##__VA_ARGS__); \ +} while (0) + +#define err_quit(fmt, ...) do { \ + __loc_msg("ErrQuit"); \ + __err_quit(fmt, ##__VA_ARGS__); \ +} while (0) + + +#define err_ret(fmt, ...) do { \ + __loc_msg("ErrRet"); \ + __err_ret(fmt, ##__VA_ARGS__); \ +} while (0) + +#define err_sys(fmt, ...) do { \ + __loc_msg("ErrSys"); \ + __err_sys(fmt, ##__VA_ARGS__); \ +} while (0) + +#define err_msg(fmt, ...) do { \ + __loc_msg("ErrMsg"); \ + __err_msg(fmt, ##__VA_ARGS__); \ +} while (0) + +#define log_msg(fmt, ...) do { \ + /* __loc_msg("LogMsg"); */ \ + __err_msg(fmt, ##__VA_ARGS__); \ +} while (0) + +#ifdef DEBUG +#define dbg_msg(fmt, ...) do { \ + __loc_msg("DbgMsg"); \ + __err_msg(fmt, ##__VA_ARGS__); \ +} while (0) +#else +#define dbg_msg(fmt, ...) do {} while (0) +#endif + +#endif /* __ERROR_H__ */ diff --git a/ubi-utils/src/example_ubi.h b/ubi-utils/src/example_ubi.h new file mode 100644 index 0000000..23c7b54 --- /dev/null +++ b/ubi-utils/src/example_ubi.h @@ -0,0 +1,28 @@ +#ifndef __EXAMPLE_UBI_H__ +#define __EXAMPLE_UBI_H__ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/** + * Defaults for our cards. + */ +#define EXAMPLE_UBI_DEVICE 0 +#define EXAMPLE_BOOTENV_VOL_ID_1 4 +#define EXAMPLE_BOOTENV_VOL_ID_2 5 + +#endif /* __EXAMPLE_UBI_H__ */ diff --git a/ubi-utils/src/hashmap.c b/ubi-utils/src/hashmap.c new file mode 100644 index 0000000..3511d56 --- /dev/null +++ b/ubi-utils/src/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 +#include +#include +#include +#include "error.h" +#include "hashmap.h" +#define DEFAULT_BUCKETS 4096 + +#if 0 +#define INFO_MSG(fmt...) do { \ + info_msg(fmt); \ +} while (0) +#else +#define INFO_MSG(fmt...) +#endif + +struct hashentry { + char* key; /* key '0' term. str */ + char* value; /* payload '0' term. str */ + + hashentry_t next; +}; + +struct hashmap { + size_t entries; /* current #entries */ + size_t maxsize; /* no. of hash buckets */ + hashentry_t* data; /* array of buckets */ +}; + +static int +is_empty(hashentry_t l) +{ + return l == NULL ? 1 : 0; +} + +hashmap_t +hashmap_new(void) +{ + hashmap_t res; + res = (hashmap_t) calloc(1, sizeof(struct hashmap)); + + if (res == NULL) + return NULL; + + res->maxsize = DEFAULT_BUCKETS; + res->entries = 0; + + res->data = (hashentry_t*) + calloc(1, res->maxsize * sizeof(struct hashentry)); + + if (res->data == NULL) + return NULL; + + return res; +} + +static hashentry_t +new_entry(const char* key, const char* value) +{ + hashentry_t res; + + res = (hashentry_t) calloc(1, sizeof(struct hashentry)); + + if (res == NULL) + return NULL; + + /* allocate key and value and copy them */ + res->key = strdup(key); + if (res->key == NULL) { + free(res); + return NULL; + } + + res->value = strdup(value); + if (res->value == NULL) { + free(res->key); + free(res); + return NULL; + } + + res->next = NULL; + + return res; +} + +static hashentry_t +free_entry(hashentry_t e) +{ + if (!is_empty(e)) { + if(e->key != NULL) { + free(e->key); + } + if(e->value != NULL) + free(e->value); + free(e); + } + + return NULL; +} + +static hashentry_t +remove_entry(hashentry_t l, const char* key, size_t* entries) +{ + hashentry_t lnext; + if (is_empty(l)) + return NULL; + + if(strcmp(l->key,key) == 0) { + lnext = l->next; + l = free_entry(l); + (*entries)--; + return lnext; + } + + l->next = remove_entry(l->next, key, entries); + + return l; +} + +static hashentry_t +insert_entry(hashentry_t l, hashentry_t e, size_t* entries) +{ + if (is_empty(l)) { + (*entries)++; + return e; + } + + /* check for update */ + if (strcmp(l->key, e->key) == 0) { + e->next = l->next; + l = free_entry(l); + return e; + } + + l->next = insert_entry(l->next, e, entries); + return l; +} + +static hashentry_t +remove_all(hashentry_t l, size_t* entries) +{ + hashentry_t lnext; + if (is_empty(l)) + return NULL; + + lnext = l->next; + free_entry(l); + (*entries)--; + + return remove_all(lnext, entries); +} + +static const char* +value_lookup(hashentry_t l, const char* key) +{ + if (is_empty(l)) + return NULL; + + if (strcmp(l->key, key) == 0) + return l->value; + + return value_lookup(l->next, key); +} + +static void +print_all(hashentry_t l) +{ + if (is_empty(l)) { + printf("\n"); + return; + } + + printf("%s=%s", l->key, l->value); + if (!is_empty(l->next)) { + printf(","); + } + + print_all(l->next); +} + +static void +keys_to_array(hashentry_t l, const char** a, size_t* i) +{ + if (is_empty(l)) + return; + + a[*i] = l->key; + (*i)++; + + keys_to_array(l->next, a, i); +} + +uint32_t +hash_str(const char* str, uint32_t mapsize) +{ + uint32_t hash = 0; + uint32_t x = 0; + uint32_t i = 0; + size_t len = strlen(str); + + for(i = 0; i < len; str++, i++) { + hash = (hash << 4) + (*str); + if((x = hash & 0xF0000000L) != 0) { + hash ^= (x >> 24); + hash &= ~x; + } + } + + return (hash & 0x7FFFFFFF) % mapsize; +} + + +int +hashmap_is_empty(hashmap_t map) +{ + if (map == NULL) + return -EINVAL; + + return map->entries > 0 ? 1 : 0; +} + +const char* +hashmap_lookup(hashmap_t map, const char* key) +{ + uint32_t i; + + if ((map == NULL) || (key == NULL)) + return NULL; + + i = hash_str(key, map->maxsize); + + return value_lookup(map->data[i], key); +} + +int +hashmap_add(hashmap_t map, const char* key, const char* value) +{ + uint32_t i; + hashentry_t entry; + + if ((map == NULL) || (key == NULL) || (value == NULL)) + return -EINVAL; + + i = hash_str(key, map->maxsize); + entry = new_entry(key, value); + if (entry == NULL) + return -ENOMEM; + + map->data[i] = insert_entry(map->data[i], + entry, &map->entries); + + INFO_MSG("HASH_ADD: chain[%d] key:%s val:%s",i, key, value); + return 0; +} + +int +hashmap_remove(hashmap_t map, const char* key) +{ + uint32_t i; + + if ((map == NULL) || (key == NULL)) + return -EINVAL; + + i = hash_str(key, map->maxsize); + map->data[i] = remove_entry(map->data[i], key, &map->entries); + + return 0; +} + +size_t +hashmap_size(hashmap_t map) +{ + if (map != NULL) + return map->entries; + else + return 0; +} + +int +hashmap_free(hashmap_t map) +{ + size_t i; + + if (map == NULL) + return -EINVAL; + + /* "children" first */ + for(i = 0; i < map->maxsize; i++) { + map->data[i] = remove_all(map->data[i], &map->entries); + } + free(map->data); + free(map); + + return 0; +} + +int +hashmap_dump(hashmap_t map) +{ + size_t i; + if (map == NULL) + return -EINVAL; + + for(i = 0; i < map->maxsize; i++) { + if (map->data[i] != NULL) { + printf("[%zd]: ", i); + print_all(map->data[i]); + } + } + + return 0; +} + +static const char** +sort_key_vector(const char** a, size_t size) +{ + /* uses bubblesort */ + size_t i, j; + const char* tmp; + + if (size <= 0) + return a; + + for (i = size - 1; i > 0; i--) { + for (j = 0; j < i; j++) { + if (strcmp(a[j], a[j+1]) > 0) { + tmp = a[j]; + a[j] = a[j+1]; + a[j+1] = tmp; + } + } + } + return a; +} + +const char** +hashmap_get_key_vector(hashmap_t map, size_t* size, int sort) +{ + const char** res; + size_t i, j; + *size = map->entries; + + res = (const char**) malloc(*size * sizeof(char*)); + if (res == NULL) + return NULL; + + j = 0; + for(i=0; i < map->maxsize; i++) { + keys_to_array(map->data[i], res, &j); + } + + if (sort) + res = sort_key_vector(res, *size); + + return res; +} + +int +hashmap_key_is_in_vector(const char** vec, size_t size, const char* key) +{ + size_t i; + for (i = 0; i < size; i++) { + if (strcmp(vec[i], key) == 0) /* found */ + return 1; + } + + return 0; +} + +const char** +hashmap_get_update_key_vector(const char** vec1, size_t vec1_size, + const char** vec2, size_t vec2_size, size_t* res_size) +{ + const char** res; + size_t i, j; + + *res_size = vec2_size; + + res = (const char**) malloc(*res_size * sizeof(char*)); + if (res == NULL) + return NULL; + + /* get all keys from vec2 which are not set in vec1 */ + j = 0; + for (i = 0; i < vec2_size; i++) { + if (!hashmap_key_is_in_vector(vec1, vec1_size, vec2[i])) + res[j++] = vec2[i]; + } + + *res_size = j; + return res; +} diff --git a/ubi-utils/src/hashmap.h b/ubi-utils/src/hashmap.h new file mode 100644 index 0000000..1b13e95 --- /dev/null +++ b/ubi-utils/src/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 +#include + +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/libiniparser.c b/ubi-utils/src/libiniparser.c deleted file mode 100644 index 3bea51e..0000000 --- a/ubi-utils/src/libiniparser.c +++ /dev/null @@ -1,646 +0,0 @@ - -/*-------------------------------------------------------------------------*/ -/** - @file iniparser.c - @author N. Devillard - @date Sep 2007 - @version 3.0 - @brief Parser for ini files. -*/ -/*--------------------------------------------------------------------------*/ -/* - $Id: iniparser.c,v 2.18 2008-01-03 18:35:39 ndevilla Exp $ - $Revision: 2.18 $ - $Date: 2008-01-03 18:35:39 $ -*/ -/*---------------------------- Includes ------------------------------------*/ -#include -#include - -/*---------------------------- Defines -------------------------------------*/ -#define ASCIILINESZ (1024) -#define INI_INVALID_KEY ((char*)-1) - -/*--------------------------------------------------------------------------- - Private to this module - ---------------------------------------------------------------------------*/ -/** - * This enum stores the status for each parsed line (internal use only). - */ -typedef enum _line_status_ { - LINE_UNPROCESSED, - LINE_ERROR, - LINE_EMPTY, - LINE_COMMENT, - LINE_SECTION, - LINE_VALUE -} line_status ; - -/*-------------------------------------------------------------------------*/ -/** - @brief Convert a string to lowercase. - @param s String to convert. - @return ptr to statically allocated string. - - This function returns a pointer to a statically allocated string - containing a lowercased version of the input string. Do not free - or modify the returned string! Since the returned string is statically - allocated, it will be modified at each function call (not re-entrant). - */ -/*--------------------------------------------------------------------------*/ -static char * strlwc(const char * s) -{ - static char l[ASCIILINESZ+1]; - int i ; - - if (s==NULL) return NULL ; - memset(l, 0, ASCIILINESZ+1); - i=0 ; - while (s[i] && i l) { - if (!isspace((int)*(last-1))) - break ; - last -- ; - } - *last = (char)0; - return (char*)l ; -} - -/*-------------------------------------------------------------------------*/ -/** - @brief Get number of sections in a dictionary - @param d Dictionary to examine - @return int Number of sections found in dictionary - - This function returns the number of sections found in a dictionary. - The test to recognize sections is done on the string stored in the - dictionary: a section name is given as "section" whereas a key is - stored as "section:key", thus the test looks for entries that do not - contain a colon. - - This clearly fails in the case a section name contains a colon, but - this should simply be avoided. - - This function returns -1 in case of error. - */ -/*--------------------------------------------------------------------------*/ -int iniparser_getnsec(dictionary * d) -{ - int i ; - int nsec ; - - if (d==NULL) return -1 ; - nsec=0 ; - for (i=0 ; isize ; i++) { - if (d->key[i]==NULL) - continue ; - if (strchr(d->key[i], ':')==NULL) { - nsec ++ ; - } - } - return nsec ; -} - -/*-------------------------------------------------------------------------*/ -/** - @brief Get name for section n in a dictionary. - @param d Dictionary to examine - @param n Section number (from 0 to nsec-1). - @return Pointer to char string - - This function locates the n-th section in a dictionary and returns - its name as a pointer to a string statically allocated inside the - dictionary. Do not free or modify the returned string! - - This function returns NULL in case of error. - */ -/*--------------------------------------------------------------------------*/ -char * iniparser_getsecname(dictionary * d, int n) -{ - int i ; - int foundsec ; - - if (d==NULL || n<0) return NULL ; - foundsec=0 ; - for (i=0 ; isize ; i++) { - if (d->key[i]==NULL) - continue ; - if (strchr(d->key[i], ':')==NULL) { - foundsec++ ; - if (foundsec>n) - break ; - } - } - if (foundsec<=n) { - return NULL ; - } - return d->key[i] ; -} - -/*-------------------------------------------------------------------------*/ -/** - @brief Dump a dictionary to an opened file pointer. - @param d Dictionary to dump. - @param f Opened file pointer to dump to. - @return void - - This function prints out the contents of a dictionary, one element by - line, onto the provided file pointer. It is OK to specify @c stderr - or @c stdout as output files. This function is meant for debugging - purposes mostly. - */ -/*--------------------------------------------------------------------------*/ -void iniparser_dump(dictionary * d, FILE * f) -{ - int i ; - - if (d==NULL || f==NULL) return ; - for (i=0 ; isize ; i++) { - if (d->key[i]==NULL) - continue ; - if (d->val[i]!=NULL) { - fprintf(f, "[%s]=[%s]\n", d->key[i], d->val[i]); - } else { - fprintf(f, "[%s]=UNDEF\n", d->key[i]); - } - } - return ; -} - -/*-------------------------------------------------------------------------*/ -/** - @brief Save a dictionary to a loadable ini file - @param d Dictionary to dump - @param f Opened file pointer to dump to - @return void - - This function dumps a given dictionary into a loadable ini file. - It is Ok to specify @c stderr or @c stdout as output files. - */ -/*--------------------------------------------------------------------------*/ -void iniparser_dump_ini(dictionary * d, FILE * f) -{ - int i, j ; - char keym[ASCIILINESZ+1]; - int nsec ; - char * secname ; - int seclen ; - - if (d==NULL || f==NULL) return ; - - nsec = iniparser_getnsec(d); - if (nsec<1) { - /* No section in file: dump all keys as they are */ - for (i=0 ; isize ; i++) { - if (d->key[i]==NULL) - continue ; - fprintf(f, "%s = %s\n", d->key[i], d->val[i]); - } - return ; - } - for (i=0 ; isize ; j++) { - if (d->key[j]==NULL) - continue ; - if (!strncmp(d->key[j], keym, seclen+1)) { - fprintf(f, - "%-30s = %s\n", - d->key[j]+seclen+1, - d->val[j] ? d->val[j] : ""); - } - } - } - fprintf(f, "\n"); - return ; -} - -/*-------------------------------------------------------------------------*/ -/** - @brief Get the string associated to a key - @param d Dictionary to search - @param key Key string to look for - @param def Default value to return if key not found. - @return pointer to statically allocated character string - - This function queries a dictionary for a key. A key as read from an - ini file is given as "section:key". If the key cannot be found, - the pointer passed as 'def' is returned. - The returned char pointer is pointing to a string allocated in - the dictionary, do not free or modify it. - */ -/*--------------------------------------------------------------------------*/ -char * iniparser_getstring(dictionary * d, const char * key, char * def) -{ - char * lc_key ; - char * sval ; - - if (d==NULL || key==NULL) - return def ; - - lc_key = strlwc(key); - sval = dictionary_get(d, lc_key, def); - return sval ; -} - -/*-------------------------------------------------------------------------*/ -/** - @brief Get the string associated to a key, convert to an int - @param d Dictionary to search - @param key Key string to look for - @param notfound Value to return in case of error - @return integer - - This function queries a dictionary for a key. A key as read from an - ini file is given as "section:key". If the key cannot be found, - the notfound value is returned. - - Supported values for integers include the usual C notation - so decimal, octal (starting with 0) and hexadecimal (starting with 0x) - are supported. Examples: - - "42" -> 42 - "042" -> 34 (octal -> decimal) - "0x42" -> 66 (hexa -> decimal) - - Warning: the conversion may overflow in various ways. Conversion is - totally outsourced to strtol(), see the associated man page for overflow - handling. - - Credits: Thanks to A. Becker for suggesting strtol() - */ -/*--------------------------------------------------------------------------*/ -int iniparser_getint(dictionary * d, const char * key, int notfound) -{ - char * str ; - - str = iniparser_getstring(d, key, INI_INVALID_KEY); - if (str==INI_INVALID_KEY) return notfound ; - return (int)strtol(str, NULL, 0); -} - -/*-------------------------------------------------------------------------*/ -/** - @brief Get the string associated to a key, convert to a double - @param d Dictionary to search - @param key Key string to look for - @param notfound Value to return in case of error - @return double - - This function queries a dictionary for a key. A key as read from an - ini file is given as "section:key". If the key cannot be found, - the notfound value is returned. - */ -/*--------------------------------------------------------------------------*/ -double iniparser_getdouble(dictionary * d, char * key, double notfound) -{ - char * str ; - - str = iniparser_getstring(d, key, INI_INVALID_KEY); - if (str==INI_INVALID_KEY) return notfound ; - return atof(str); -} - -/*-------------------------------------------------------------------------*/ -/** - @brief Get the string associated to a key, convert to a boolean - @param d Dictionary to search - @param key Key string to look for - @param notfound Value to return in case of error - @return integer - - This function queries a dictionary for a key. A key as read from an - ini file is given as "section:key". If the key cannot be found, - the notfound value is returned. - - A true boolean is found if one of the following is matched: - - - A string starting with 'y' - - A string starting with 'Y' - - A string starting with 't' - - A string starting with 'T' - - A string starting with '1' - - A false boolean is found if one of the following is matched: - - - A string starting with 'n' - - A string starting with 'N' - - A string starting with 'f' - - A string starting with 'F' - - A string starting with '0' - - The notfound value returned if no boolean is identified, does not - necessarily have to be 0 or 1. - */ -/*--------------------------------------------------------------------------*/ -int iniparser_getboolean(dictionary * d, const char * key, int notfound) -{ - char * c ; - int ret ; - - c = iniparser_getstring(d, key, INI_INVALID_KEY); - if (c==INI_INVALID_KEY) return notfound ; - if (c[0]=='y' || c[0]=='Y' || c[0]=='1' || c[0]=='t' || c[0]=='T') { - ret = 1 ; - } else if (c[0]=='n' || c[0]=='N' || c[0]=='0' || c[0]=='f' || c[0]=='F') { - ret = 0 ; - } else { - ret = notfound ; - } - return ret; -} - -/*-------------------------------------------------------------------------*/ -/** - @brief Finds out if a given entry exists in a dictionary - @param ini Dictionary to search - @param entry Name of the entry to look for - @return integer 1 if entry exists, 0 otherwise - - Finds out if a given entry exists in the dictionary. Since sections - are stored as keys with NULL associated values, this is the only way - of querying for the presence of sections in a dictionary. - */ -/*--------------------------------------------------------------------------*/ -int iniparser_find_entry( - dictionary * ini, - char * entry -) -{ - int found=0 ; - if (iniparser_getstring(ini, entry, INI_INVALID_KEY)!=INI_INVALID_KEY) { - found = 1 ; - } - return found ; -} - -/*-------------------------------------------------------------------------*/ -/** - @brief Set an entry in a dictionary. - @param ini Dictionary to modify. - @param entry Entry to modify (entry name) - @param val New value to associate to the entry. - @return int 0 if Ok, -1 otherwise. - - If the given entry can be found in the dictionary, it is modified to - contain the provided value. If it cannot be found, -1 is returned. - It is Ok to set val to NULL. - */ -/*--------------------------------------------------------------------------*/ -int iniparser_set(dictionary * ini, char * entry, char * val) -{ - return dictionary_set(ini, strlwc(entry), val) ; -} - -/*-------------------------------------------------------------------------*/ -/** - @brief Delete an entry in a dictionary - @param ini Dictionary to modify - @param entry Entry to delete (entry name) - @return void - - If the given entry can be found, it is deleted from the dictionary. - */ -/*--------------------------------------------------------------------------*/ -void iniparser_unset(dictionary * ini, char * entry) -{ - dictionary_unset(ini, strlwc(entry)); -} - -/*-------------------------------------------------------------------------*/ -/** - @brief Load a single line from an INI file - @param input_line Input line, may be concatenated multi-line input - @param section Output space to store section - @param key Output space to store key - @param value Output space to store value - @return line_status value - */ -/*--------------------------------------------------------------------------*/ -static line_status iniparser_line( - char * input_line, - char * section, - char * key, - char * value) -{ - line_status sta ; - char line[ASCIILINESZ+1]; - int len ; - - strcpy(line, strstrip(input_line)); - len = (int)strlen(line); - - sta = LINE_UNPROCESSED ; - if (len<1) { - /* Empty line */ - sta = LINE_EMPTY ; - } else if (line[0]=='#') { - /* Comment line */ - sta = LINE_COMMENT ; - } else if (line[0]=='[' && line[len-1]==']') { - /* Section name */ - sscanf(line, "[%[^]]", section); - strcpy(section, strstrip(section)); - strcpy(section, strlwc(section)); - sta = LINE_SECTION ; - } else if (sscanf (line, "%[^=] = \"%[^\"]\"", key, value) == 2 - || sscanf (line, "%[^=] = '%[^\']'", key, value) == 2 - || sscanf (line, "%[^=] = %[^;#]", key, value) == 2) { - /* Usual key=value, with or without comments */ - strcpy(key, strstrip(key)); - strcpy(key, strlwc(key)); - strcpy(value, strstrip(value)); - /* - * sscanf cannot handle '' or "" as empty values - * this is done here - */ - if (!strcmp(value, "\"\"") || (!strcmp(value, "''"))) { - value[0]=0 ; - } - sta = LINE_VALUE ; - } else if (sscanf(line, "%[^=] = %[;#]", key, value)==2 - || sscanf(line, "%[^=] %[=]", key, value) == 2) { - /* - * Special cases: - * key= - * key=; - * key=# - */ - strcpy(key, strstrip(key)); - strcpy(key, strlwc(key)); - value[0]=0 ; - sta = LINE_VALUE ; - } else { - /* Generate syntax error */ - sta = LINE_ERROR ; - } - return sta ; -} - -/*-------------------------------------------------------------------------*/ -/** - @brief Parse an ini file and return an allocated dictionary object - @param ininame Name of the ini file to read. - @return Pointer to newly allocated dictionary - - This is the parser for ini files. This function is called, providing - the name of the file to be read. It returns a dictionary object that - should not be accessed directly, but through accessor functions - instead. - - The returned dictionary must be freed using iniparser_freedict(). - */ -/*--------------------------------------------------------------------------*/ -dictionary * iniparser_load(const char * ininame) -{ - FILE * in ; - - char line [ASCIILINESZ+1] ; - char section [ASCIILINESZ+1] ; - char key [ASCIILINESZ+1] ; - char tmp [ASCIILINESZ+1] ; - char val [ASCIILINESZ+1] ; - - int last=0 ; - int len ; - int lineno=0 ; - int errs=0; - - dictionary * dict ; - - if ((in=fopen(ininame, "r"))==NULL) { - fprintf(stderr, "iniparser: cannot open %s\n", ininame); - return NULL ; - } - - dict = dictionary_new(0) ; - if (!dict) { - fclose(in); - return NULL ; - } - - memset(line, 0, ASCIILINESZ); - memset(section, 0, ASCIILINESZ); - memset(key, 0, ASCIILINESZ); - memset(val, 0, ASCIILINESZ); - last=0 ; - - while (fgets(line+last, ASCIILINESZ-last, in)!=NULL) { - lineno++ ; - len = (int)strlen(line)-1; - /* Safety check against buffer overflows */ - if (line[len]!='\n') { - fprintf(stderr, - "iniparser: input line too long in %s (%d)\n", - ininame, - lineno); - dictionary_del(dict); - fclose(in); - return NULL ; - } - /* Get rid of \n and spaces at end of line */ - while ((len>=0) && - ((line[len]=='\n') || (isspace(line[len])))) { - line[len]=0 ; - len-- ; - } - /* Detect multi-line */ - if (line[len]=='\\') { - /* Multi-line value */ - last=len ; - continue ; - } else { - last=0 ; - } - switch (iniparser_line(line, section, key, val)) { - case LINE_EMPTY: - case LINE_COMMENT: - break ; - - case LINE_SECTION: - errs = dictionary_set(dict, section, NULL); - break ; - - case LINE_VALUE: - sprintf(tmp, "%s:%s", section, key); - errs = dictionary_set(dict, tmp, val) ; - break ; - - case LINE_ERROR: - fprintf(stderr, "iniparser: syntax error in %s (%d):\n", - ininame, - lineno); - fprintf(stderr, "-> %s\n", line); - errs++ ; - break; - - default: - break ; - } - memset(line, 0, ASCIILINESZ); - last=0; - if (errs<0) { - fprintf(stderr, "iniparser: memory allocation failure\n"); - break ; - } - } - if (errs) { - dictionary_del(dict); - dict = NULL ; - } - fclose(in); - return dict ; -} - -/*-------------------------------------------------------------------------*/ -/** - @brief Free all memory associated to an ini dictionary - @param d Dictionary to free - @return void - - Free all memory associated to an ini dictionary. - It is mandatory to call this function before the dictionary object - gets out of the current context. - */ -/*--------------------------------------------------------------------------*/ -void iniparser_freedict(dictionary * d) -{ - dictionary_del(d); -} - -/* vim: set ts=4 et sw=4 tw=75 */ diff --git a/ubi-utils/src/libpfiflash.c b/ubi-utils/src/libpfiflash.c new file mode 100644 index 0000000..7e3d3b3 --- /dev/null +++ b/ubi-utils/src/libpfiflash.c @@ -0,0 +1,1325 @@ +/* + * Copyright International Business Machines Corp., 2006, 2007 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * Authors: Oliver Lohmann + * Drake Dowsett + * Contact: Andreas Arnez + */ + +/* TODO Compare 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 +#include +#include +#include +#define __USE_GNU +#include +#include +#include +#include + +#include +#include + +#include /* FIXME Is this ok here? */ +#include + +#include "pfiflash_error.h" +#include "ubimirror.h" +#include "error.h" +#include "reader.h" +#include "example_ubi.h" +#include "bootenv.h" + +/* ubi-header.h and crc32.h needed for CRC checking */ +#include /* FIXME Is this ok here? */ +#include "crc32.h" + +#define ubi_unused __attribute__((unused)) + +#define COMPARE_BUFFER_SIZE 2048 + +#define DEFAULT_DEV_PATTERN "/dev/ubi%d" +#define DEFAULT_VOL_PATTERN "/dev/ubi%d_%d" + +static const char copyright [] ubi_unused = + "Copyright International Business Machines Corp., 2006, 2007"; + +/* simply clear buffer, then write into front of it */ +#define EBUF(fmt...) \ + snprintf(err_buf, err_buf_size, fmt); + +/* make a history of buffer and then prepend something in front */ +#define EBUF_PREPEND(fmt) \ + do { \ + int EBUF_HISTORY_LENGTH = strlen(err_buf); \ + char EBUF_HISTORY[EBUF_HISTORY_LENGTH + 1]; \ + strncpy(EBUF_HISTORY, err_buf, EBUF_HISTORY_LENGTH + 1);\ + EBUF(fmt ": %s", EBUF_HISTORY); \ + } while (0) + +/* An array of PDD function pointers indexed by the algorithm. */ +static pdd_func_t pdd_funcs[PDD_HANDLING_NUM] = + { + &bootenv_pdd_keep, + &bootenv_pdd_merge, + &bootenv_pdd_overwrite + }; + +typedef enum ubi_update_process_t { + UBI_REMOVE = 0, + UBI_WRITE, + UBI_COMPARE, +} ubi_update_process_t; + + +/** + * skip_raw_volumes - reads data from pfi to advance fp past raw block + * @pfi: fp to pfi data + * @pfi_raws: header information + * + * Error handling): + * when early EOF in pfi data + * - returns -PFIFLASH_ERR_EOF, err_buf matches text to err + * when file I/O error + * - returns -PFIFLASH_ERR_FIO, err_buf matches text to err + **/ +static int +skip_raw_volumes(FILE* pfi, list_t pfi_raws, + char* err_buf, size_t err_buf_size) +{ + int rc; + void *i; + list_t ptr; + + if (is_empty(pfi_raws)) + return 0; + + rc = 0; + foreach(i, ptr, pfi_raws) { + size_t j; + pfi_raw_t raw; + + raw = (pfi_raw_t)i; + for(j = 0; j < raw->data_size; j++) { + int c; + + c = fgetc(pfi); + if (c == EOF) + rc = -PFIFLASH_ERR_EOF; + else if (ferror(pfi)) + rc = -PFIFLASH_ERR_FIO; + + if (rc != 0) + goto err; + } + } + + err: + EBUF(PFIFLASH_ERRSTR[-rc]); + return rc; +} + + +/** + * my_ubi_mkvol - wraps the ubi_mkvol functions and impl. bootenv update hook + * @devno: UBI device number. + * @s: Current seqnum. + * @u: Information about the UBI volume from the PFI. + * + * Error handling: + * when UBI system couldn't be opened + * - returns -PFIFLASH_ERR_UBI_OPEN, err_buf matches text to err + * when UBI system couldn't create a volume + * - returns -PFIFLASH_ERR_UBI_MKVOL, err_buf matches text to err + **/ +static int +my_ubi_mkvol(int devno, int s, pfi_ubi_t u, + char *err_buf, size_t err_buf_size) +{ + int rc, type; + char path[PATH_MAX]; + libubi_t ulib; + struct ubi_mkvol_request req; + + rc = 0; + ulib = NULL; + + log_msg("[ ubimkvol id=%d, size=%d, data_size=%d, type=%d, " + "alig=%d, nlen=%d, name=%s", + u->ids[s], u->size, u->data_size, u->type, u->alignment, + strnlen(u->names[s], PFI_UBI_VOL_NAME_LEN), u->names[s]); + + ulib = libubi_open(); + if (ulib == NULL) { + rc = -PFIFLASH_ERR_UBI_OPEN; + EBUF(PFIFLASH_ERRSTR[-rc]); + goto err; + } + + switch (u->type) { + case pfi_ubi_static: + type = UBI_STATIC_VOLUME; break; + case pfi_ubi_dynamic: + default: + type = UBI_DYNAMIC_VOLUME; + } + + snprintf(path, PATH_MAX, DEFAULT_DEV_PATTERN, devno); + + req.vol_id = u->ids[s]; + req.alignment = u->alignment; + req.bytes = u->size; + req.vol_type = type; + req.name = u->names[s]; + + rc = ubi_mkvol(ulib, path, &req); + if (rc != 0) { + rc = -PFIFLASH_ERR_UBI_MKVOL; + EBUF(PFIFLASH_ERRSTR[-rc], u->ids[s]); + goto err; + } + + err: + if (ulib != NULL) + libubi_close(ulib); + + return rc; +} + + +/** + * my_ubi_rmvol - a wrapper around the UBI library function ubi_rmvol + * @devno UBI device number + * @id UBI volume id to remove + * + * If the volume does not exist, the function will return success. + * + * Error handling: + * when UBI system couldn't be opened + * - returns -PFIFLASH_ERR_UBI_OPEN, err_buf matches text to err + * when UBI system couldn't update (truncate) a volume + * - returns -PFIFLASH_ERR_UBI_VOL_UPDATE, err_buf matches text to err + * when UBI system couldn't remove a volume + * - returns -PFIFLASH_ERR_UBI_RMVOL, err_buf matches text to err + **/ +static int +my_ubi_rmvol(int devno, uint32_t id, + char *err_buf, size_t err_buf_size) +{ + int rc, fd; + char path[PATH_MAX]; + libubi_t ulib; + + rc = 0; + ulib = NULL; + + log_msg("[ ubirmvol id=%d", id); + + ulib = libubi_open(); + if (ulib == NULL) { + rc = -PFIFLASH_ERR_UBI_OPEN; + EBUF(PFIFLASH_ERRSTR[-rc]); + goto err; + } + + snprintf(path, PATH_MAX, DEFAULT_VOL_PATTERN, devno, id); + + /* truncate whether it exist or not */ + fd = open(path, O_RDWR); + if (fd < 0) { + libubi_close(ulib); + return 0; /* not existent, return 0 */ + } + + rc = ubi_update_start(ulib, fd, 0); + close(fd); + if (rc < 0) { + rc = -PFIFLASH_ERR_UBI_VOL_UPDATE; + EBUF(PFIFLASH_ERRSTR[-rc], id); + goto err; /* if EBUSY than empty device, continue */ + } + + snprintf(path, PATH_MAX, DEFAULT_DEV_PATTERN, devno); + + rc = ubi_rmvol(ulib, path, id); + if (rc != 0) { +#ifdef DEBUG + int rc_old = rc; + dbg_msg("Remove UBI volume %d returned with error: %d " + "errno=%d", id, rc_old, errno); +#endif + + rc = -PFIFLASH_ERR_UBI_RMVOL; + EBUF(PFIFLASH_ERRSTR[-rc], id); + + /* 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 */ + + goto err; + } + + err: + if (ulib != NULL) + libubi_close(ulib); + + return rc; +} + + +/** + * read_bootenv_volume - reads the current bootenv data from id into be_old + * @devno UBI device number + * @id UBI volume id to remove + * @bootenv_old to hold old boot_env data + * + * Error handling: + * when UBI system couldn't be opened + * - returns -PFIFLASH_ERR_UBI_OPEN, err_buf matches text to err + * when UBI system couldn't open a volume to read + * - returns -PFIFLASH_ERR_UBI_VOL_FOPEN, err_buf matches text to err + * when couldn't read bootenv data + * - returns -PFIFLASH_ERR_BOOTENV_READ, err_buf matches text to err + **/ +static int +read_bootenv_volume(int devno, uint32_t id, bootenv_t bootenv_old, + char *err_buf, size_t err_buf_size) +{ + int rc; + FILE* fp_in; + char path[PATH_MAX]; + libubi_t ulib; + + rc = 0; + fp_in = NULL; + ulib = NULL; + + ulib = libubi_open(); + if (ulib == NULL) { + rc = -PFIFLASH_ERR_UBI_OPEN; + EBUF(PFIFLASH_ERRSTR[-rc]); + goto err; + } + + snprintf(path, PATH_MAX, DEFAULT_VOL_PATTERN, devno, id); + + fp_in = fopen(path, "r"); + if (!fp_in) { + rc = -PFIFLASH_ERR_UBI_VOL_FOPEN; + EBUF(PFIFLASH_ERRSTR[-rc], id); + goto err; + } + + log_msg("[ reading old bootenvs ..."); + + /* Save old bootenvs for reference */ + rc = bootenv_read(fp_in, bootenv_old, BOOTENV_MAXSIZE); + if (rc != 0) { + rc = -PFIFLASH_ERR_BOOTENV_READ; + EBUF(PFIFLASH_ERRSTR[-rc]); + goto err; + } + + err: + if (fp_in) + fclose(fp_in); + if (ulib) + libubi_close(ulib); + + return rc; +} + + +/** + * write_bootenv_volume - writes data from PFI file int to bootenv UBI volume + * @devno UBI device number + * @id UBI volume id + * @bootend_old old PDD data from machine + * @pdd_f function to handle PDD with + * @fp_in new pdd data contained in PFI + * @fp_in_size data size of new pdd data in PFI + * @pfi_crc crc value from PFI header + * + * Error handling: + * when UBI system couldn't be opened + * - returns -PFIFLASH_ERR_UBI_OPEN, err_buf matches text to err + * when bootenv can't be created + * - returns -PFIFLASH_ERR_BOOTENV_CREATE, err_buf matches text to err + * when bootenv can't be read + * - returns -PFIFLASH_ERR_BOOTENV_READ, err_buf matches text to err + * when PDD handling function returns and error + * - passes rc and err_buf data + * when CRC check fails + * - returns -PFIFLASH_ERR_CRC_CHECK, err_buf matches text to err + * when bootenv can't be resized + * - returns -PFIFLASH_ERR_BOOTENV_SIZE, err_buf matches text to err + * when UBI system couldn't open a volume + * - returns -PFIFLASH_ERR_UBI_VOL_FOPEN, err_buf matches text to err + * when couldn't write bootenv data + * - returns -PFIFLASH_ERR_BOOTENV_WRITE, err_buf matches text to err + **/ +static int +write_bootenv_volume(int devno, uint32_t id, bootenv_t bootenv_old, + pdd_func_t pdd_f, FILE* fp_in, size_t fp_in_size, + uint32_t pfi_crc, + char *err_buf, size_t err_buf_size) +{ + int rc, warnings, fd_out; + uint32_t crc; + char path[PATH_MAX]; + size_t update_size; + FILE *fp_out; + bootenv_t bootenv_new, bootenv_res; + libubi_t ulib; + + rc = 0; + warnings = 0; + crc = 0; + update_size = 0; + fp_out = NULL; + bootenv_new = NULL; + bootenv_res = NULL; + ulib = NULL; + + log_msg("[ ubiupdatevol bootenv id=%d, fp_in=%p", 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* + */ + + ulib = libubi_open(); + if (ulib == NULL) { + rc = -PFIFLASH_ERR_UBI_OPEN; + EBUF(PFIFLASH_ERRSTR[-rc]); + goto err; + } + + rc = bootenv_create(&bootenv_new); + if (rc != 0) { + rc = -PFIFLASH_ERR_BOOTENV_CREATE; + EBUF(PFIFLASH_ERRSTR[-rc], " 'new'"); + goto err; + } + + rc = bootenv_create(&bootenv_res); + if (rc != 0) { + rc = -PFIFLASH_ERR_BOOTENV_CREATE; + EBUF(PFIFLASH_ERRSTR[-rc], " 'res'"); + goto err; + } + + rc = bootenv_read_crc(fp_in, bootenv_new, fp_in_size, &crc); + if (rc != 0) { + rc = -PFIFLASH_ERR_BOOTENV_READ; + EBUF(PFIFLASH_ERRSTR[-rc]); + goto err; + } else if (crc != pfi_crc) { + rc = -PFIFLASH_ERR_CRC_CHECK; + EBUF(PFIFLASH_ERRSTR[-rc], pfi_crc, crc); + goto err; + } + + rc = pdd_f(bootenv_old, bootenv_new, &bootenv_res, &warnings, + err_buf, err_buf_size); + if (rc != 0) { + EBUF_PREPEND("handling PDD"); + goto err; + } + else if (warnings) + /* TODO do something with warnings */ + dbg_msg("A warning in the PDD operation occured: %d", + warnings); + + rc = bootenv_size(bootenv_res, &update_size); + if (rc != 0) { + rc = -PFIFLASH_ERR_BOOTENV_SIZE; + EBUF(PFIFLASH_ERRSTR[-rc]); + goto err; + } + + snprintf(path, PATH_MAX, DEFAULT_VOL_PATTERN, devno, id); + + fd_out = open(path, O_RDWR); + if (fd_out < 0) { + rc = -PFIFLASH_ERR_UBI_VOL_FOPEN; + EBUF(PFIFLASH_ERRSTR[-rc], id); + goto err; + } + fp_out = fdopen(fd_out, "r+"); + if (!fp_out) { + rc = -PFIFLASH_ERR_UBI_VOL_FOPEN; + EBUF(PFIFLASH_ERRSTR[-rc], id); + goto err; + } + rc = ubi_update_start(ulib, fd_out, update_size); + if (rc < 0) { + rc = -PFIFLASH_ERR_UBI_VOL_UPDATE; + EBUF(PFIFLASH_ERRSTR[-rc], id); + goto err; + } + + rc = bootenv_write(fp_out, bootenv_res); + if (rc != 0) { + rc = -PFIFLASH_ERR_BOOTENV_WRITE; + EBUF(PFIFLASH_ERRSTR[-rc], devno, id); + goto err; + } + + err: + if (ulib != NULL) + libubi_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; +} + + +/** + * write_normal_volume - writes data from PFI file int to regular UBI volume + * @devno UBI device number + * @id UBI volume id + * @update_size size of data stream + * @fp_in PFI data file pointer + * @pfi_crc CRC data from PFI header + * + * Error handling: + * when UBI system couldn't be opened + * - returns -PFIFLASH_ERR_UBI_OPEN, err_buf matches text to err + * when UBI system couldn't open a volume + * - returns -PFIFLASH_ERR_UBI_VOL_FOPEN, err_buf matches text to err + * when unexpected EOF is encountered + * - returns -PFIFLASH_ERR_EOF, err_buf matches text to err + * when file I/O error + * - returns -PFIFLASH_ERR_FIO, err_buf matches text to err + * when CRC check fails + * - retruns -PFIFLASH_ERR_CRC_CHECK, err_buf matches text to err + **/ +static int +write_normal_volume(int devno, uint32_t id, size_t update_size, FILE* fp_in, + uint32_t pfi_crc, + char *err_buf, size_t err_buf_size) +{ + int rc, fd_out; + uint32_t crc, crc32_table[256]; + char path[PATH_MAX]; + size_t bytes_left; + FILE* fp_out; + libubi_t ulib; + + rc = 0; + crc = UBI_CRC32_INIT; + bytes_left = update_size; + fp_out = NULL; + ulib = NULL; + + log_msg("[ ubiupdatevol id=%d, update_size=%d fp_in=%p", + id, update_size, fp_in); + + ulib = libubi_open(); + if (ulib == NULL) { + rc = -PFIFLASH_ERR_UBI_OPEN; + EBUF(PFIFLASH_ERRSTR[-rc]); + goto err; + } + + snprintf(path, PATH_MAX, DEFAULT_VOL_PATTERN, devno, id); + + fd_out = open(path, O_RDWR); + if (fd_out < 0) { + rc = -PFIFLASH_ERR_UBI_VOL_FOPEN; + EBUF(PFIFLASH_ERRSTR[-rc], id); + goto err; + } + fp_out = fdopen(fd_out, "r+"); + if (!fp_out) { + rc = -PFIFLASH_ERR_UBI_VOL_FOPEN; + EBUF(PFIFLASH_ERRSTR[-rc], id); + goto err; + } + rc = ubi_update_start(ulib, fd_out, update_size); + if (rc < 0) { + rc = -PFIFLASH_ERR_UBI_VOL_UPDATE; + EBUF(PFIFLASH_ERRSTR[-rc], id); + goto err; + } + + init_crc32_table(crc32_table); + while (bytes_left) { + char buf[1024]; + size_t to_rw = sizeof buf > bytes_left ? + bytes_left : sizeof buf; + if (fread(buf, 1, to_rw, fp_in) != to_rw) { + rc = -PFIFLASH_ERR_EOF; + EBUF(PFIFLASH_ERRSTR[-rc]); + goto err; + } + crc = clc_crc32(crc32_table, crc, buf, to_rw); + if (fwrite(buf, 1, to_rw, fp_out) != to_rw) { + rc = -PFIFLASH_ERR_FIO; + EBUF(PFIFLASH_ERRSTR[-rc]); + goto err; + } + bytes_left -= to_rw; + } + + if (crc != pfi_crc) { + rc = -PFIFLASH_ERR_CRC_CHECK; + EBUF(PFIFLASH_ERRSTR[-rc], pfi_crc, crc); + goto err; + } + + err: + if (fp_out) + fclose(fp_out); + if (ulib) + libubi_close(ulib); + + return rc; +} + +static int compare_bootenv(FILE *fp_pfi, FILE **fp_flash, uint32_t ids_size, + uint32_t data_size, pdd_func_t pdd_f, char *err_buf, + size_t err_buf_size) +{ + int rc, warnings = 0; + unsigned int i; + bootenv_t bootenv_pfi, bootenv_res = NULL, bootenv_flash = NULL; + + rc = bootenv_create(&bootenv_pfi); + if (rc != 0) { + rc = -PFIFLASH_ERR_BOOTENV_CREATE; + goto err; + } + + rc = bootenv_create(&bootenv_res); + if (rc != 0) { + rc = -PFIFLASH_ERR_BOOTENV_CREATE; + goto err; + } + + rc = bootenv_read(fp_pfi, bootenv_pfi, data_size); + if (rc != 0) { + rc = -PFIFLASH_ERR_BOOTENV_READ; + goto err; + } + + for (i = 0; i < ids_size; i++) { + rc = bootenv_create(&bootenv_flash); + if (rc != 0) { + rc = -PFIFLASH_ERR_BOOTENV_CREATE; + goto err; + } + + rc = bootenv_read(fp_flash[i], bootenv_flash, BOOTENV_MAXSIZE); + if (rc != 0) { + rc = -PFIFLASH_ERR_BOOTENV_READ; + goto err; + } + + rc = pdd_f(bootenv_flash, bootenv_pfi, &bootenv_res, + &warnings, err_buf, err_buf_size); + if (rc != 0) { + rc = -PFIFLASH_ERR_PDD_UNKNOWN; + goto err; + } + + rc = bootenv_compare(bootenv_flash, bootenv_res); + if (rc > 0) { + rc = -PFIFLASH_CMP_DIFF; + goto err; + } else if (rc < 0) { + rc = -PFIFLASH_ERR_COMPARE; + goto err; + } + + bootenv_destroy(&bootenv_flash); + bootenv_flash = NULL; + } + +err: + if (bootenv_pfi) + bootenv_destroy(&bootenv_pfi); + if (bootenv_res) + bootenv_destroy(&bootenv_res); + if (bootenv_flash) + bootenv_destroy(&bootenv_flash); + + return rc; +} + +static int compare_data(FILE *fp_pfi, FILE **fp_flash, uint32_t ids_size, + uint32_t bytes_left) +{ + unsigned int i; + size_t read_bytes, rc = 0; + char buf_pfi[COMPARE_BUFFER_SIZE]; + char *buf_flash[ids_size]; + + for (i = 0; i < ids_size; i++) { + buf_flash[i] = malloc(COMPARE_BUFFER_SIZE); + if (!buf_flash[i]) + return -PFIFLASH_ERR_COMPARE; + } + + while (bytes_left) { + if (bytes_left > COMPARE_BUFFER_SIZE) + read_bytes = COMPARE_BUFFER_SIZE; + else + read_bytes = bytes_left; + + rc = fread(buf_pfi, 1, read_bytes, fp_pfi); + if (rc != read_bytes) { + rc = -PFIFLASH_ERR_COMPARE; + goto err; + } + + for (i = 0; i < ids_size; i++) { + rc = fread(buf_flash[i], 1, read_bytes, fp_flash[i]); + if (rc != read_bytes) { + rc = -PFIFLASH_CMP_DIFF; + goto err; + } + + rc = memcmp(buf_pfi, buf_flash[i], read_bytes); + if (rc != 0) { + rc = -PFIFLASH_CMP_DIFF; + goto err; + } + } + + bytes_left -= read_bytes; + } + +err: + for (i = 0; i < ids_size; i++) + free(buf_flash[i]); + + return rc; +} + +static int compare_volumes(int devno, pfi_ubi_t u, FILE *fp_pfi, + pdd_func_t pdd_f, char *err_buf, size_t err_buf_size) +{ + int rc, is_bootenv = 0; + unsigned int i; + char path[PATH_MAX]; + libubi_t ulib = NULL; + FILE *fp_flash[u->ids_size]; + + ulib = libubi_open(); + if (ulib == NULL) { + rc = -PFIFLASH_ERR_UBI_OPEN; + goto err; + } + + for (i = 0; i < u->ids_size; i++) { + if (u->ids[i] == EXAMPLE_BOOTENV_VOL_ID_1 || + u->ids[i] == EXAMPLE_BOOTENV_VOL_ID_2) + is_bootenv = 1; + + snprintf(path, PATH_MAX, DEFAULT_VOL_PATTERN, devno, u->ids[i]); + + fp_flash[i] = fopen(path, "r"); + if (fp_flash[i] == NULL) { + rc = -PFIFLASH_ERR_UBI_OPEN; + goto err; + } + } + + if (is_bootenv) + rc = compare_bootenv(fp_pfi, fp_flash, u->ids_size, + u->data_size, pdd_f, err_buf, err_buf_size); + else + rc = compare_data(fp_pfi, fp_flash, u->ids_size, u->data_size); + +err: + if (rc < 0) + EBUF(PFIFLASH_ERRSTR[-rc]); + + for (i = 0; i < u->ids_size; i++) + fclose(fp_flash[i]); + if (ulib) + libubi_close(ulib); + + return rc; +} + +static int +erase_mtd_region(FILE* file_p, int start, int length) +{ + int rc, fd; + erase_info_t erase; + mtd_info_t mtdinfo; + loff_t offset = start; + loff_t end = offset + length; + + fd = fileno(file_p); + if (fd < 0) + return -PFIFLASH_ERR_MTD_ERASE; + + rc = ioctl(fd, MEMGETINFO, &mtdinfo); + if (rc) + return -PFIFLASH_ERR_MTD_ERASE; + + /* check for bad blocks in case of NAND flash */ + if (mtdinfo.type == MTD_NANDFLASH) { + while (offset < end) { + rc = ioctl(fd, MEMGETBADBLOCK, &offset); + if (rc > 0) { + return -PFIFLASH_ERR_MTD_ERASE; + } + + offset += mtdinfo.erasesize; + } + } + + erase.start = start; + erase.length = length; + + rc = ioctl(fd, MEMERASE, &erase); + if (rc) { + return -PFIFLASH_ERR_MTD_ERASE; + } + + return rc; +} + +/** + * process_raw_volumes - writes the raw sections of the PFI data + * @pfi PFI data file pointer + * @pfi_raws list of PFI raw headers + * @rawdev device to use to write raw data + * + * Error handling: + * when early EOF in PFI data + * - returns -PFIFLASH_ERR_EOF, err_buf matches text to err + * when file I/O error + * - returns -PFIFLASH_ERR_FIO, err_buf matches text to err + * when CRC check fails + * - returns -PFIFLASH_ERR_CRC_CHECK, err_buf matches text to err + * when opening MTD device fails + * - reutrns -PFIFLASH_ERR_MTD_OPEN, err_buf matches text to err + * when closing MTD device fails + * - returns -PFIFLASH_ERR_MTD_CLOSE, err_buf matches text to err + **/ +static int +process_raw_volumes(FILE* pfi, list_t pfi_raws, const char* rawdev, + char* err_buf, size_t err_buf_size) +{ + int rc; + char *pfi_data; + void *i; + uint32_t crc, crc32_table[256]; + size_t j, k; + FILE* mtd = NULL; + list_t ptr; + + if (is_empty(pfi_raws)) + return 0; + + if (rawdev == NULL) + return 0; + + rc = 0; + + pfi_data = NULL; + + log_msg("[ rawupdate dev=%s", rawdev); + + crc = UBI_CRC32_INIT; + init_crc32_table(crc32_table); + + /* most likely only one element in list, but just in case */ + foreach(i, ptr, pfi_raws) { + pfi_raw_t r = (pfi_raw_t)i; + + /* read in pfi data */ + if (pfi_data != NULL) + free(pfi_data); + pfi_data = malloc(r->data_size * sizeof(char)); + for (j = 0; j < r->data_size; j++) { + int c = fgetc(pfi); + if (c == EOF) { + rc = -PFIFLASH_ERR_EOF; + EBUF(PFIFLASH_ERRSTR[-rc]); + goto err; + } else if (ferror(pfi)) { + rc = -PFIFLASH_ERR_FIO; + EBUF(PFIFLASH_ERRSTR[-rc]); + goto err; + } + pfi_data[j] = (char)c; + } + crc = clc_crc32(crc32_table, crc, pfi_data, r->data_size); + + /* check crc */ + if (crc != r->crc) { + rc = -PFIFLASH_ERR_CRC_CHECK; + EBUF(PFIFLASH_ERRSTR[-rc], r->crc, crc); + goto err; + } + + /* open device */ + mtd = fopen(rawdev, "r+"); + if (mtd == NULL) { + rc = -PFIFLASH_ERR_MTD_OPEN; + EBUF(PFIFLASH_ERRSTR[-rc], rawdev); + goto err; + } + + for (j = 0; j < r->starts_size; j++) { + rc = erase_mtd_region(mtd, r->starts[j], r->data_size); + if (rc) { + EBUF(PFIFLASH_ERRSTR[-rc]); + goto err; + } + + fseek(mtd, r->starts[j], SEEK_SET); + for (k = 0; k < r->data_size; k++) { + int c = fputc((int)pfi_data[k], mtd); + if (c == EOF) { + fclose(mtd); + rc = -PFIFLASH_ERR_EOF; + EBUF(PFIFLASH_ERRSTR[-rc]); + goto err; + } + if ((char)c != pfi_data[k]) { + fclose(mtd); + rc = -1; + goto err; + } + } + } + rc = fclose(mtd); + mtd = NULL; + if (rc != 0) { + rc = -PFIFLASH_ERR_MTD_CLOSE; + EBUF(PFIFLASH_ERRSTR[-rc], rawdev); + goto err; + } + } + + err: + if (mtd != NULL) + fclose(mtd); + if (pfi_data != NULL) + free(pfi_data); + return rc; +} + + +/** + * erase_unmapped_ubi_volumes - skip volumes provided by PFI file, clear rest + * @devno UBI device number + * @pfi_ubis list of UBI header data + * + * Error handling: + * when UBI id is out of bounds + * - returns -PFIFLASH_ERR_UBI_VID_OOB, err_buf matches text to err + * when UBI volume can't be removed + * - passes rc, prepends err_buf with contextual aid + **/ +static int +erase_unmapped_ubi_volumes(int devno, list_t pfi_ubis, + char *err_buf, size_t err_buf_size) +{ + int rc; + uint8_t ubi_volumes[PFI_UBI_MAX_VOLUMES]; + size_t i; + list_t ptr; + pfi_ubi_t u; + + rc = 0; + + 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) { + rc = -PFIFLASH_ERR_UBI_VID_OOB; + EBUF(PFIFLASH_ERRSTR[-rc], 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) { + EBUF_PREPEND("remove volume failed"); + goto err; + } + } + } + + err: + return rc; +} + + +/** + * process_ubi_volumes - delegate tasks regarding UBI volumes + * @pfi PFI data file pointer + * @seqnum sequence number + * @pfi_ubis list of UBI header data + * @bootenv_old storage for current system PDD + * @pdd_f function to handle PDD + * @ubi_update_process whether reading or writing + * + * Error handling: + * when and unknown ubi_update_process is given + * - returns -PFIFLASH_ERR_UBI_UNKNOWN, err_buf matches text to err + * otherwise + * - passes rc and err_buf + **/ +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; + pfi_ubi_t u; + list_t ptr; + + rc = 0; + + foreach(u, ptr, pfi_ubis) { + int s = seqnum; + + if (s > ((int)u->ids_size - 1)) + s = 0; /* per default use the first */ + u->curr_seqnum = s; + + switch (ubi_update_process) { + case UBI_REMOVE: + /* TODO are all these "EXAMPLE" vars okay? */ + 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); + /* it's okay if there is no bootenv + * we're going to write one */ + if ((rc == -PFIFLASH_ERR_UBI_VOL_FOPEN) || + (rc == -PFIFLASH_ERR_BOOTENV_READ)) + rc = 0; + 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) { + EBUF_PREPEND("creating volume"); + 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, + u->crc, + err_buf, + err_buf_size); + if (rc != 0) + EBUF_PREPEND("bootenv volume"); + } else { + rc = write_normal_volume(EXAMPLE_UBI_DEVICE, + u->ids[s], + u->data_size, pfi, + u->crc, + err_buf, + err_buf_size); + if (rc != 0) + EBUF_PREPEND("normal volume"); + } + if (rc != 0) + goto err; + + break; + case UBI_COMPARE: + rc = compare_volumes(EXAMPLE_UBI_DEVICE, u, pfi, pdd_f, + err_buf, err_buf_size); + if (rc != 0) { + EBUF_PREPEND("compare volume"); + goto err; + } + + break; + default: + rc = -PFIFLASH_ERR_UBI_UNKNOWN; + EBUF(PFIFLASH_ERRSTR[-rc]); + goto err; + } + } + + err: + return rc; +} + + +/** + * mirror_ubi_volumes - mirror redundant pairs of volumes + * @devno UBI device number + * @pfi_ubis list of PFI header data + * + * Error handling: + * when UBI system couldn't be opened + * - returns -PFIFLASH_ERR_UBI_OPEN, err_buf matches text to err + **/ +static int +mirror_ubi_volumes(uint32_t devno, list_t pfi_ubis, + char *err_buf, size_t err_buf_size) +{ + int rc; + uint32_t j; + list_t ptr; + pfi_ubi_t i; + libubi_t ulib; + + rc = 0; + ulib = NULL; + + log_msg("[ mirror ..."); + + ulib = libubi_open(); + if (ulib == NULL) { + rc = -PFIFLASH_ERR_UBI_OPEN; + EBUF(PFIFLASH_ERRSTR[-rc]); + 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) + libubi_close(ulib); + + return rc; +} + + +/** + * pfiflash_with_options - exposed func to flash memory with a PFI file + * @pfi PFI data file pointer + * @complete flag to erase unmapped volumes + * @seqnum sequence number + * @compare flag to compare + * @pdd_handling method to handle pdd (keep, merge, overwrite...) + * + * Error handling: + * when bootenv can't be created + * - returns -PFIFLASH_ERR_BOOTENV_CREATE, err_buf matches text to err + * when PFI headers can't be read, or + * when fail to skip raw sections, or + * when error occurs while processing raw volumes, or + * when fail to erase unmapped UBI vols, or + * when error occurs while processing UBI volumes, or + * when error occurs while mirroring UBI volumes + * - passes rc, prepends err_buf with contextual aid + **/ +int +pfiflash_with_options(FILE* pfi, int complete, int seqnum, int compare, + pdd_handling_t pdd_handling, const char* rawdev, + char *err_buf, size_t err_buf_size) +{ + int rc; + bootenv_t bootenv; + pdd_func_t pdd_f; + + if (pfi == NULL) + return -EINVAL; + + rc = 0; + pdd_f = NULL; + + /* 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 */ + + rc = bootenv_create(&bootenv); + if (rc != 0) { + rc = -PFIFLASH_ERR_BOOTENV_CREATE; + EBUF(PFIFLASH_ERRSTR[-rc], ""); + goto err; + } + + rc = read_pfi_headers(&pfi_raws, &pfi_ubis, pfi, err_buf, err_buf_size); + if (rc != 0) { + EBUF_PREPEND("reading PFI header"); + goto err; + } + + if (rawdev == NULL || compare) + rc = skip_raw_volumes(pfi, pfi_raws, err_buf, err_buf_size); + else + rc = process_raw_volumes(pfi, pfi_raws, rawdev, err_buf, + err_buf_size); + if (rc != 0) { + EBUF_PREPEND("handling raw section"); + goto err; + } + + if (complete && !compare) { + rc = erase_unmapped_ubi_volumes(EXAMPLE_UBI_DEVICE, pfi_ubis, + err_buf, err_buf_size); + if (rc != 0) { + EBUF_PREPEND("deleting unmapped UBI volumes"); + goto err; + } + } + + if (((int)pdd_handling >= 0) && + (pdd_handling < PDD_HANDLING_NUM)) + pdd_f = pdd_funcs[pdd_handling]; + else { + rc = -PFIFLASH_ERR_PDD_UNKNOWN; + EBUF(PFIFLASH_ERRSTR[-rc]); + goto err; + } + + if (!compare) { + rc = process_ubi_volumes(pfi, curr_seqnum, pfi_ubis, bootenv, + pdd_f, UBI_REMOVE, err_buf, err_buf_size); + if (rc != 0) { + EBUF_PREPEND("removing UBI volumes"); + goto err; + } + + rc = process_ubi_volumes(pfi, curr_seqnum, pfi_ubis, bootenv, + pdd_f, UBI_WRITE, err_buf, err_buf_size); + if (rc != 0) { + EBUF_PREPEND("writing UBI volumes"); + 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) { + EBUF_PREPEND("mirroring UBI volumes"); + goto err; + } + } + } else { + /* only compare volumes, don't alter the content */ + rc = process_ubi_volumes(pfi, curr_seqnum, pfi_ubis, bootenv, + pdd_f, UBI_COMPARE, err_buf, err_buf_size); + + if (rc == -PFIFLASH_CMP_DIFF) + /* update is necessary, return positive value */ + rc = 1; + + if (rc < 0) { + EBUF_PREPEND("comparing UBI volumes"); + 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; +} + + +/** + * pfiflash - passes to pfiflash_with_options + * @pfi PFI data file pointer + * @complete flag to erase unmapped volumes + * @seqnum sequence number + * @pdd_handling method to handle pdd (keep, merge, overwrite...) + **/ +int +pfiflash(FILE* pfi, int complete, int seqnum, pdd_handling_t pdd_handling, + char *err_buf, size_t err_buf_size) +{ + return pfiflash_with_options(pfi, complete, seqnum, 0, pdd_handling, + NULL, err_buf, err_buf_size); +} diff --git a/ubi-utils/src/libubi.c b/ubi-utils/src/libubi.c index b53f18c..a028fc6 100644 --- a/ubi-utils/src/libubi.c +++ b/ubi-utils/src/libubi.c @@ -27,480 +27,13 @@ #include #include #include +#include #include #include #include #include "libubi.h" #include "libubi_int.h" -/** - * mkpath - compose full path from 2 given components. - * @path: the first component - * @name: the second component - * - * This function returns the resulting path in case of success and %NULL in - * case of failure. - */ -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) { - sys_errmsg("cannot allocate %d bytes", len1 + len2 + 2); - return NULL; - } - - memcpy(n, path, len1); - if (n[len1 - 1] != '/') - n[len1++] = '/'; - - memcpy(n + len1, name, len2 + 1); - return n; -} - -/** - * read_positive_ll - read a positive 'long long' value from a file. - * @file: the file to read from - * @value: the result is stored here - * - * This function reads file @file and interprets its contents as a positive - * 'long long' integer. If this is not true, it fails with %EINVAL error code. - * Returns %0 in case of success and %-1 in case of failure. - */ -static int read_positive_ll(const char *file, long long *value) -{ - int fd, rd; - char buf[50]; - - fd = open(file, O_RDONLY); - if (fd == -1) - return -1; - - rd = read(fd, buf, 50); - if (rd == -1) { - sys_errmsg("cannot read \"%s\"", file); - goto out_error; - } - if (rd == 50) { - errmsg("contents of \"%s\" is too long", file); - errno = EINVAL; - goto out_error; - } - - if (sscanf(buf, "%lld\n", value) != 1) { - /* This must be a UBI bug */ - errmsg("cannot read integer from \"%s\"\n", file); - errno = EINVAL; - goto out_error; - } - - if (*value < 0) { - errmsg("negative value %lld in \"%s\"", *value, file); - errno = EINVAL; - goto out_error; - } - - if (close(fd)) - return sys_errmsg("close failed on \"%s\"", file); - - return 0; - -out_error: - close(fd); - return -1; -} - -/** - * read_positive_int - read a positive 'int' value from a file. - * @file: the file to read from - * @value: the result is stored here - * - * This function is the same as 'read_positive_ll()', but it reads an 'int' - * value, not 'long long'. - */ -static int read_positive_int(const char *file, int *value) -{ - long long res; - - if (read_positive_ll(file, &res)) - return -1; - - /* Make sure the value is not too big */ - if (res > INT_MAX) { - errmsg("value %lld read from file \"%s\" is out of range", - res, file); - errno = EINVAL; - return -1; - } - - *value = res; - return 0; -} - -/** - * read_data - read data from a file. - * @file: the file to read from - * @buf: the buffer to read to - * @buf_len: buffer length - * - * This function returns number of read bytes in case of success and %-1 in - * case of failure. Note, if the file contains more then @buf_len bytes of - * date, this function fails with %EINVAL error code. - */ -static int read_data(const char *file, void *buf, int buf_len) -{ - int fd, rd, tmp, tmp1; - - fd = open(file, O_RDONLY); - if (fd == -1) - return -1; - - rd = read(fd, buf, buf_len); - if (rd == -1) { - sys_errmsg("cannot read \"%s\"", file); - goto out_error; - } - - /* Make sure all data is read */ - tmp1 = read(fd, &tmp, 1); - if (tmp1 == 1) { - sys_errmsg("cannot read \"%s\"", file); - goto out_error; - } - if (tmp1) { - errmsg("file \"%s\" contains too much data (> %d bytes)", - file, buf_len); - errno = EINVAL; - goto out_error; - } - - if (close(fd)) { - sys_errmsg("close failed on \"%s\"", file); - return -1; - } - - return rd; - -out_error: - close(fd); - return -1; -} - -/** - * read_major - read major and minor numbers from a file. - * @file: name of the file to read from - * @major: major number is returned here - * @minor: minor number is returned here - * - * This function returns % in case of succes, and %-1 in case of failure. - */ -static int read_major(const char *file, int *major, int *minor) -{ - int ret; - char buf[50]; - - ret = read_data(file, buf, 50); - if (ret < 0) - return ret; - - ret = sscanf(buf, "%d:%d\n", major, minor); - if (ret != 2) { - errno = EINVAL; - return errmsg("\"%s\" does not have major:minor format", file); - } - - if (*major < 0 || *minor < 0) { - errno = EINVAL; - return errmsg("bad major:minor %d:%d in \"%s\"", - *major, *minor, file); - } - - return 0; -} - -/** - * dev_read_int - read a positive 'int' value from an UBI device sysfs file. - * @patt: file pattern to read from - * @dev_num: UBI device number - * @value: the result is stored here - * - * This function returns %0 in case of success and %-1 in case of failure. - */ -static int dev_read_int(const char *patt, int dev_num, int *value) -{ - char file[strlen(patt) + 50]; - - sprintf(file, patt, dev_num); - return read_positive_int(file, value); -} - -/** - * vol_read_int - read a positive 'int' value from an UBI volume sysfs file. - * @patt: file pattern to read from - * @dev_num: UBI device number - * @vol_id: volume ID - * @value: the result is stored here - * - * This function returns %0 in case of success and %-1 in case of failure. - */ -static int vol_read_int(const char *patt, int dev_num, int vol_id, int *value) -{ - char file[strlen(patt) + 100]; - - sprintf(file, patt, dev_num, vol_id); - return read_positive_int(file, value); -} - -/** - * dev_read_ll - read a positive 'long long' value from an UBI device sysfs file. - * @patt: file pattern to read from - * @dev_num: UBI device number - * @value: the result is stored here - * - * This function returns %0 in case of success and %-1 in case of failure. - */ -static int dev_read_ll(const char *patt, int dev_num, long long *value) -{ - char file[strlen(patt) + 50]; - - sprintf(file, patt, dev_num); - return read_positive_ll(file, value); -} - -/** - * vol_read_ll - read a positive 'long long' value from an UBI volume sysfs file. - * @patt: file pattern to read from - * @dev_num: UBI device number - * @vol_id: volume ID - * @value: the result is stored here - * - * This function returns %0 in case of success and %-1 in case of failure. - */ -static int vol_read_ll(const char *patt, int dev_num, int vol_id, - long long *value) -{ - char file[strlen(patt) + 100]; - - sprintf(file, patt, dev_num, vol_id); - return read_positive_ll(file, value); -} - -/** - * vol_read_data - read data from an UBI volume's sysfs file. - * @patt: file pattern to read from - * @dev_num: UBI device number - * @vol_id: volume ID - * @buf: buffer to read to - * @buf_len: buffer length - * - * This function returns number of read bytes in case of success and %-1 in - * case of failure. - */ -static int vol_read_data(const char *patt, int dev_num, int vol_id, void *buf, - int buf_len) -{ - char file[strlen(patt) + 100]; - - sprintf(file, patt, dev_num, vol_id); - return read_data(file, buf, buf_len); -} - -/** - * dev_get_major - get major and minor numbers of an UBI device. - * @lib: libubi descriptor - * @dev_num: UBI device number - * @major: major number is returned here - * @minor: minor number is returned here - * - * This function returns zero in case of succes and %-1 in case of failure. - */ -static int dev_get_major(struct libubi *lib, int dev_num, int *major, int *minor) -{ - char file[strlen(lib->dev_dev) + 50]; - - sprintf(file, lib->dev_dev, dev_num); - return read_major(file, major, minor); -} - -/** - * vol_get_major - get major and minor numbers of an UBI volume. - * @lib: libubi descriptor - * @dev_num: UBI device number - * @vol_id: volume ID - * @major: major number is returned here - * @minor: minor number is returned here - * - * This function returns zero in case of succes and %-1 in case of failure. - */ -static int vol_get_major(struct libubi *lib, int dev_num, int vol_id, - int *major, int *minor) -{ - char file[strlen(lib->vol_dev) + 100]; - - sprintf(file, lib->vol_dev, dev_num, vol_id); - return read_major(file, major, minor); -} - -/** - * vol_node2nums - find UBI device number and volume ID by volume device node - * file. - * @lib: UBI library descriptor - * @node: UBI character device node name - * @dev_num: UBI device number is returned here - * @vol_id: volume ID is returned hers - * - * This function returns zero in case of succes and %-1 in case of failure. - */ -static int vol_node2nums(struct libubi *lib, const char *node, int *dev_num, - int *vol_id) -{ - struct stat st; - struct ubi_info info; - int i, fd, major, minor; - char file[strlen(lib->ubi_vol) + 100]; - - if (lstat(node, &st)) - return -1; - - if (!S_ISCHR(st.st_mode)) { - errno = EINVAL; - return errmsg("\"%s\" is not a character device", node); - } - - major = major(st.st_rdev); - minor = minor(st.st_rdev); - - if (minor == 0) { - errno = EINVAL; - return errmsg("\"%s\" is not a volume character device", node); - } - - if (ubi_get_info((libubi_t *)lib, &info)) - return -1; - - for (i = info.lowest_dev_num; i <= info.highest_dev_num; i++) { - int major1, minor1, ret; - - ret = dev_get_major(lib, i, &major1, &minor1); - if (ret) { - if (errno == ENOENT) - continue; - return -1; - } - - if (major1 == major) - break; - } - - if (i > info.highest_dev_num) { - errno = ENODEV; - return -1; - } - - /* Make sure this UBI volume exists */ - sprintf(file, lib->ubi_vol, i, minor - 1); - fd = open(file, O_RDONLY); - if (fd == -1) { - errno = ENODEV; - return -1; - } - - *dev_num = i; - *vol_id = minor - 1; - errno = 0; - return 0; -} - -/** - * dev_node2num - find UBI device number by its character device node. - * @lib: UBI library descriptor - * @node: UBI character device node name - * - * This function returns positive UBI device number in case of success and %-1 - * in case of failure. - */ -static int dev_node2num(struct libubi *lib, const char *node, int *dev_num) -{ - struct stat stat; - struct ubi_info info; - int i, major, minor; - - if (lstat(node, &stat)) - return -1; - - if (!S_ISCHR(stat.st_mode)) { - errno = EINVAL; - return errmsg("\"%s\" is not a character device", node); - } - - major = major(stat.st_rdev); - minor = minor(stat.st_rdev); - - if (minor != 0) { - errno = EINVAL; - return errmsg("\"%s\" is not an UBI character device", node); - } - - if (ubi_get_info((libubi_t *)lib, &info)) - return -1; - - for (i = info.lowest_dev_num; i <= info.highest_dev_num; i++) { - int major1, minor1, ret; - - ret = dev_get_major(lib, i, &major1, &minor1); - if (ret) { - if (errno == ENOENT) - continue; - return -1; - } - - if (major1 == major) { - if (minor1 != 0) { - errmsg("UBI character device minor number is " - "%d, but must be 0", minor1); - errno = EINVAL; - return -1; - } - errno = 0; - *dev_num = i; - return 0; - } - } - - errno = ENODEV; - return -1; -} - -static int mtd_num2ubi_dev(struct libubi *lib, int mtd_num, int *dev_num) -{ - struct ubi_info info; - int i, ret, mtd_num1; - - if (ubi_get_info((libubi_t *)lib, &info)) - return -1; - - for (i = info.lowest_dev_num; i <= info.highest_dev_num; i++) { - ret = dev_read_int(lib->dev_mtd_num, i, &mtd_num1); - if (ret) { - if (errno == ENOENT) - continue; - return -1; - } - - if (mtd_num1 == mtd_num) { - errno = 0; - *dev_num = i; - return 0; - } - } - - errno = ENODEV; - return -1; -} - libubi_t libubi_open(void) { int fd, version; @@ -513,130 +46,132 @@ libubi_t libubi_open(void) /* TODO: this must be discovered instead */ lib->sysfs = strdup("/sys"); if (!lib->sysfs) - goto out_error; - - lib->sysfs_ctrl = mkpath(lib->sysfs, SYSFS_CTRL); - if (!lib->sysfs_ctrl) - goto out_error; - - lib->ctrl_dev = mkpath(lib->sysfs_ctrl, CTRL_DEV); - if (!lib->ctrl_dev) - goto out_error; + goto error; lib->sysfs_ubi = mkpath(lib->sysfs, SYSFS_UBI); if (!lib->sysfs_ubi) - goto out_error; + goto error; /* Make sure UBI is present */ fd = open(lib->sysfs_ubi, O_RDONLY); - if (fd == -1) { - errmsg("cannot open \"%s\", UBI does not seem to exist in system", - lib->sysfs_ubi); - goto out_error; - } - - if (close(fd)) { - sys_errmsg("close failed on \"%s\"", lib->sysfs_ubi); - goto out_error; - } + if (fd == -1) + goto error; + close(fd); lib->ubi_dev = mkpath(lib->sysfs_ubi, UBI_DEV_NAME_PATT); if (!lib->ubi_dev) - goto out_error; + goto error; lib->ubi_version = mkpath(lib->sysfs_ubi, UBI_VER); if (!lib->ubi_version) - goto out_error; + goto error; lib->dev_dev = mkpath(lib->ubi_dev, DEV_DEV); if (!lib->dev_dev) - goto out_error; + goto error; lib->dev_avail_ebs = mkpath(lib->ubi_dev, DEV_AVAIL_EBS); if (!lib->dev_avail_ebs) - goto out_error; + goto error; lib->dev_total_ebs = mkpath(lib->ubi_dev, DEV_TOTAL_EBS); if (!lib->dev_total_ebs) - goto out_error; + goto error; lib->dev_bad_count = mkpath(lib->ubi_dev, DEV_BAD_COUNT); if (!lib->dev_bad_count) - goto out_error; + goto error; lib->dev_eb_size = mkpath(lib->ubi_dev, DEV_EB_SIZE); if (!lib->dev_eb_size) - goto out_error; + goto error; lib->dev_max_ec = mkpath(lib->ubi_dev, DEV_MAX_EC); if (!lib->dev_max_ec) - goto out_error; + goto error; lib->dev_bad_rsvd = mkpath(lib->ubi_dev, DEV_MAX_RSVD); if (!lib->dev_bad_rsvd) - goto out_error; + goto error; lib->dev_max_vols = mkpath(lib->ubi_dev, DEV_MAX_VOLS); if (!lib->dev_max_vols) - goto out_error; + goto error; lib->dev_min_io_size = mkpath(lib->ubi_dev, DEV_MIN_IO_SIZE); if (!lib->dev_min_io_size) - goto out_error; - - lib->dev_mtd_num = mkpath(lib->ubi_dev, DEV_MTD_NUM); - if (!lib->dev_mtd_num) - goto out_error; + goto error; lib->ubi_vol = mkpath(lib->sysfs_ubi, UBI_VOL_NAME_PATT); if (!lib->ubi_vol) - goto out_error; + goto error; lib->vol_type = mkpath(lib->ubi_vol, VOL_TYPE); if (!lib->vol_type) - goto out_error; + goto error; lib->vol_dev = mkpath(lib->ubi_vol, VOL_DEV); if (!lib->vol_dev) - goto out_error; + goto error; lib->vol_alignment = mkpath(lib->ubi_vol, VOL_ALIGNMENT); if (!lib->vol_alignment) - goto out_error; + goto error; lib->vol_data_bytes = mkpath(lib->ubi_vol, VOL_DATA_BYTES); if (!lib->vol_data_bytes) - goto out_error; + goto error; lib->vol_rsvd_ebs = mkpath(lib->ubi_vol, VOL_RSVD_EBS); if (!lib->vol_rsvd_ebs) - goto out_error; + goto error; lib->vol_eb_size = mkpath(lib->ubi_vol, VOL_EB_SIZE); if (!lib->vol_eb_size) - goto out_error; + goto error; lib->vol_corrupted = mkpath(lib->ubi_vol, VOL_CORRUPTED); if (!lib->vol_corrupted) - goto out_error; + goto error; lib->vol_name = mkpath(lib->ubi_vol, VOL_NAME); if (!lib->vol_name) - goto out_error; + goto error; - if (read_positive_int(lib->ubi_version, &version)) - goto out_error; + if (read_int(lib->ubi_version, &version)) + goto error; if (version != LIBUBI_UBI_VERSION) { fprintf(stderr, "LIBUBI: this library was made for UBI version " "%d, but UBI version %d is detected\n", LIBUBI_UBI_VERSION, version); - goto out_error; + goto error; } return lib; -out_error: - libubi_close((libubi_t)lib); +error: + free(lib->vol_corrupted); + free(lib->vol_eb_size); + free(lib->vol_rsvd_ebs); + free(lib->vol_data_bytes); + free(lib->vol_alignment); + free(lib->vol_dev); + free(lib->vol_type); + free(lib->ubi_vol); + free(lib->dev_min_io_size); + free(lib->dev_max_vols); + free(lib->dev_bad_rsvd); + free(lib->dev_max_ec); + free(lib->dev_eb_size); + free(lib->dev_bad_count); + free(lib->dev_total_ebs); + free(lib->dev_avail_ebs); + free(lib->dev_dev); + free(lib->ubi_version); + free(lib->ubi_dev); + free(lib->sysfs_ubi); + free(lib->sysfs); + free(lib); return NULL; } @@ -653,7 +188,6 @@ void libubi_close(libubi_t desc) free(lib->vol_dev); free(lib->vol_type); free(lib->ubi_vol); - free(lib->dev_mtd_num); free(lib->dev_min_io_size); free(lib->dev_max_vols); free(lib->dev_bad_rsvd); @@ -666,478 +200,716 @@ void libubi_close(libubi_t desc) free(lib->ubi_version); free(lib->ubi_dev); free(lib->sysfs_ubi); - free(lib->ctrl_dev); - free(lib->sysfs_ctrl); free(lib->sysfs); free(lib); } -int ubi_attach_mtd(libubi_t desc, const char *node, - struct ubi_attach_request *req) +int ubi_get_info(libubi_t desc, struct ubi_info *info) +{ + DIR *sysfs_ubi; + struct dirent *dirent; + struct libubi *lib = (struct libubi *)desc; + + memset(info, '\0', sizeof(struct ubi_info)); + + /* + * We have to scan the UBI sysfs directory to identify how many UBI + * devices are present. + */ + sysfs_ubi = opendir(lib->sysfs_ubi); + if (!sysfs_ubi) + return -1; + + info->lowest_dev_num = INT_MAX; + while ((dirent = readdir(sysfs_ubi))) { + char *name = &dirent->d_name[0]; + int dev_num, ret; + + ret = sscanf(name, UBI_DEV_NAME_PATT, &dev_num); + if (ret == 1) { + info->dev_count += 1; + if (dev_num > info->highest_dev_num) + info->highest_dev_num = dev_num; + if (dev_num < info->lowest_dev_num) + info->lowest_dev_num = dev_num; + } + } + + if (info->lowest_dev_num == INT_MAX) + info->lowest_dev_num = 0; + + if (read_int(lib->ubi_version, &info->version)) + goto close; + + return closedir(sysfs_ubi); + +close: + closedir(sysfs_ubi); + return -1; +} + +int ubi_mkvol(libubi_t desc, const char *node, struct ubi_mkvol_request *req) +{ + int fd, ret; + struct ubi_mkvol_req r; + size_t n; + + desc = desc; + r.vol_id = req->vol_id; + r.alignment = req->alignment; + r.bytes = req->bytes; + r.vol_type = req->vol_type; + + n = strlen(req->name); + if (n > UBI_MAX_VOLUME_NAME) + return -1; + + strncpy(r.name, req->name, UBI_MAX_VOLUME_NAME + 1); + r.name_len = n; + + fd = open(node, O_RDONLY); + if (fd == -1) + return -1; + + ret = ioctl(fd, UBI_IOCMKVOL, &r); + if (!ret) + req->vol_id = r.vol_id; + + close(fd); + return ret; +} + +int ubi_rmvol(libubi_t desc, const char *node, int vol_id) { int fd, ret; - struct ubi_attach_req r; - - memset(&r, sizeof(struct ubi_attach_req), '\0'); desc = desc; - r.ubi_num = req->dev_num; - r.mtd_num = req->mtd_num; - r.vid_hdr_offset = req->vid_hdr_offset; - fd = open(node, O_RDONLY); if (fd == -1) return -1; - ret = ioctl(fd, UBI_IOCATT, &r); + ret = ioctl(fd, UBI_IOCRMVOL, &vol_id); close(fd); - if (ret == -1) - return -1; + return ret; +} - req->dev_num = r.ubi_num; +int ubi_rsvol(libubi_t desc, const char *node, int vol_id, long long bytes) +{ + int fd, ret; + struct ubi_rsvol_req req; -#ifdef UDEV_SETTLE_HACK - if (system("udevsettle") == -1) - return -1; - if (system("udevsettle") == -1) + desc = desc; + fd = open(node, O_RDONLY); + if (fd == -1) return -1; -#endif + req.bytes = bytes; + req.vol_id = vol_id; + + ret = ioctl(fd, UBI_IOCRSVOL, &req); + close(fd); return ret; } -int ubi_detach_mtd(libubi_t desc, const char *node, int mtd_num) +int ubi_update_start(libubi_t desc, int fd, long long bytes) +{ + desc = desc; + if (ioctl(fd, UBI_IOCVOLUP, &bytes)) + return -1; + return 0; +} + +int ubi_get_dev_info(libubi_t desc, const char *node, struct ubi_dev_info *info) { - int ret, ubi_dev; + int dev_num; + struct libubi *lib = (struct libubi *)desc; - ret = mtd_num2ubi_dev(desc, mtd_num, &ubi_dev); - if (ret == -1) - return ret; + dev_num = find_dev_num(lib, node); + if (dev_num == -1) + return -1; - return ubi_remove_dev(desc, node, ubi_dev); + return ubi_get_dev_info1(desc, dev_num, info); } -int ubi_remove_dev(libubi_t desc, const char *node, int ubi_dev) +int ubi_get_dev_info1(libubi_t desc, int dev_num, struct ubi_dev_info *info) { - int fd, ret; + DIR *sysfs_ubi; + struct dirent *dirent; + struct libubi *lib = (struct libubi *)desc; - desc = desc; + memset(info, '\0', sizeof(struct ubi_dev_info)); + info->dev_num = dev_num; - fd = open(node, O_RDONLY); - if (fd == -1) + sysfs_ubi = opendir(lib->sysfs_ubi); + if (!sysfs_ubi) return -1; - ret = ioctl(fd, UBI_IOCDET, &ubi_dev); - if (ret == -1) - goto out_close; -#ifdef UDEV_SETTLE_HACK - if (system("udevsettle") == -1) + info->lowest_vol_num = INT_MAX; + while ((dirent = readdir(sysfs_ubi))) { + char *name = &dirent->d_name[0]; + int vol_id, ret, devno; + + ret = sscanf(name, UBI_VOL_NAME_PATT, &devno, &vol_id); + if (ret == 2 && devno == dev_num) { + info->vol_count += 1; + if (vol_id > info->highest_vol_num) + info->highest_vol_num = vol_id; + if (vol_id < info->lowest_vol_num) + info->lowest_vol_num = vol_id; + } + } + + closedir(sysfs_ubi); + + if (info->lowest_vol_num == INT_MAX) + info->lowest_vol_num = 0; + + if (dev_read_int(lib->dev_avail_ebs, dev_num, &info->avail_ebs)) + return -1; + if (dev_read_int(lib->dev_total_ebs, dev_num, &info->total_ebs)) + return -1; + if (dev_read_int(lib->dev_bad_count, dev_num, &info->bad_count)) + return -1; + if (dev_read_int(lib->dev_eb_size, dev_num, &info->eb_size)) + return -1; + if (dev_read_int(lib->dev_bad_rsvd, dev_num, &info->bad_rsvd)) + return -1; + if (dev_read_ll(lib->dev_max_ec, dev_num, &info->max_ec)) + return -1; + if (dev_read_int(lib->dev_max_vols, dev_num, &info->max_vol_count)) + return -1; + if (dev_read_int(lib->dev_min_io_size, dev_num, &info->min_io_size)) return -1; -#endif -out_close: - close(fd); - return ret; + info->avail_bytes = info->avail_ebs * info->eb_size; + info->total_bytes = info->total_ebs * info->eb_size; + + return 0; } -int ubi_node_type(libubi_t desc, const char *node) +int ubi_get_vol_info(libubi_t desc, const char *node, struct ubi_vol_info *info) { - struct stat st; - struct ubi_info info; - int i, fd, major, minor; + int vol_id, dev_num; struct libubi *lib = (struct libubi *)desc; - char file[strlen(lib->ubi_vol) + 100]; - if (lstat(node, &st)) + dev_num = find_dev_num_vol(lib, node); + if (dev_num == -1) return -1; - if (!S_ISCHR(st.st_mode)) { - errno = EINVAL; + vol_id = find_vol_num(lib, dev_num, node); + if (vol_id == -1) return -1; - } - - major = major(st.st_rdev); - minor = minor(st.st_rdev); - if (ubi_get_info((libubi_t *)lib, &info)) - return -1; + return ubi_get_vol_info1(desc, dev_num, vol_id, info); +} - for (i = info.lowest_dev_num; i <= info.highest_dev_num; i++) { - int major1, minor1, ret; +int ubi_get_vol_info1(libubi_t desc, int dev_num, int vol_id, + struct ubi_vol_info *info) +{ + int ret; + struct libubi *lib = (struct libubi *)desc; + char buf[50]; - ret = dev_get_major(lib, i, &major1, &minor1); - if (ret) { - if (errno == ENOENT) - continue; - return -1; - } + memset(info, '\0', sizeof(struct ubi_vol_info)); + info->dev_num = dev_num; + info->vol_id = vol_id; - if (major1 == major) - break; - } + ret = vol_read_data(lib->vol_type, dev_num, vol_id, &buf[0], 50); + if (ret < 0) + return -1; - if (i > info.highest_dev_num) { - /* - * The character device node does not correspond to any - * existing UBI device or volume, but we do not want to return - * any error number in this case, to indicate the fact that it - * could be a UBI device/volume, but it doesn't. - */ - errno = 0; + if (strncmp(&buf[0], "static\n", ret) == 0) + info->type = UBI_STATIC_VOLUME; + else if (strncmp(&buf[0], "dynamic\n", ret) == 0) + info->type = UBI_DYNAMIC_VOLUME; + else { + fprintf(stderr, "LIBUBI: bad value at sysfs file\n"); + errno = EINVAL; return -1; } - if (minor == 0) - return 1; + ret = vol_read_int(lib->vol_alignment, dev_num, vol_id, + &info->alignment); + if (ret) + return -1; + ret = vol_read_ll(lib->vol_data_bytes, dev_num, vol_id, + &info->data_bytes); + if (ret) + return -1; + ret = vol_read_int(lib->vol_rsvd_ebs, dev_num, vol_id, &info->rsvd_ebs); + if (ret) + return -1; + ret = vol_read_int(lib->vol_eb_size, dev_num, vol_id, &info->eb_size); + if (ret) + return -1; + ret = vol_read_int(lib->vol_corrupted, dev_num, vol_id, + &info->corrupted); + if (ret) + return -1; + info->rsvd_bytes = info->eb_size * info->rsvd_ebs; - /* This is supposdely an UBI volume device node */ - sprintf(file, lib->ubi_vol, i, minor - 1); - fd = open(file, O_RDONLY); - if (fd == -1) { - errno = 0; + ret = vol_read_data(lib->vol_name, dev_num, vol_id, &info->name, + UBI_VOL_NAME_MAX + 2); + if (ret < 0) return -1; - } - return 2; + info->name[ret - 1] = '\0'; + return 0; } -int ubi_get_info(libubi_t desc, struct ubi_info *info) +/** + * read_int - read an 'int' value from a file. + * + * @file the file to read from + * @value the result is stored here + * + * This function returns %0 in case of success and %-1 in case of failure. + */ +static int read_int(const char *file, int *value) { - DIR *sysfs_ubi; - struct dirent *dirent; - struct libubi *lib = (struct libubi *)desc; + int fd, rd; + char buf[50]; - memset(info, '\0', sizeof(struct ubi_info)); + fd = open(file, O_RDONLY); + if (fd == -1) + return -1; + + rd = read(fd, &buf[0], 50); + if (rd == -1) + goto error; - if (read_major(lib->ctrl_dev, &info->ctrl_major, &info->ctrl_minor)) { - /* - * Older UBI versions did not have control device, so we do not - * panic here for compatibility reasons. May be few years later - * we could return -1 here, but for now just set major:minor to - * -1. - */ - info->ctrl_major = info->ctrl_minor = -1; + if (sscanf(&buf[0], "%d\n", value) != 1) { + /* This must be a UBI bug */ + fprintf(stderr, "LIBUBI: bad value at sysfs file\n"); + errno = EINVAL; + goto error; } - /* - * We have to scan the UBI sysfs directory to identify how many UBI - * devices are present. - */ - sysfs_ubi = opendir(lib->sysfs_ubi); - if (!sysfs_ubi) - return sys_errmsg("cannot open %s", lib->sysfs_ubi); + close(fd); + return 0; - info->lowest_dev_num = INT_MAX; - while (1) { - int dev_num, ret; - char tmp_buf[256]; +error: + close(fd); + return -1; +} - errno = 0; - dirent = readdir(sysfs_ubi); - if (!dirent) - break; +/** + * dev_read_int - read an 'int' value from an UBI device's sysfs file. + * + * @patt the file pattern to read from + * @dev_num UBI device number + * @value the result is stored here + * + * This function returns %0 in case of success and %-1 in case of failure. + */ +static int dev_read_int(const char *patt, int dev_num, int *value) +{ + int fd, rd; + char buf[50]; + char file[strlen(patt) + 50]; - if (strlen(dirent->d_name) > 256) { - errmsg("invalid entry in %s: \"%s\"", - lib->sysfs_ubi, dirent->d_name); - goto out_close; - } + sprintf(&file[0], patt, dev_num); + fd = open(&file[0], O_RDONLY); + if (fd == -1) + return -1; - ret = sscanf(dirent->d_name, UBI_DEV_NAME_PATT"%s", - &dev_num, tmp_buf); - if (ret == 1) { - info->dev_count += 1; - if (dev_num > info->highest_dev_num) - info->highest_dev_num = dev_num; - if (dev_num < info->lowest_dev_num) - info->lowest_dev_num = dev_num; - } - } + rd = read(fd, &buf[0], 50); + if (rd == -1) + goto error; - if (!dirent && errno) { - sys_errmsg("readdir failed on \"%s\"", lib->sysfs_ubi); - goto out_close; + if (sscanf(&buf[0], "%d\n", value) != 1) { + /* This must be a UBI bug */ + fprintf(stderr, "LIBUBI: bad value at sysfs file\n"); + errno = EINVAL; + goto error; } - if (closedir(sysfs_ubi)) - return sys_errmsg("closedir failed on \"%s\"", lib->sysfs_ubi); + close(fd); + return 0; - if (info->lowest_dev_num == INT_MAX) - info->lowest_dev_num = 0; +error: + close(fd); + return -1; +} + +/** + * dev_read_ll - read a 'long long' value from an UBI device's sysfs file. + * + * @patt the file pattern to read from + * @dev_num UBI device number + * @value the result is stored here + * + * This function returns %0 in case of success and %-1 in case of failure. + */ +static int dev_read_ll(const char *patt, int dev_num, long long *value) +{ + int fd, rd; + char buf[50]; + char file[strlen(patt) + 50]; - if (read_positive_int(lib->ubi_version, &info->version)) + sprintf(&file[0], patt, dev_num); + fd = open(&file[0], O_RDONLY); + if (fd == -1) return -1; + rd = read(fd, &buf[0], 50); + if (rd == -1) + goto error; + + if (sscanf(&buf[0], "%lld\n", value) != 1) { + /* This must be a UBI bug */ + fprintf(stderr, "LIBUBI: bad value at sysfs file\n"); + errno = EINVAL; + goto error; + } + + close(fd); return 0; -out_close: - closedir(sysfs_ubi); +error: + close(fd); return -1; } -int ubi_mkvol(libubi_t desc, const char *node, struct ubi_mkvol_request *req) +/** + * dev_read_data - read data from an UBI device's sysfs file. + * + * @patt the file pattern to read from + * @dev_num UBI device number + * @buf buffer to read data to + * @buf_len buffer length + * + * This function returns number of read bytes in case of success and %-1 in + * case of failure. + */ +static int dev_read_data(const char *patt, int dev_num, void *buf, int buf_len) { - int fd, ret; - struct ubi_mkvol_req r; - size_t n; - - memset(&r, sizeof(struct ubi_mkvol_req), '\0'); + int fd, rd; + char file[strlen(patt) + 50]; - desc = desc; - r.vol_id = req->vol_id; - r.alignment = req->alignment; - r.bytes = req->bytes; - r.vol_type = req->vol_type; + sprintf(&file[0], patt, dev_num); + fd = open(&file[0], O_RDONLY); + if (fd == -1) + return -1; - n = strlen(req->name); - if (n > UBI_MAX_VOLUME_NAME) + rd = read(fd, buf, buf_len); + if (rd == -1) { + close(fd); return -1; + } - strncpy(r.name, req->name, UBI_MAX_VOLUME_NAME + 1); - r.name_len = n; + close(fd); + return rd; +} + +/** + * vol_read_int - read an 'int' value from an UBI volume's sysfs file. + * + * @patt the file pattern to read from + * @dev_num UBI device number + * @vol_id volume identifier + * @value the result is stored here + * + * This function returns %0 in case of success and %-1 in case of failure. + */ +static int vol_read_int(const char *patt, int dev_num, int vol_id, int *value) +{ + int fd, rd; + char buf[50]; + char file[strlen(patt) + 100]; - fd = open(node, O_RDONLY); + sprintf(&file[0], patt, dev_num, vol_id); + fd = open(&file[0], O_RDONLY); if (fd == -1) return -1; - ret = ioctl(fd, UBI_IOCMKVOL, &r); - if (ret == -1) - goto out_close; + rd = read(fd, &buf[0], 50); + if (rd == -1) + goto error; - req->vol_id = r.vol_id; + if (sscanf(&buf[0], "%d\n", value) != 1) { + /* This must be a UBI bug */ + fprintf(stderr, "LIBUBI: bad value at sysfs file\n"); + errno = EINVAL; + goto error; + } -#ifdef UDEV_SETTLE_HACK - if (system("udevsettle") == -1) - return -1; -#endif + close(fd); + return 0; -out_close: +error: close(fd); - return ret; + return -1; } -int ubi_rmvol(libubi_t desc, const char *node, int vol_id) +/** + * vol_read_ll - read a 'long long' value from an UBI volume's sysfs file. + * + * @patt the file pattern to read from + * @dev_num UBI device number + * @vol_id volume identifier + * @value the result is stored here + * + * This function returns %0 in case of success and %-1 in case of failure. + */ +static int vol_read_ll(const char *patt, int dev_num, int vol_id, + long long *value) { - int fd, ret; + int fd, rd; + char buf[50]; + char file[strlen(patt) + 100]; - desc = desc; - fd = open(node, O_RDONLY); + sprintf(&file[0], patt, dev_num, vol_id); + fd = open(&file[0], O_RDONLY); if (fd == -1) return -1; - ret = ioctl(fd, UBI_IOCRMVOL, &vol_id); - if (ret == -1) - goto out_close; + rd = read(fd, &buf[0], 50); + if (rd == -1) + goto error; -#ifdef UDEV_SETTLE_HACK - if (system("udevsettle") == -1) - return -1; -#endif + if (sscanf(&buf[0], "%lld\n", value) != 1) { + /* This must be a UBI bug */ + fprintf(stderr, "LIBUBI: bad value at sysfs file\n"); + errno = EINVAL; + goto error; + } -out_close: close(fd); - return ret; + return 0; + +error: + close(fd); + return -1; } -int ubi_rsvol(libubi_t desc, const char *node, int vol_id, long long bytes) +/** + * vol_read_data - read data from an UBI volume's sysfs file. + * + * @patt the file pattern to read from + * @dev_num UBI device number + * @vol_id volume identifier + * @buf buffer to read to + * @buf_len buffer length + * + * This function returns number of read bytes in case of success and %-1 in + * case of failure. + */ +static int vol_read_data(const char *patt, int dev_num, int vol_id, void *buf, + int buf_len) { - int fd, ret; - struct ubi_rsvol_req req; + int fd, rd; + char file[strlen(patt) + 100]; - desc = desc; - fd = open(node, O_RDONLY); + sprintf(&file[0], patt, dev_num, vol_id); + fd = open(&file[0], O_RDONLY); if (fd == -1) return -1; - req.bytes = bytes; - req.vol_id = vol_id; + rd = read(fd, buf, buf_len); + if (rd == -1) { + close(fd); + return -1; + } - ret = ioctl(fd, UBI_IOCRSVOL, &req); close(fd); - return ret; + return rd; } -int ubi_update_start(libubi_t desc, int fd, long long bytes) +/** + * mkpath - compose full path from 2 given components. + * + * @path first component + * @name second component + * + * This function returns the resulting path in case of success and %NULL in + * case of failure. + */ +static char *mkpath(const char *path, const char *name) { - desc = desc; - if (ioctl(fd, UBI_IOCVOLUP, &bytes)) - return -1; - return 0; -} + char *n; + int len1 = strlen(path); + int len2 = strlen(name); -int ubi_leb_change_start(libubi_t desc, int fd, int lnum, int bytes, int dtype) -{ - struct ubi_leb_change_req req; + n = malloc(len1 + len2 + 2); + if (!n) + return NULL; - desc = desc; - memset(&req, 0, sizeof(struct ubi_leb_change_req)); - req.lnum = lnum; - req.bytes = bytes; - req.dtype = dtype; + memcpy(n, path, len1); + if (n[len1 - 1] != '/') + n[len1++] = '/'; - if (ioctl(fd, UBI_IOCEBCH, &req)) - return -1; - return 0; + memcpy(n + len1, name, len2 + 1); + return n; } -int ubi_get_dev_info1(libubi_t desc, int dev_num, struct ubi_dev_info *info) +/** + * find_dev_num - find UBI device number by its character device node. + * + * @lib UBI library descriptor + * @node UBI character device node name + * + * This function returns positive UBI device number in case of success and %-1 + * in case of failure. + */ +static int find_dev_num(struct libubi *lib, const char *node) { - DIR *sysfs_ubi; - struct dirent *dirent; - struct libubi *lib = (struct libubi *)desc; + struct stat stat; + struct ubi_info info; + int i, major, minor; - memset(info, '\0', sizeof(struct ubi_dev_info)); - info->dev_num = dev_num; + if (lstat(node, &stat)) + return -1; - sysfs_ubi = opendir(lib->sysfs_ubi); - if (!sysfs_ubi) + if (!S_ISCHR(stat.st_mode)) { + errno = EINVAL; return -1; + } - info->lowest_vol_num = INT_MAX; + major = major(stat.st_rdev); + minor = minor(stat.st_rdev); - while (1) { - int vol_id, ret, devno; - char tmp_buf[256]; + if (minor != 0) { + errno = -EINVAL; + return -1; + } - errno = 0; - dirent = readdir(sysfs_ubi); - if (!dirent) - break; + if (ubi_get_info((libubi_t *)lib, &info)) + return -1; - if (strlen(dirent->d_name) > 256) { - errmsg("invalid entry in %s: \"%s\"", - lib->sysfs_ubi, dirent->d_name); - goto out_close; - } + for (i = info.lowest_dev_num; i <= info.highest_dev_num; i++) { + int major1, minor1, ret; + char buf[50]; - ret = sscanf(dirent->d_name, UBI_VOL_NAME_PATT"%s", &devno, &vol_id, tmp_buf); - if (ret == 2 && devno == dev_num) { - info->vol_count += 1; - if (vol_id > info->highest_vol_num) - info->highest_vol_num = vol_id; - if (vol_id < info->lowest_vol_num) - info->lowest_vol_num = vol_id; + ret = dev_read_data(lib->dev_dev, i, &buf[0], 50); + if (ret < 0) + return -1; + + ret = sscanf(&buf[0], "%d:%d\n", &major1, &minor1); + if (ret != 2) { + fprintf(stderr, "LIBUBI: bad value at sysfs file\n"); + errno = EINVAL; + return -1; } - } - if (!dirent && errno) { - sys_errmsg("readdir failed on \"%s\"", lib->sysfs_ubi); - goto out_close; + if (minor1 == minor && major1 == major) + return i; } - if (closedir(sysfs_ubi)) - return sys_errmsg("closedir failed on \"%s\"", lib->sysfs_ubi); + errno = ENOENT; + return -1; +} - if (info->lowest_vol_num == INT_MAX) - info->lowest_vol_num = 0; +/** + * find_dev_num_vol - find UBI device number by volume character device node. + * + * @lib UBI library descriptor + * @node UBI character device node name + * + * This function returns positive UBI device number in case of success and %-1 + * in case of failure. + */ +static int find_dev_num_vol(struct libubi *lib, const char *node) +{ + struct stat stat; + struct ubi_info info; + int i, major; - if (dev_get_major(lib, dev_num, &info->major, &info->minor)) + if (lstat(node, &stat)) return -1; - if (dev_read_int(lib->dev_avail_ebs, dev_num, &info->avail_lebs)) - return -1; - if (dev_read_int(lib->dev_total_ebs, dev_num, &info->total_lebs)) - return -1; - if (dev_read_int(lib->dev_bad_count, dev_num, &info->bad_count)) - return -1; - if (dev_read_int(lib->dev_eb_size, dev_num, &info->leb_size)) - return -1; - if (dev_read_int(lib->dev_bad_rsvd, dev_num, &info->bad_rsvd)) - return -1; - if (dev_read_ll(lib->dev_max_ec, dev_num, &info->max_ec)) - return -1; - if (dev_read_int(lib->dev_max_vols, dev_num, &info->max_vol_count)) + if (!S_ISCHR(stat.st_mode)) { + errno = EINVAL; return -1; - if (dev_read_int(lib->dev_min_io_size, dev_num, &info->min_io_size)) + } + + major = major(stat.st_rdev); + + if (minor(stat.st_rdev) == 0) { + errno = -EINVAL; return -1; + } - info->avail_bytes = info->avail_lebs * info->leb_size; - info->total_bytes = info->total_lebs * info->leb_size; + if (ubi_get_info((libubi_t *)lib, &info)) + return -1; - return 0; + for (i = info.lowest_dev_num; i <= info.highest_dev_num; i++) { + int major1, minor1, ret; + char buf[50]; -out_close: - closedir(sysfs_ubi); - return -1; -} + ret = dev_read_data(lib->dev_dev, i, &buf[0], 50); + if (ret < 0) + return -1; -int ubi_get_dev_info(libubi_t desc, const char *node, struct ubi_dev_info *info) -{ - int dev_num; - struct libubi *lib = (struct libubi *)desc; + ret = sscanf(&buf[0], "%d:%d\n", &major1, &minor1); + if (ret != 2) { + fprintf(stderr, "LIBUBI: bad value at sysfs file\n"); + errno = EINVAL; + return -1; + } - if (dev_node2num(lib, node, &dev_num)) - return -1; + if (major1 == major) + return i; + } - return ubi_get_dev_info1(desc, dev_num, info); + errno = ENOENT; + return -1; } -int ubi_get_vol_info1(libubi_t desc, int dev_num, int vol_id, - struct ubi_vol_info *info) +/** + * find_vol_num - find UBI volume number by its character device node. + * + * @lib UBI library descriptor + * @dev_num UBI device number + * @node UBI volume character device node name + * + * This function returns positive UBI volume number in case of success and %-1 + * in case of failure. + */ +static int find_vol_num(struct libubi *lib, int dev_num, const char *node) { - int ret; - struct libubi *lib = (struct libubi *)desc; - char buf[50]; - - memset(info, '\0', sizeof(struct ubi_vol_info)); - info->dev_num = dev_num; - info->vol_id = vol_id; - - if (dev_get_major(lib, dev_num, &info->dev_major, &info->dev_minor)) - return -1; - if (vol_get_major(lib, dev_num, vol_id, &info->major, &info->minor)) - return -1; + struct stat stat; + struct ubi_dev_info info; + int i, major, minor; - ret = vol_read_data(lib->vol_type, dev_num, vol_id, buf, 50); - if (ret < 0) + if (lstat(node, &stat)) return -1; - if (strncmp(buf, "static\n", ret) == 0) - info->type = UBI_STATIC_VOLUME; - else if (strncmp(buf, "dynamic\n", ret) == 0) - info->type = UBI_DYNAMIC_VOLUME; - else { - errmsg("bad value at \"%s\"", buf); + if (!S_ISCHR(stat.st_mode)) { errno = EINVAL; return -1; } - ret = vol_read_int(lib->vol_alignment, dev_num, vol_id, - &info->alignment); - if (ret) - return -1; - ret = vol_read_ll(lib->vol_data_bytes, dev_num, vol_id, - &info->data_bytes); - if (ret) - return -1; - ret = vol_read_int(lib->vol_rsvd_ebs, dev_num, vol_id, &info->rsvd_lebs); - if (ret) - return -1; - ret = vol_read_int(lib->vol_eb_size, dev_num, vol_id, &info->leb_size); - if (ret) - return -1; - ret = vol_read_int(lib->vol_corrupted, dev_num, vol_id, - &info->corrupted); - if (ret) + major = major(stat.st_rdev); + minor = minor(stat.st_rdev); + + if (minor == 0) { + errno = -EINVAL; return -1; - info->rsvd_bytes = info->leb_size * info->rsvd_lebs; + } - ret = vol_read_data(lib->vol_name, dev_num, vol_id, &info->name, - UBI_VOL_NAME_MAX + 2); - if (ret < 0) + if (ubi_get_dev_info1((libubi_t *)lib, dev_num, &info)) return -1; - info->name[ret - 1] = '\0'; - return 0; -} + for (i = info.lowest_vol_num; i <= info.highest_vol_num; i++) { + int major1, minor1, ret; + char buf[50]; -int ubi_get_vol_info(libubi_t desc, const char *node, struct ubi_vol_info *info) -{ - int vol_id, dev_num; - struct libubi *lib = (struct libubi *)desc; + ret = vol_read_data(lib->vol_dev, dev_num, i, &buf[0], 50); + if (ret < 0) + return -1; - if (vol_node2nums(lib, node, &dev_num, &vol_id)) - return -1; + ret = sscanf(&buf[0], "%d:%d\n", &major1, &minor1); + if (ret != 2) { + fprintf(stderr, "LIBUBI: bad value at sysfs file\n"); + errno = EINVAL; + return -1; + } - return ubi_get_vol_info1(desc, dev_num, vol_id, info); + if (minor1 == minor && major1 == major) + return i; + } + + errno = ENOENT; + return -1; } diff --git a/ubi-utils/src/libubi_int.h b/ubi-utils/src/libubi_int.h index dab3e62..e68b791 100644 --- a/ubi-utils/src/libubi_int.h +++ b/ubi-utils/src/libubi_int.h @@ -23,45 +23,20 @@ #ifndef __LIBUBI_INT_H__ #define __LIBUBI_INT_H__ -#include -#include - #ifdef __cplusplus extern "C" { #endif -/* Error messages */ -#define errmsg(fmt, ...) ({ \ - fprintf(stderr, "libubi error: " fmt "\n", ##__VA_ARGS__); \ - -1; \ -}) - -/* System error messages */ -#define sys_errmsg(fmt, ...) ({ \ - int _err = errno; \ - fprintf(stderr, "libubi error: " fmt "\n", ##__VA_ARGS__); \ - fprintf(stderr, "error %d (%s)\n", _err, strerror(_err)); \ - -1; \ -}) - /* + * UBI heavily makes use of the sysfs file system to interact with users-pace. * The below are pre-define UBI file and directory names. - * - * Note, older kernels put 'ubiX_Y' directories straight to '/sys/class/ubi/'. - * New kernels puts 'ubiX_Y' directories to '/sys/class/ubi/ubiX/', which is - * saner. And for compatibility reasons it also puts symlinks to 'ubiX_Y' - * directories to '/sys/class/ubi/'. For now libubi assumes old layout. */ #define SYSFS_UBI "class/ubi" -#define SYSFS_CTRL "class/misc/ubi_ctrl/" - -#define CTRL_DEV "dev" - -#define UBI_VER "version" #define UBI_DEV_NAME_PATT "ubi%d" - +#define UBI_VER "version" #define DEV_DEV "dev" +#define UBI_VOL_NAME_PATT "ubi%d_%d" #define DEV_AVAIL_EBS "avail_eraseblocks" #define DEV_TOTAL_EBS "total_eraseblocks" #define DEV_BAD_COUNT "bad_peb_count" @@ -70,9 +45,6 @@ extern "C" { #define DEV_MAX_RSVD "reserved_for_bad" #define DEV_MAX_VOLS "max_vol_count" #define DEV_MIN_IO_SIZE "min_io_size" -#define DEV_MTD_NUM "mtd_num" - -#define UBI_VOL_NAME_PATT "ubi%d_%d" #define VOL_TYPE "type" #define VOL_DEV "dev" #define VOL_ALIGNMENT "alignment" @@ -84,37 +56,34 @@ extern "C" { /** * libubi - UBI library description data structure. - * @sysfs: sysfs file system path - * @sysfs_ctrl: UBI control device directory in sysfs - * @ctrl_dev: UBI control device major/minor numbers sysfs file - * @sysfs_ubi: UBI directory in sysfs - * @ubi_dev: UBI device sysfs directory pattern - * @ubi_version: UBI version file sysfs path - * @dev_dev: UBI device major/minor numbers file pattern - * @dev_avail_ebs: count of available eraseblocks sysfs path pattern - * @dev_total_ebs: total eraseblocks count sysfs path pattern - * @dev_bad_count: count of bad eraseblocks sysfs path pattern - * @dev_eb_size: size of UBI device's eraseblocks sysfs path pattern - * @dev_max_ec: maximum erase counter sysfs path pattern - * @dev_bad_rsvd: count of physical eraseblock reserved for bad eraseblocks - * handling - * @dev_max_vols: maximum volumes number count sysfs path pattern - * @dev_min_io_size: minimum I/O unit size sysfs path pattern - * @ubi_vol: UBI volume sysfs directory pattern - * @vol_type: volume type sysfs path pattern - * @vol_dev: volume major/minor numbers file pattern - * @vol_alignment: volume alignment sysfs path pattern - * @vol_data_bytes: volume data size sysfs path pattern - * @vol_rsvd_ebs: volume reserved size sysfs path pattern - * @vol_eb_size: volume eraseblock size sysfs path pattern - * @vol_corrupted: volume corruption flag sysfs path pattern - * @vol_name: volume name sysfs path pattern + * + * @sysfs sysfs file system path + * @sysfs_ubi UBI directory in sysfs + * @ubi_dev UBI device sysfs directory pattern + * @ubi_version UBI version file sysfs path + * @dev_dev UBI device's major/minor numbers file pattern + * @dev_avail_ebs count of available eraseblocks sysfs path pattern + * @dev_total_ebs total eraseblocks count sysfs path pattern + * @dev_bad_count count of bad eraseblocks sysfs path pattern + * @dev_eb_size size of UBI device's eraseblocks sysfs path pattern + * @dev_max_ec maximum erase counter sysfs path pattern + * @dev_bad_rsvd count of physical eraseblock reserved for bad eraseblocks + * handling + * @dev_max_vols maximum volumes number count sysfs path pattern + * @dev_min_io_size minimum I/O unit size sysfs path pattern + * @ubi_vol UBI volume sysfs directory pattern + * @vol_type volume type sysfs path pattern + * @vol_dev volume's major/minor numbers file pattern + * @vol_alignment volume alignment sysfs path pattern + * @vol_data_bytes volume data size sysfs path pattern + * @vol_rsvd_ebs volume reserved size sysfs path pattern + * @vol_eb_size volume eraseblock size sysfs path pattern + * @vol_corrupted volume corruption flag sysfs path pattern + * @vol_name volume name sysfs path pattern */ struct libubi { char *sysfs; - char *sysfs_ctrl; - char *ctrl_dev; char *sysfs_ubi; char *ubi_dev; char *ubi_version; @@ -127,7 +96,6 @@ struct libubi char *dev_bad_rsvd; char *dev_max_vols; char *dev_min_io_size; - char *dev_mtd_num; char *ubi_vol; char *vol_type; char *vol_dev; @@ -140,6 +108,20 @@ struct libubi char *vol_max_count; }; +static int read_int(const char *file, int *value); +static int dev_read_int(const char *patt, int dev_num, int *value); +static int dev_read_ll(const char *patt, int dev_num, long long *value); +static int dev_read_data(const char *patt, int dev_num, void *buf, int buf_len); +static int vol_read_int(const char *patt, int dev_num, int vol_id, int *value); +static int vol_read_ll(const char *patt, int dev_num, int vol_id, + long long *value); +static int vol_read_data(const char *patt, int dev_num, int vol_id, void *buf, + int buf_len); +static char *mkpath(const char *path, const char *name); +static int find_dev_num(struct libubi *lib, const char *node); +static int find_dev_num_vol(struct libubi *lib, const char *node); +static int find_vol_num(struct libubi *lib, int dev_num, const char *node); + #ifdef __cplusplus } #endif diff --git a/ubi-utils/src/libubigen.c b/ubi-utils/src/libubigen.c index 8d71fde..1fce3f9 100644 --- a/ubi-utils/src/libubigen.c +++ b/ubi-utils/src/libubigen.c @@ -1,6 +1,5 @@ /* * Copyright (c) International Business Machines Corp., 2006 - * Copyright (C) 2008 Nokia Corporation * * 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 @@ -15,307 +14,474 @@ * 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. - */ - -/* - * Generating UBI images. * - * Authors: Oliver Lohmann - * Artem Bityutskiy + * Author: Oliver Lohmann + * + * Add UBI headers to binary data. */ #include #include #include -#include +#include #include - #include -#include +#include + +#include "config.h" +#include "ubigen.h" #include "crc32.h" -#include "common.h" - -#define PROGRAM_NAME "libubigen" - -/** - * ubigen_info_init - initialize libubigen. - * @ui: libubigen information - * @peb_size: flash physical eraseblock size - * @min_io_size: flash minimum input/output unit size - * @subpage_size: flash sub-page, if present (has to be equivalent to - * @min_io_size if does not exist) - * @vid_hdr_offs: offset of the VID header - * @ubi_ver: UBI version - * @ec: initial erase counter - */ -void ubigen_info_init(struct ubigen_info *ui, int peb_size, int min_io_size, - int subpage_size, int vid_hdr_offs, int ubi_ver, - long long ec) + +#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) { - if (!vid_hdr_offs) - vid_hdr_offs = subpage_size; - - ui->peb_size = peb_size; - ui->min_io_size = min_io_size; - ui->vid_hdr_offs = vid_hdr_offs; - ui->data_offs = vid_hdr_offs + UBI_VID_HDR_SIZE + min_io_size - 1; - ui->data_offs /= min_io_size; - ui->data_offs *= min_io_size; - ui->leb_size = peb_size - ui->data_offs; - ui->ubi_ver = ubi_ver; - ui->ec = ec; - - ui->vtbl_size = ui->leb_size; - if (ui->vtbl_size > UBI_MAX_VOLUMES * UBI_VTBL_RECORD_SIZE) - ui->vtbl_size = UBI_MAX_VOLUMES * UBI_VTBL_RECORD_SIZE; - ui->max_volumes = ui->vtbl_size / UBI_VTBL_RECORD_SIZE; + return (byte % eb_size) == 0 + ? (byte / eb_size) + : (byte / eb_size) + 1; } -/** - * ubigen_create_empty_vtbl - creates empty volume table. - * - * This function creates an empty volume table and returns a pointer to it in - * case of success and %NULL in case of failure. The returned object has to be - * freed with 'free()' call. - */ -struct ubi_vtbl_record *ubigen_create_empty_vtbl(const struct ubigen_info *ui) +static int +validate_ubi_info(ubi_info_t u) { - struct ubi_vtbl_record *vtbl; - int i; - - vtbl = calloc(1, ui->vtbl_size); - if (!vtbl) { - errmsg("cannot allocate %d bytes of memory", ui->vtbl_size); - return NULL; + if ((u->v->vol_type != UBI_VID_DYNAMIC) && + (u->v->vol_type != UBI_VID_STATIC)) { + return EUBIGEN_INVALID_TYPE; } - for (i = 0; i < UBI_MAX_VOLUMES; i++) { - uint32_t crc = crc32(UBI_CRC32_INIT, &vtbl[i], - UBI_VTBL_RECORD_SIZE_CRC); - vtbl[i].crc = cpu_to_be32(crc); + if (be32_to_cpu(u->ec->vid_hdr_offset) < UBI_VID_HDR_SIZE) { + return EUBIGEN_INVALID_HDR_OFFSET; } - return vtbl; + return 0; } -/** - * ubigen_add_volume - add a volume to the volume table. - * @ui: libubigen information - * @vi: volume information - * @vtbl: volume table to add to - * - * This function adds volume described by input parameters to the volume table - * @vtbl. - */ -int ubigen_add_volume(const struct ubigen_info *ui, - const struct ubigen_vol_info *vi, - struct ubi_vtbl_record *vtbl) +static int +skip_blks(ubi_info_t u, uint32_t blks) { - struct ubi_vtbl_record *vtbl_rec = &vtbl[vi->id]; - uint32_t tmp; - - if (vi->id >= ui->max_volumes) - return errmsg("too high volume id %d, max. volumes is %d", - vi->id, ui->max_volumes); - - if (vi->alignment >= ui->leb_size) - return errmsg("too large alignment %d, max is %d (LEB size)", - vi->alignment, ui->leb_size); - - memset(vtbl_rec, '\0', sizeof(struct ubi_vtbl_record)); - tmp = (vi->bytes + ui->leb_size - 1) / ui->leb_size; - vtbl_rec->reserved_pebs = cpu_to_be32(tmp); - vtbl_rec->alignment = cpu_to_be32(vi->alignment); - vtbl_rec->vol_type = vi->type; - tmp = ui->leb_size % vi->alignment; - vtbl_rec->data_pad = cpu_to_be32(tmp); - vtbl_rec->flags = vi->flags; - - memcpy(vtbl_rec->name, vi->name, vi->name_len); - vtbl_rec->name[vi->name_len] = '\0'; - vtbl_rec->name_len = cpu_to_be16(vi->name_len); - - tmp = crc32(UBI_CRC32_INIT, vtbl_rec, UBI_VTBL_RECORD_SIZE_CRC); - vtbl_rec->crc = cpu_to_be32(tmp); + 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; } -/** - * init_ec_hdr - initialize EC header. - * @ui: libubigen information - * @hdr: the EC header to initialize - */ -static void init_ec_hdr(const struct ubigen_info *ui, - struct ubi_ec_hdr *hdr) +static void +clear_buf(ubi_info_t u) { - uint32_t crc; + memset(u->buf, 0xff, u->eb_size); +} - memset(hdr, '\0', sizeof(struct ubi_ec_hdr)); +static void +write_ec_hdr(ubi_info_t u) +{ + memcpy(u->ptr_ec_hdr, u->ec, UBI_EC_HDR_SIZE); +} - hdr->magic = cpu_to_be32(UBI_EC_HDR_MAGIC); - hdr->version = ui->ubi_ver; - hdr->ec = cpu_to_be64(ui->ec); - hdr->vid_hdr_offset = cpu_to_be32(ui->vid_hdr_offs); +static int +fill_data_buffer_from_file(ubi_info_t u, size_t* read) +{ + size_t to_read = 0; - hdr->data_offset = cpu_to_be32(ui->data_offs); + if (u-> fp_in == NULL) + return -EIO; - crc = crc32(UBI_CRC32_INIT, hdr, UBI_EC_HDR_SIZE_CRC); - hdr->hdr_crc = cpu_to_be32(crc); + 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; } -/** - * init_vid_hdr - initialize VID header. - * @ui: libubigen information - * @vi: volume information - * @hdr: the VID header to initialize - * @lnum: logical eraseblock number - * @data: the contents of the LEB (static volumes only) - * @data_size: amount of data in this LEB (static volumes only) - * - * Note, @used_ebs, @data and @data_size are ignored in case of dynamic - * volumes. - */ -static void init_vid_hdr(const struct ubigen_info *ui, - const struct ubigen_vol_info *vi, - struct ubi_vid_hdr *hdr, int lnum, - const void *data, int data_size) +static void +add_static_info(ubi_info_t u, size_t data_size, ubigen_action_t action) { - uint32_t crc; + uint32_t crc = clc_crc32(crc32_table, UBI_CRC32_INIT, + u->ptr_data, data_size); - memset(hdr, '\0', sizeof(struct ubi_vid_hdr)); - - hdr->magic = cpu_to_be32(UBI_VID_HDR_MAGIC); - hdr->version = ui->ubi_ver; - hdr->vol_type = vi->type; - hdr->vol_id = cpu_to_be32(vi->id); - hdr->lnum = cpu_to_be32(lnum); - hdr->data_pad = cpu_to_be32(vi->data_pad); - hdr->compat = vi->compat; - - if (vi->type == UBI_VID_STATIC) { - hdr->data_size = cpu_to_be32(data_size); - hdr->used_ebs = cpu_to_be32(vi->used_ebs); - crc = crc32(UBI_CRC32_INIT, data, data_size); - hdr->data_crc = cpu_to_be32(crc); + u->v->data_size = cpu_to_be32(data_size); + u->v->data_crc = cpu_to_be32(crc); + + if (action & BROKEN_DATA_CRC) { + u->v->data_crc = + cpu_to_be32(be32_to_cpu(u->v->data_crc) + 1); + } + if (action & BROKEN_DATA_SIZE) { + u->v->data_size = + cpu_to_be32(be32_to_cpu(u->v->data_size) + 1); } +} - crc = crc32(UBI_CRC32_INIT, hdr, UBI_VID_HDR_SIZE_CRC); - hdr->hdr_crc = cpu_to_be32(crc); +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_be32(crc); + if (action & BROKEN_HDR_CRC) { + u->v->hdr_crc = cpu_to_be32(be32_to_cpu(u->v->hdr_crc) + 1); + } + memcpy(u->ptr_vid_hdr, u->v, UBI_VID_HDR_SIZE); } -/** - * ubigen_write_volume - write UBI volume. - * @ui: libubigen information - * @vi: volume information - * @bytes: volume size in bytes - * @in: input file descriptor (has to be properly seeked) - * @out: output file descriptor - * - * This function reads the contents of the volume from the input file @in and - * writes the UBI volume to the output file @out. Returns zero on success and - * %-1 on failure. - */ -int ubigen_write_volume(const struct ubigen_info *ui, - const struct ubigen_vol_info *vi, - long long bytes, FILE *in, FILE *out) +static int +write_to_output_stream(ubi_info_t u) { - int len = vi->usable_leb_size, rd, lnum = 0; - char inbuf[ui->leb_size], outbuf[ui->peb_size]; + size_t written; - if (vi->id >= ui->max_volumes) - return errmsg("too high volume id %d, max. volumes is %d", - vi->id, ui->max_volumes); + written = fwrite(u->buf, 1, u->eb_size, u->fp_out); + if (written != u->eb_size) { + return -EIO; + } + return 0; +} - if (vi->alignment >= ui->leb_size) - return errmsg("too large alignment %d, max is %d (LEB size)", - vi->alignment, ui->leb_size); +int +ubigen_write_leb(ubi_info_t u, ubigen_action_t action) +{ + int rc = 0; + size_t read = 0; - memset(outbuf, 0xFF, ui->data_offs); - init_ec_hdr(ui, (struct ubi_ec_hdr *)outbuf); + clear_buf(u); + write_ec_hdr(u); - while (bytes) { - int l; - struct ubi_vid_hdr *vid_hdr; + rc = fill_data_buffer_from_file(u, &read); + if (rc != 0) + return rc; - if (bytes < len) - len = bytes; - bytes -= len; + if (u->v->vol_type == UBI_VID_STATIC) { + add_static_info(u, read, action); + } - l = len; - do { - rd = fread(inbuf + len - l, 1, l, in); - if (rd == 0) { - if (ferror(in)) - return errmsg("cannot read %d bytes from the input file", l); - else - return errmsg("not enough data in the input file"); - } + u->v->lnum = cpu_to_be32(u->blks_written); - l -= rd; - } while (l); + if (action & MARK_AS_UPDATE) { + u->v->copy_flag = (u->v->copy_flag)++; + } - vid_hdr = (struct ubi_vid_hdr *)(&outbuf[ui->vid_hdr_offs]); - init_vid_hdr(ui, vi, vid_hdr, lnum, inbuf, len); + write_vid_hdr(u, action); + rc = write_to_output_stream(u); + if (rc != 0) + return rc; - memcpy(outbuf + ui->data_offs, inbuf, len); - memset(outbuf + ui->data_offs + len, 0xFF, - ui->peb_size - ui->data_offs - len); + /* Update current handle */ + u->bytes_read += read; + u->blks_written++; + return 0; +} - if (fwrite(outbuf, 1, ui->peb_size, out) != ui->peb_size) - return errmsg("cannot write %d bytes from the output file", l); +int +ubigen_write_complete(ubi_info_t u) +{ + size_t i; + int rc = 0; - lnum += 1; + for (i = 0; i < u->leb_total; i++) { + rc = ubigen_write_leb(u, NO_ERROR); + if (rc != 0) + return rc; } return 0; } -/** - * ubigen_write_layout_vol - write UBI layout volume - * @ui: libubigen information - * @vtbl: volume table - * @out: output file stream - * - * This function creates the UBI layout volume which contains 2 copies of the - * volume table. Returns zero in case of success and %-1 in case of failure. - */ -int ubigen_write_layout_vol(const struct ubigen_info *ui, - struct ubi_vtbl_record *vtbl, FILE *out) +int +ubigen_write_broken_update(ubi_info_t u, uint32_t blk) { - int size = ui->leb_size; - struct ubigen_vol_info vi; - char outbuf[ui->peb_size]; - struct ubi_vid_hdr *vid_hdr; - - if (size > UBI_MAX_VOLUMES * UBI_VTBL_RECORD_SIZE) - size = UBI_MAX_VOLUMES * UBI_VTBL_RECORD_SIZE; - - vi.bytes = ui->leb_size * UBI_LAYOUT_VOLUME_EBS; - vi.id = UBI_LAYOUT_VOLUME_ID; - vi.alignment = UBI_LAYOUT_VOLUME_ALIGN; - vi.data_pad = ui->leb_size % UBI_LAYOUT_VOLUME_ALIGN; - vi.usable_leb_size = ui->leb_size - vi.data_pad; - vi.data_pad = ui->leb_size - vi.usable_leb_size; - vi.type = UBI_LAYOUT_VOLUME_TYPE; - vi.name = UBI_LAYOUT_VOLUME_NAME; - vi.name_len = strlen(UBI_LAYOUT_VOLUME_NAME); - vi.compat = UBI_LAYOUT_VOLUME_COMPAT; - - memset(outbuf, 0xFF, ui->data_offs); - vid_hdr = (struct ubi_vid_hdr *)(&outbuf[ui->vid_hdr_offs]); - init_ec_hdr(ui, (struct ubi_ec_hdr *)outbuf); - memcpy(outbuf + ui->data_offs, vtbl, size); - memset(outbuf + ui->data_offs + size, 0xFF, - ui->peb_size - ui->data_offs - size); - - init_vid_hdr(ui, &vi, vid_hdr, 0, NULL, 0); - size = fwrite(outbuf, 1, ui->peb_size, out); - if (size == ui->peb_size) { - init_vid_hdr(ui, &vi, vid_hdr, 1, NULL, 0); - size = fwrite(outbuf, 1, ui->peb_size, out); - if (size != ui->peb_size) - return sys_errmsg("cannot write %d bytes", ui->peb_size); + 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 ubi_unused) +{ +#ifdef DEBUG + int err = 0; + if (!u) { + fprintf(stderr, ""); + return; + } + if (!u->ec) { + fprintf(stderr, ""); + err = 1; + } + if (!u->v) { + fprintf(stderr, ""); + err = 1; + } + if (err) return; + + fprintf(stderr, "ubi volume\n"); + fprintf(stderr, "version : %8d\n", u->v->version); + fprintf(stderr, "vol_id : %8d\n", be32_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", + be32_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", + be32_to_cpu(u->v->data_pad)); + fprintf(stderr, "leb_total : %8d\n", u->leb_total); + fprintf(stderr, "header offs : 0x%08x\n", + be32_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_be32(UBI_VID_HDR_MAGIC); + res->v->version = version ? version : UBI_VERSION; + res->v->vol_type = vol_type; + res->v->vol_id = cpu_to_be32(vol_id); + res->v->compat = compat_flag; + res->v->data_pad = cpu_to_be32(res->data_pad); + + /* static only: used_ebs */ + if (res->v->vol_type == UBI_VID_STATIC) { + res->v->used_ebs = cpu_to_be32(byte_to_blk + (res->bytes_total, + res->leb_size)); + } + + /* ec hdr (fixed, doesn't change) */ + res->ec->magic = cpu_to_be32(UBI_EC_HDR_MAGIC); + res->ec->version = version ? version : UBI_VERSION; + res->ec->ec = cpu_to_be64(ec); + res->ec->vid_hdr_offset = cpu_to_be32(vid_hdr_offset); + + res->ec->data_offset = cpu_to_be32(data_offset); + + crc = clc_crc32(crc32_table, UBI_CRC32_INIT, res->ec, + UBI_EC_HDR_SIZE_CRC); + res->ec->hdr_crc = cpu_to_be32(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 + be32_to_cpu(res->ec->vid_hdr_offset); + res->ptr_data = res->buf + be32_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_vtbl_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_be32(byte_to_blk(reserved_bytes, u->leb_size)); + lvol_rec->alignment = cpu_to_be32(u->alignment); + lvol_rec->data_pad = u->v->data_pad; + lvol_rec->vol_type = u->v->vol_type; + + lvol_rec->name_len = + cpu_to_be16((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_be32(crc); + return 0; } diff --git a/ubi-utils/src/libubimirror.c b/ubi-utils/src/libubimirror.c new file mode 100644 index 0000000..d06770e --- /dev/null +++ b/ubi-utils/src/libubimirror.c @@ -0,0 +1,237 @@ +/* + * 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 +#include +#include +#include +#include +#include + +#include +#include "ubimirror.h" + +#define COMPARE_BUF_SIZE (128 * 1024) + +#define DEFAULT_DEV_PATTERN "/dev/ubi%d" +#define DEFAULT_VOL_PATTERN "/dev/ubi%d_%d" + +#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, ssize_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, ssize_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 files 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; +} + +int 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; +} + +static int copy_files(libubi_t ulib, 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 (vol_get_used_bytes(fd_in, &update_size) == -1 || + ubi_update_start(ulib, 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, ssize_t ids_size, + char *err_buf, size_t err_buf_size) +{ + int rc = 0; + uint32_t src_id; + char path[PATH_MAX]; + libubi_t ulib; + 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]; + } + + ulib = libubi_open(); + if (ulib == NULL) + return ubi_error; + + snprintf(path, PATH_MAX, DEFAULT_VOL_PATTERN, devno, src_id); + + fd_in = open(path, 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; + + snprintf(path, PATH_MAX, DEFAULT_VOL_PATTERN, devno, ids[i]); + + fd_out = open(path, O_RDWR); + if (fd_out < 0){ + 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; + } else if (rc == compare_different) { + rc = copy_files(ulib, fd_in, fd_out); + if (rc != 0) { + EBUF("mirror error volume %d to %d", src_id, + ids[i]); + goto err; + } + } + if ((rc = close(fd_out)) == -1) { + EBUF("close error volume %d", ids[i]); + rc = close_error; + goto err; + } else + fd_out = -1; + } +err: + if (fd_out != -1) + close(fd_out); + if (fd_in != -1) + close(fd_in); + if (ulib != NULL) + libubi_close(ulib); + return rc; +} diff --git a/ubi-utils/src/list.c b/ubi-utils/src/list.c new file mode 100644 index 0000000..6eb716b --- /dev/null +++ b/ubi-utils/src/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 +#include +#include + +#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/list.h b/ubi-utils/src/list.h new file mode 100644 index 0000000..e8452a2 --- /dev/null +++ b/ubi-utils/src/list.h @@ -0,0 +1,56 @@ +#ifndef __LIST_H__ +#define __LIST_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 + +#define foreach(elem, ptr, list) \ + for (elem = list != NULL ? (typeof(elem)) head(list) \ + : NULL, ptr = list; \ + ptr != NULL; \ + ptr = tail(ptr), \ + elem = (typeof(elem)) ptr ? head(ptr) : NULL) + +typedef struct node* list_t; +typedef void* info_t; +typedef int (*free_func_t)(info_t*); +typedef int (*cmp_func_t)(info_t, info_t); +typedef void (*process_func_t)(info_t); + +struct node { + list_t next; + info_t info; +}; + +list_t mk_empty(void); +int is_empty(list_t l); +info_t is_in(cmp_func_t cmp, info_t e, list_t l); +info_t head(list_t l); +list_t tail(list_t l); +list_t remove_head(list_t l); +list_t cons(info_t e, list_t l); +list_t prepend_elem(info_t e, list_t); +list_t append_elem(info_t e, list_t); +list_t remove_all(free_func_t free_func, list_t l); +list_t insert_sorted(cmp_func_t cmp_func, info_t e, list_t l); +void apply(process_func_t process_func, list_t l); + +#endif /* __LIST_H__ */ diff --git a/ubi-utils/src/mkbootenv.c b/ubi-utils/src/mkbootenv.c new file mode 100644 index 0000000..952f651 --- /dev/null +++ b/ubi-utils/src/mkbootenv.c @@ -0,0 +1,168 @@ +/* + * 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. + * + * 1.2 Removed argp because we want to use uClibc. + * 1.3 Minor cleanup + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "bootenv.h" +#include "error.h" + +#define PROGRAM_VERSION "1.3" + +static char doc[] = "\nVersion: " PROGRAM_VERSION "\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 const char *optionsstr = +" -c, --copyright Print copyright informatoin.\n" +" -o, --output= Write the output data to instead of\n" +" stdout.\n" +" -?, --help Give this help list\n" +" --usage Give a short usage message\n" +" -V, --version Print program version\n"; + +static const char *usage = +"Usage: mkbootenv [-c?V] [-o ] [--copyright] [--output=]\n" +" [--help] [--usage] [--version] [bootenv-txt-file]\n"; + +struct option long_options[] = { + { .name = "copyright", .has_arg = 0, .flag = NULL, .val = 'c' }, + { .name = "output", .has_arg = 1, .flag = NULL, .val = 'o' }, + { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' }, + { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 }, + { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, + { NULL, 0, NULL, 0} +}; + +typedef struct myargs { + FILE* fp_in; + FILE* fp_out; + + char *arg1; + char **options; /* [STRING...] */ +} myargs; + +static int +parse_opt(int argc, char **argv, myargs *args) +{ + while (1) { + int key; + + key = getopt_long(argc, argv, "co:?V", long_options, NULL); + if (key == -1) + break; + + switch (key) { + case 'c': + fprintf(stderr, "%s\n", copyright); + exit(0); + break; + case 'o': + args->fp_out = fopen(optarg, "wb"); + if ((args->fp_out) == NULL) { + fprintf(stderr, "Cannot open file %s " + "for output\n", optarg); + exit(1); + } + break; + case '?': /* help */ + printf("%s", doc); + printf("%s", optionsstr); + printf("\nReport bugs to %s\n", + PACKAGE_BUGREPORT); + exit(0); + break; + case 'V': + printf("%s\n", PROGRAM_VERSION); + exit(0); + break; + default: + printf("%s", usage); + exit(-1); + } + } + + if (optind < argc) { + args->fp_in = fopen(argv[optind++], "rb"); + if ((args->fp_in) == NULL) { + fprintf(stderr, "Cannot open file %s for input\n", + argv[optind]); + exit(1); + } + } + + return 0; +} + +int +main(int argc, char **argv) { + int rc = 0; + bootenv_t env; + + myargs args = { + .fp_in = stdin, + .fp_out = stdout, + .arg1 = NULL, + .options = NULL, + }; + + parse_opt(argc, argv, &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/nand2bin.c b/ubi-utils/src/nand2bin.c new file mode 100644 index 0000000..be62e30 --- /dev/null +++ b/ubi-utils/src/nand2bin.c @@ -0,0 +1,493 @@ +/* + * Copyright (c) International Business Machines Corp., 2006, 2007 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Frank Haverkamp + * + * An utility to decompose NAND images and strip OOB off. Not yet finished ... + * + * 1.2 Removed argp because we want to use uClibc. + * 1.3 Minor cleanup + * 1.4 Fixed OOB output file + * 1.5 Added verbose output and option to set blocksize. + * Added split block mode for more convenient analysis. + * 1.6 Fixed ECC error detection and correction. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "nandecc.h" + +#define PROGRAM_VERSION "1.6" + +#define MAXPATH 1024 +#define MIN(x,y) ((x)<(y)?(x):(y)) + +struct args { + const char *oob_file; + const char *output_file; + size_t pagesize; + size_t blocksize; + int split_blocks; + size_t in_len; /* size of input file */ + int correct_ecc; + + /* 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, + .blocksize = 128 * 1024, + .in_len = 0, + .split_blocks = 0, + .correct_ecc = 0, + .arg1 = NULL, + .options = NULL, +}; + +static char doc[] = "\nVersion: " PROGRAM_VERSION "\n" + "nand2bin - split data and OOB.\n"; + +static const char *optionsstr = +" -o, --output= Data output file\n" +" -O, --oob= OOB output file\n" +" -p, --pagesize= NAND pagesize\n" +" -b, --blocksize= NAND blocksize\n" +" -s, --split-blocks generate binaries for each block\n" +" -e, --correct-ecc Correct data according to ECC info\n" +" -v, --verbose verbose output\n" +" -?, --help Give this help list\n" +" --usage Give a short usage message\n"; + +static const char *usage = +"Usage: nand2bin [-?] [-o ] [-O ] [-p ]\n" +" [--output=] [--oob=] [--pagesize=] [--help]\n" +" [--usage] input.mif\n"; + +static int verbose = 0; + +static struct option long_options[] = { + { .name = "output", .has_arg = 1, .flag = NULL, .val = 'o' }, + { .name = "oob", .has_arg = 1, .flag = NULL, .val = 'O' }, + { .name = "pagesize", .has_arg = 1, .flag = NULL, .val = 'p' }, + { .name = "blocksize", .has_arg = 1, .flag = NULL, .val = 'b' }, + { .name = "split-blocks", .has_arg = 0, .flag = NULL, .val = 's' }, + { .name = "correct-ecc", .has_arg = 0, .flag = NULL, .val = 'e' }, + { .name = "verbose", .has_arg = 0, .flag = NULL, .val = 'v' }, + { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' }, + { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 }, + { NULL, 0, NULL, 0} +}; + +/* + * str_to_num - Convert string into number and cope with endings like + * k, K, kib, KiB for kilobyte + * m, M, mib, MiB for megabyte + */ +static 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 argc The number of arguments + * @param argv The argument list + * @param args Pointer to program args structure + * + * @return error + * + */ +static int parse_opt(int argc, char **argv, struct args *args) +{ + while (1) { + int key; + + key = getopt_long(argc, argv, "b:eo:O:p:sv?", long_options, NULL); + if (key == -1) + break; + + switch (key) { + case 'p': /* --pagesize */ + args->pagesize = str_to_num(optarg); + break; + + case 'b': /* --blocksize */ + args->blocksize = str_to_num(optarg); + break; + + case 'v': /* --verbose */ + verbose++; + break; + + case 's': /* --split-blocks */ + args->split_blocks = 1; + break; + + case 'e': /* --correct-ecc */ + args->correct_ecc = 1; + break; + + case 'o': /* --output= */ + args->output_file = optarg; + break; + + case 'O': /* --oob= */ + args->oob_file = optarg; + break; + + case '?': /* help */ + printf("Usage: nand2bin [OPTION...] input.mif\n"); + printf("%s", doc); + printf("%s", optionsstr); + printf("\nReport bugs to %s\n", + PACKAGE_BUGREPORT); + exit(0); + break; + + case 'V': + printf("%s\n", PROGRAM_VERSION); + exit(0); + break; + + default: + printf("%s", usage); + exit(-1); + } + } + + if (optind < argc) + args->arg1 = argv[optind++]; + + 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, ssize_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: %zd\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 bad_marker_offs_in_oob(int pagesize) +{ + switch (pagesize) { + case 2048: return 0; + case 512: return 5; + } + return -EINVAL; +} + +static int decompose_image(struct args *args, FILE *in_fp, + FILE *bin_fp, FILE *oob_fp) +{ + int read, rc, page = 0; + size_t oobsize = calc_oobsize(args->pagesize); + uint8_t *buf = malloc(args->pagesize); + uint8_t *oob = malloc(oobsize); + uint8_t *calc_oob = malloc(oobsize); + uint8_t *calc_buf = malloc(args->pagesize); + uint8_t *page_buf; + int pages_per_block = args->blocksize / args->pagesize; + int eccpoi = 0, eccpoi_start; + unsigned int i; + int badpos = bad_marker_offs_in_oob(args->pagesize); + + switch (args->pagesize) { + case 2048: eccpoi_start = 64 / 2; break; + case 512: eccpoi_start = 16 / 2; break; + default: exit(EXIT_FAILURE); + } + + 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 page by page */ + read = fread(buf, 1, args->pagesize, in_fp); + if (ferror(in_fp)) { + fprintf(stderr, "I/O Error."); + exit(EXIT_FAILURE); + } + if (read != (ssize_t)args->pagesize) + break; + + read = fread(oob, 1, oobsize, in_fp); + if (ferror(in_fp)) { + fprintf(stderr, "I/O Error."); + exit(EXIT_FAILURE); + } + + page_buf = buf; /* default is unmodified data */ + + if ((page == 0 || page == 1) && (oob[badpos] != 0xff)) { + if (verbose) + printf("Block %d is bad\n", + page / pages_per_block); + goto write_data; + } + if (args->correct_ecc) + page_buf = calc_buf; + + process_page(buf, calc_oob, args->pagesize); + memcpy(calc_buf, buf, args->pagesize); + + /* + * Our oob format uses only the last 3 bytes out of 4. + * The first byte is 0x00 when the ECC is generated by + * our toolset and 0xff when generated by Linux. This + * is to be fixed when we want nand2bin work for other + * ECC layouts too. + */ + for (i = 0, eccpoi = eccpoi_start; i < args->pagesize; + i += 256, eccpoi += 4) + oob[eccpoi] = calc_oob[eccpoi] = 0xff; + + if (verbose && memcmp(oob, calc_oob, oobsize) != 0) { + printf("\nECC compare mismatch found at block %d page %d!\n", + page / pages_per_block, page % pages_per_block); + + printf("Read out OOB Data:\n"); + hexdump(stdout, oob, oobsize); + + printf("Calculated OOB Data:\n"); + hexdump(stdout, calc_oob, oobsize); + } + + /* Do correction on subpage base */ + for (i = 0, eccpoi = eccpoi_start; i < args->pagesize; + i += 256, eccpoi += 4) { + rc = nand_correct_data(calc_buf + i, &oob[eccpoi + 1], + &calc_oob[eccpoi + 1]); + + if (rc == -1) + fprintf(stdout, "Uncorrectable ECC error at " + "block %d page %d/%d\n", + page / pages_per_block, + page % pages_per_block, i / 256); + else if (rc > 0) + fprintf(stdout, "Correctable ECC error at " + "block %d page %d/%d\n", + page / pages_per_block, + page % pages_per_block, i / 256); + } + + write_data: + rc = fwrite(page_buf, 1, args->pagesize, bin_fp); + if (ferror(bin_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); + } + + page++; + } + free(calc_buf); + free(calc_oob); + free(oob); + free(buf); + return 0; +} + +static int split_blocks(struct args *args, FILE *in_fp) +{ + uint8_t *buf; + size_t oobsize = calc_oobsize(args->pagesize); + int pages_per_block = args->blocksize / args->pagesize; + int block_len = pages_per_block * (args->pagesize + oobsize); + int blocks = args->in_len / block_len; + char bname[256] = { 0, }; + int badpos = bad_marker_offs_in_oob(args->pagesize); + int bad_blocks = 0, i, bad_block = 0; + ssize_t rc; + FILE *b; + + buf = malloc(block_len); + if (!buf) { + perror("Not enough memory"); + exit(EXIT_FAILURE); + } + + for (i = 0; i < blocks; i++) { + rc = fread(buf, 1, block_len, in_fp); + if (rc != block_len) { + fprintf(stderr, "cannot read enough data!\n"); + exit(EXIT_FAILURE); + } + + /* do block analysis */ + bad_block = 0; + if ((buf[args->pagesize + badpos] != 0xff) || + (buf[2 * args->pagesize + oobsize + badpos] != 0xff)) { + bad_blocks++; + bad_block = 1; + } + if ((verbose && bad_block) || (verbose > 1)) { + printf("-- (block %d oob of page 0 and 1)\n", i); + hexdump(stdout, buf + args->pagesize, oobsize); + printf("--\n"); + hexdump(stdout, buf + 2 * args->pagesize + + oobsize, oobsize); + } + + /* write complete block out */ + snprintf(bname, sizeof(bname) - 1, "%s.%d", args->arg1, i); + b = fopen(bname, "w+"); + if (!b) { + perror("Cannot open file"); + exit(EXIT_FAILURE); + } + rc = fwrite(buf, 1, block_len, b); + if (rc != block_len) { + fprintf(stderr, "could not write all data!\n"); + exit(EXIT_FAILURE); + } + fclose(b); + } + + free(buf); + if (bad_blocks || verbose) + fprintf(stderr, "%d blocks, %d bad blocks\n", + blocks, bad_blocks); + return 0; +} + +int +main(int argc, char *argv[]) +{ + FILE *in, *bin = NULL, *oob = NULL; + struct stat file_info; + + parse_opt(argc, argv, &myargs); + + if (!myargs.arg1) { + fprintf(stderr, "Please specify input file!\n"); + exit(EXIT_FAILURE); + } + + if (lstat(myargs.arg1, &file_info) != 0) { + perror("Cannot fetch file size from input file.\n"); + exit(EXIT_FAILURE); + } + myargs.in_len = file_info.st_size; + + in = fopen(myargs.arg1, "r"); + if (!in) { + perror("Cannot open file"); + exit(EXIT_FAILURE); + } + + if (myargs.split_blocks) { + split_blocks(&myargs, in); + goto out; + } + + 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(&myargs, in, bin, oob); + + out: + if (in) fclose(in); + if (bin) fclose(bin); + if (oob) fclose(oob); + exit(EXIT_SUCCESS); +} diff --git a/ubi-utils/src/nandcorr.c b/ubi-utils/src/nandcorr.c new file mode 100644 index 0000000..caa07e2 --- /dev/null +++ b/ubi-utils/src/nandcorr.c @@ -0,0 +1,95 @@ +/* + * 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; +} + +/** + * @dat: data which should be corrected + * @read_ecc: ecc information read from flash + * @calc_ecc: calculated ecc information from the data + * @return: number of corrected bytes + * or -1 when no correction is possible + */ +int nand_correct_data(uint8_t *dat, const uint8_t *read_ecc, + const uint8_t *calc_ecc) +{ + uint8_t s0, s1, s2; + + /* + * Do error detection + * + * Be careful, the index magic is due to a pointer to a + * uint32_t. + */ + s0 = calc_ecc[0] ^ read_ecc[0]; + s1 = calc_ecc[1] ^ read_ecc[1]; + s2 = calc_ecc[2] ^ read_ecc[2]; + + if ((s0 | s1 | s2) == 0) + return 0; + + /* 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/nandecc.c b/ubi-utils/src/nandecc.c new file mode 100644 index 0000000..71660ef --- /dev/null +++ b/ubi-utils/src/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/nandecc.h b/ubi-utils/src/nandecc.h new file mode 100644 index 0000000..bcf1982 --- /dev/null +++ b/ubi-utils/src/nandecc.h @@ -0,0 +1,29 @@ +#ifndef _NAND_ECC_H +#define _NAND_ECC_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. + * + * NAND ecc functions + */ + +#include + +int nand_calculate_ecc(const uint8_t *dat, uint8_t *ecc_code); +int nand_correct_data(uint8_t *dat, const uint8_t *read_ecc, + const uint8_t *calc_ecc); + +#endif diff --git a/ubi-utils/src/pddcustomize.c b/ubi-utils/src/pddcustomize.c new file mode 100644 index 0000000..515efd6 --- /dev/null +++ b/ubi-utils/src/pddcustomize.c @@ -0,0 +1,509 @@ +/* + * 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. + * + * 1.3 Removed argp because we want to use uClibc. + * 1.4 Minor cleanups + * 1.5 Migrated to new libubi + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "bootenv.h" +#include "error.h" +#include "example_ubi.h" +#include "libubi.h" +#include "ubimirror.h" + +#define PROGRAM_VERSION "1.5" + +#define DEFAULT_DEV_PATTERN "/dev/ubi%d" +#define DEFAULT_VOL_PATTERN "/dev/ubi%d_%d" + +typedef enum action_t { + ACT_NORMAL = 0, + ACT_LIST, + ACT_ARGP_ABORT, + ACT_ARGP_ERR, +} action_t; + +#define ABORT_ARGP do { \ + args->action = ACT_ARGP_ABORT; \ +} while (0) + +#define ERR_ARGP do { \ + args->action = ACT_ARGP_ERR; \ +} while (0) + +static char doc[] = "\nVersion: " PROGRAM_VERSION "\n" + "pddcustomize - customize bootenv and pdd values.\n"; + +static const char *optionsstr = +" -b, --both Mirror updated PDD to redundand copy.\n" +" -c, --copyright Print copyright information.\n" +" -i, --input= Binary input file. For debug purposes.\n" +" -l, --list List card bootenv/pdd values.\n" +" -o, --output= Binary output file. For debug purposes.\n" +" -s, --side= The side/seqnum to update.\n" +" -x, --host use x86 platform for debugging.\n" +" -?, --help Give this help list\n" +" --usage Give a short usage message\n" +" -V, --version Print program version\n"; + +static const char *usage = +"Usage: pddcustomize [-bclx?V] [-i ] [-o ] [-s ]\n" +" [--both] [--copyright] [--input=] [--list]\n" +" [--output=] [--side=] [--host] [--help] [--usage]\n" +" [--version] [key=value] [...]\n"; + +struct option long_options[] = { + { .name = "both", .has_arg = 0, .flag = NULL, .val = 'b' }, + { .name = "copyright", .has_arg = 0, .flag = NULL, .val = 'c' }, + { .name = "input", .has_arg = 1, .flag = NULL, .val = 'i' }, + { .name = "list", .has_arg = 0, .flag = NULL, .val = 'l' }, + { .name = "output", .has_arg = 1, .flag = NULL, .val = 'o' }, + { .name = "side", .has_arg = 1, .flag = NULL, .val = 's' }, + { .name = "host", .has_arg = 0, .flag = NULL, .val = 'x' }, + { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' }, + { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 }, + { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, + { NULL, 0, NULL, 0} +}; + +static const char copyright [] __attribute__((unused)) = + "Copyright IBM Corp 2006"; + +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 int +parse_opt(int argc, char **argv, myargs *args) +{ + int rc = 0; + + while (1) { + int key; + + key = getopt_long(argc, argv, "clbxs:i:o:?V", + long_options, NULL); + if (key == -1) + break; + + 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(optarg); + if (args->side < 0) { + err_msg("Unsupported seqnum: %d.\n" + "Supported seqnums are " + "'0' and '1'\n", + args->side, optarg); + ERR_ARGP; + } + break; + case 'i': + args->file_in = optarg; + break; + case 'o': + args->file_out = optarg; + break; + case '?': /* help */ + err_msg("Usage: pddcustomize [OPTION...] " + "[key=value] [...]"); + err_msg("%s", doc); + err_msg("%s", optionsstr); + err_msg("\nReport bugs to %s", + PACKAGE_BUGREPORT); + exit(0); + break; + case 'V': + err_msg("%s", PROGRAM_VERSION); + exit(0); + break; + default: + err_msg("%s", usage); + exit(-1); + } + } + + if (optind < argc) { + rc = extract_pair(args->env_in, argv[optind++]); + if (rc != 0) + ERR_ARGP; + } + + return 0; +} + +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) +{ + libubi_t ulib; + int rc = 0; + char path[PATH_MAX]; + FILE* fp_in = NULL; + + ulib = libubi_open(); + if (ulib == NULL) { + err_msg("Cannot allocate ubi structure\n"); + return -1; + } + + snprintf(path, PATH_MAX, DEFAULT_VOL_PATTERN, devno, id); + + fp_in = fopen(path, "r"); + 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); + libubi_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) +{ + libubi_t ulib; + int rc = 0; + char path[PATH_MAX]; + FILE* fp_out = NULL; + size_t nbytes ; + + rc = bootenv_size(env, &nbytes); + if (rc) { + err_msg("Cannot determine size of bootenv structure\n"); + return rc; + } + ulib = libubi_open(); + if (ulib == NULL) { + err_msg("Cannot allocate ubi structure\n"); + return rc; + } + + snprintf(path, PATH_MAX, DEFAULT_VOL_PATTERN, devno, id); + + fp_out = fopen(path, "r+"); + if (fp_out == NULL) { + err_msg("Cannot fopen 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); + libubi_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; + } + + parse_opt(argc, argv, &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/peb.c b/ubi-utils/src/peb.c new file mode 100644 index 0000000..08b770f --- /dev/null +++ b/ubi-utils/src/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 +#include +#include +#include +#include +#include + +#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/peb.h b/ubi-utils/src/peb.h new file mode 100644 index 0000000..246bce8 --- /dev/null +++ b/ubi-utils/src/peb.h @@ -0,0 +1,41 @@ +#ifndef __RAW_BLOCK_H__ +#define __RAW_BLOCK_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 +#include + +typedef struct peb *peb_t; +struct peb { + uint32_t num; /* Physical eraseblock number + * in the RAW file. */ + uint32_t size; /* Data Size (equals physical + * erase block size) */ + uint8_t* data; /* Data buffer */ +}; + +int peb_new(uint32_t peb_num, uint32_t peb_size, peb_t* peb); +int peb_free(peb_t* peb); +int peb_cmp(peb_t peb_1, peb_t peb_2); +int peb_write(FILE* fp_out, peb_t peb); +void peb_dump(FILE* fp_out, peb_t peb); + +#endif /* __RAW_BLOCK_H__ */ diff --git a/ubi-utils/src/pfi.c b/ubi-utils/src/pfi.c new file mode 100644 index 0000000..fa835e2 --- /dev/null +++ b/ubi-utils/src/pfi.c @@ -0,0 +1,458 @@ +/* + * 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. + * + * Wed Feb 8 11:38:22 CET 2006: Initial creation. + */ + +#include +#include +#include +#include +#include +#include +#include + +#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 */ +}; + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) + +static const char* modes[] = {"raw", "ubi"}; /* 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) +{ + int i; + + for (i = 0; i < (int)ARRAY_SIZE(modes); i++) + if (strcmp(mode, modes[i]) == 0) + return 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 (value == 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/pfi.h b/ubi-utils/src/pfi.h new file mode 100644 index 0000000..8c5cc07 --- /dev/null +++ b/ubi-utils/src/pfi.h @@ -0,0 +1,244 @@ +#ifndef __pfi_h +#define __pfi_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. + */ + +/** + * @file pfi.h + * + * @author Oliver Lohmann + * Andreas Arnez + * Joern Engel + * Frank Haverkamp + * + * @brief libpfi will hold all code to create and process pfi + * images. Definitions made in this file are equaly usable for the + * development host and the target system. + * + * @note This header additionally holds the official definitions for + * the pfi headers. + */ + +#include /* FILE */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Definitions. */ + +#define PFI_HDRVERSION 1 /* current header version */ + +#define PFI_ENOVERSION 1 /* unknown version */ +#define PFI_ENOHEADER 2 /* not a pfi header */ +#define PFI_EINSUFF 3 /* insufficient information */ +#define PFI_EUNDEF 4 /* key not defined */ +#define PFI_ENOMEM 5 /* out of memory */ +#define PFI_EBADTYPE 6 /* bad data type */ +#define PFI_EFILE 7 /* file I/O error: see errno */ +#define PFI_EFILEINVAL 8 /* file format not valid */ +#define PFI_EINVAL 9 /* invalid parameter */ +#define PFI_ERANGE 10 /* invalid range */ +#define PFI_EMODE 11 /* expecting other mode in this header */ +#define PFI_DATA_START 12 /* data section starts */ +#define PFI_EMAX 13 /* should be always larger as the largest + error code */ + +#define PFI_LABEL_LEN 64 /* This is the maximum length for a + PFI header label */ +#define PFI_KEYWORD_LEN 32 /* This is the maximum length for an + entry in the mode and type fields */ + +#define PFI_UBI_MAX_VOLUMES 128 +#define PFI_UBI_VOL_NAME_LEN 127 + +/** + * @brief The pfi header allows to set flags which influence the flashing + * behaviour. + */ +#define PFI_FLAG_PROTECTED 0x00000001 + + +/** + * @brief Handle to pfi header. Used in most of the functions associated + * with pfi file handling. + */ +typedef struct pfi_header *pfi_header; + + +/** + * @brief Initialize a pfi header object. + * + * @param head Pointer to handle. This function allocates memory + * for this data structure. + * @return 0 on success, otherwise: + * PFI_ENOMEM : no memory available for the handle. + */ +int pfi_header_init (pfi_header *head); + + +/** + * @brief Destroy a pfi header object. + * + * @param head handle. head is invalid after calling this function. + * @return 0 always. + */ +int pfi_header_destroy (pfi_header *head); + + +/** + * @brief Add a key/value pair to a pfi header object. + * + * @param head handle. + * @param key pointer to key string. Must be 0 terminated. + * @param value pointer to value string. Must be 0 terminated. + * @return 0 on success, otherwise: + * PFI_EUNDEF : key was not found. + * PFI_ENOMEM : no memory available for the handle. + * PFI_EBADTYPE : value is not an hex string. This happens + * when the key stores an integer and the + * new value is not convertable e.g. not in + * 0xXXXXXXXX format. + */ +int pfi_header_setvalue (pfi_header head, + const char *key, const char *value); + + +/** + * @brief Add a key/value pair to a pfi header object. Provide the + * value as a number. + * + * @param head handle. + * @param key pointer to key string. Must be 0 terminated. + * @param value value to set. + * @return 0 on success, otherwise: + * PFI_EUNDEF : key was not found. + * PFI_EBADTYPE : value is not a string. This happens + * when the key stores a string. + */ +int pfi_header_setnumber (pfi_header head, + const char *key, uint32_t value); + + +/** + * @brief For a given key, return the numerical value stored in a + * pfi header object. + * + * @param head handle. + * @param key pointer to key string. Must be 0 terminated. + * @param value pointer to value. + * @return 0 on success, otherwise: + * PFI_EUNDEF : key was not found. + * PFI_EBADTYPE : stored value is not an integer but a string. + */ +int pfi_header_getnumber (pfi_header head, + const char *key, uint32_t *value); + + +static inline uint32_t +pfi_getnumber(pfi_header head, const char *key) +{ + uint32_t value; + pfi_header_getnumber(head, key, &value); + return value; +} + +/** + * @brief For a given key, return the string value stored in a pfi + * header object. + * + * @param head handle. + * @param key pointer to key string. Must be 0 terminated. + * @param value pointer to value string. Memory must be allocated by the user. + * @return 0 on success, otherwise: + * PFI_EUNDEF : key was not found. + * PFI_EBADTYPE : stored value is not a string but an integer. + */ +int pfi_header_getstring (pfi_header head, + const char *key, char *value, size_t size); + + +/** + * @brief Write a pfi header object into a given file. + * + * @param out output stream. + * @param head handle. + * @return 0 on success, error values otherwise: + * PFI_EINSUFF : not all mandatory fields are filled. + * PFI_ENOHEADER : wrong header version or magic number. + * -E* : see . + */ +int pfi_header_write (FILE *out, pfi_header head); + + +/** + * @brief Read a pfi header object from a given file. + * + * @param in input stream. + * @param head handle. + * @return 0 on success, error values otherwise: + * PFI_ENOVERSION: unknown header version. + * PFI_EFILE : cannot read enough data. + * PFI_ENOHEADER : wrong header version or magic number. + * -E* : see . + * + * If the header verification returned success the user can assume that + * all mandatory fields for a particular version are accessible. Checking + * the return code when calling the get-function for those keys is not + * required in those cases. For optional fields the checking must still be + * done. + */ +int pfi_header_read (FILE *in, pfi_header head); + + +/** + * @brief Display a pfi header in human-readable form. + * + * @param out output stream. + * @param head handle. + * @return always 0. + * + * @note Prints out that it is not implemented and whom you should + * contact if you need it urgently!. + */ +int pfi_header_dump (FILE *out, pfi_header head); + + +/* + * @brief Iterates over a stream of pfi files. The iterator function + * must advance the file pointer in FILE *in to the next pfi + * header. Function exists on feof(in). + * + * @param in input file descriptor, must be open and valid. + * @param func iterator function called when pfi header could be + * read and was validated. The function must return 0 on + * success. + * @return See pfi_header_init and pfi_header_read. + * PFI_EINVAL : func is not valid + * 0 ok. + */ +typedef int (* pfi_read_func)(FILE *in, pfi_header hdr, void *priv_data); + +int pfi_read (FILE *in, pfi_read_func func, void *priv_data); + + +#ifdef __cplusplus +} +#endif + +#endif /* __pfi_h */ diff --git a/ubi-utils/src/pfi2bin.c b/ubi-utils/src/pfi2bin.c new file mode 100644 index 0000000..a8c76a3 --- /dev/null +++ b/ubi-utils/src/pfi2bin.c @@ -0,0 +1,682 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Oliver Lohmann + * + * Convert a PFI file (partial flash image) into a plain binary file. + * This tool can be used to prepare the data to be burned into flash + * chips in a manufacturing step where the flashes are written before + * being soldered onto the hardware. For NAND images another step is + * required to add the right OOB data to the binary image. + * + * 1.3 Removed argp because we want to use uClibc. + * 1.4 Minor cleanups + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "config.h" +#include "list.h" +#include "error.h" +#include "reader.h" +#include "peb.h" +#include "crc32.h" + +#define PROGRAM_VERSION "1.4" + +#define MAX_FNAME 255 +#define DEFAULT_ERASE_COUNT 0 /* Hmmm.... Perhaps */ +#define ERR_BUF_SIZE 1024 + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) + +static uint32_t crc32_table[256]; +static char err_buf[ERR_BUF_SIZE]; + +/* + * Data used to buffer raw blocks which have to be + * located at a specific point inside the generated RAW file + */ + +typedef enum action_t { + ACT_NOTHING = 0x00000000, + ACT_RAW = 0x00000001, +} action_t; + +static const char copyright [] __attribute__((unused)) = + "(c) Copyright IBM Corp 2006\n"; + +static char doc[] = "\nVersion: " PROGRAM_VERSION "\n" + "pfi2bin - a tool to convert PFI files into binary images.\n"; + +static const char *optionsstr = +" Common settings:\n" +" -c, --copyright\n" +" -v, --verbose Print more information.\n" +"\n" +" Input:\n" +" -j, --platform=pdd-file PDD information which contains the card settings.\n" +"\n" +" Output:\n" +" -o, --output=filename Outputfile, default: stdout.\n" +"\n" +" -?, --help Give this help list\n" +" --usage Give a short usage message\n" +" -V, --version Print program version\n"; + +static const char *usage = +"Usage: pfi2bin [-cv?V] [-j pdd-file] [-o filename] [--copyright]\n" +" [--verbose] [--platform=pdd-file] [--output=filename] [--help]\n" +" [--usage] [--version] pfifile\n"; + +struct option long_options[] = { + { .name = "copyright", .has_arg = 0, .flag = NULL, .val = 'c' }, + { .name = "verbose", .has_arg = 0, .flag = NULL, .val = 'v' }, + { .name = "platform", .has_arg = 1, .flag = NULL, .val = 'j' }, + { .name = "output", .has_arg = 1, .flag = NULL, .val = 'o' }, + { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' }, + { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 }, + { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, + { NULL, 0, NULL, 0} +}; + +typedef struct io { + FILE* fp_pdd; /* a FilePointer to the PDD data */ + FILE* fp_pfi; /* a FilePointer to the PFI input stream */ + FILE* fp_out; /* a FilePointer to the output stream */ +} *io_t; + +typedef struct myargs { + /* common settings */ + action_t action; + int verbose; + const char *f_in_pfi; + const char *f_in_pdd; + const char *f_out; + + /* special stuff needed to get additional arguments */ + char *arg1; + char **options; /* [STRING...] */ +} myargs; + +static int +parse_opt(int argc, char **argv, myargs *args) +{ + while (1) { + int key; + + key = getopt_long(argc, argv, "cvj:o:?V", long_options, NULL); + if (key == -1) + break; + + switch (key) { + /* common settings */ + case 'v': /* --verbose= */ + args->verbose = 1; + break; + + case 'c': /* --copyright */ + fprintf(stderr, "%s\n", copyright); + exit(0); + break; + + case 'j': /* --platform */ + args->f_in_pdd = optarg; + break; + + case 'o': /* --output */ + args->f_out = optarg; + break; + + case '?': /* help */ + printf("pfi2bin [OPTION...] pfifile\n"); + printf("%s", doc); + printf("%s", optionsstr); + printf("\nReport bugs to %s\n", + PACKAGE_BUGREPORT); + exit(0); + break; + + case 'V': + printf("%s\n", PROGRAM_VERSION); + exit(0); + break; + + default: + printf("%s", usage); + exit(-1); + } + } + + if (optind < argc) + args->f_in_pfi = argv[optind++]; + + return 0; +} + + +static size_t +byte_to_blk(size_t byte, size_t blk_size) +{ + return (byte % blk_size) == 0 + ? byte / blk_size + : byte / blk_size + 1; +} + + + + +/** + * @precondition IO: File stream points to first byte of RAW data. + * @postcondition IO: File stream points to first byte of next + * or EOF. + */ +static int +memorize_raw_eb(pfi_raw_t pfi_raw, pdd_data_t pdd, list_t *raw_pebs, + io_t io) +{ + int rc = 0; + uint32_t i; + + size_t read, to_read, eb_num; + size_t bytes_left; + list_t pebs = *raw_pebs; + peb_t peb = NULL; + + long old_file_pos = ftell(io->fp_pfi); + for (i = 0; i < pfi_raw->starts_size; i++) { + bytes_left = pfi_raw->data_size; + rc = fseek(io->fp_pfi, old_file_pos, SEEK_SET); + if (rc != 0) + goto err; + + eb_num = byte_to_blk(pfi_raw->starts[i], pdd->eb_size); + while (bytes_left) { + to_read = MIN(bytes_left, pdd->eb_size); + rc = peb_new(eb_num++, pdd->eb_size, &peb); + if (rc != 0) + goto err; + read = fread(peb->data, 1, to_read, io->fp_pfi); + if (read != to_read) { + rc = -EIO; + goto err; + } + pebs = append_elem(peb, pebs); + bytes_left -= read; + } + + } + *raw_pebs = pebs; + return 0; +err: + pebs = remove_all((free_func_t)&peb_free, pebs); + return rc; +} + +static int +convert_ubi_volume(pfi_ubi_t ubi, pdd_data_t pdd, list_t raw_pebs, + struct ubi_vtbl_record *vol_tab, + size_t *ebs_written, io_t io) +{ + int rc = 0; + uint32_t i, j; + peb_t raw_peb; + peb_t cmp_peb; + ubi_info_t u; + size_t leb_total = 0; + uint8_t vol_type; + + switch (ubi->type) { + case pfi_ubi_static: + vol_type = UBI_VID_STATIC; break; + case pfi_ubi_dynamic: + vol_type = UBI_VID_DYNAMIC; break; + default: + vol_type = UBI_VID_DYNAMIC; + } + + rc = peb_new(0, 0, &cmp_peb); + if (rc != 0) + goto err; + + long old_file_pos = ftell(io->fp_pfi); + for (i = 0; i < ubi->ids_size; i++) { + rc = fseek(io->fp_pfi, old_file_pos, SEEK_SET); + if (rc != 0) + goto err; + rc = ubigen_create(&u, ubi->ids[i], vol_type, + pdd->eb_size, DEFAULT_ERASE_COUNT, + ubi->alignment, UBI_VERSION, + pdd->vid_hdr_offset, 0, ubi->data_size, + io->fp_pfi, io->fp_out); + if (rc != 0) + goto err; + + rc = ubigen_get_leb_total(u, &leb_total); + if (rc != 0) + goto err; + + j = 0; + while(j < leb_total) { + cmp_peb->num = *ebs_written; + raw_peb = is_in((cmp_func_t)peb_cmp, cmp_peb, + raw_pebs); + if (raw_peb) { + rc = peb_write(io->fp_out, raw_peb); + } + else { + rc = ubigen_write_leb(u, NO_ERROR); + j++; + } + if (rc != 0) + goto err; + (*ebs_written)++; + } + /* memorize volume table entry */ + rc = ubigen_set_lvol_rec(u, ubi->size, + ubi->names[i], + (void*) &vol_tab[ubi->ids[i]]); + if (rc != 0) + goto err; + ubigen_destroy(&u); + } + + peb_free(&cmp_peb); + return 0; + +err: + peb_free(&cmp_peb); + ubigen_destroy(&u); + return rc; +} + + +static FILE* +my_fmemopen (void *buf, size_t size, const char *opentype) +{ + FILE* f; + size_t ret; + + assert(strcmp(opentype, "r") == 0); + + f = tmpfile(); + ret = fwrite(buf, 1, size, f); + rewind(f); + + return f; +} + +/** + * @brief Builds a UBI volume table from a volume entry list. + * @return 0 On success. + * else Error. + */ +static int +write_ubi_volume_table(pdd_data_t pdd, list_t raw_pebs, + struct ubi_vtbl_record *vol_tab, size_t vol_tab_size, + size_t *ebs_written, io_t io) +{ + int rc = 0; + ubi_info_t u; + peb_t raw_peb; + peb_t cmp_peb; + size_t leb_size, leb_total, j = 0; + uint8_t *ptr = NULL; + FILE* fp_leb = NULL; + int vt_slots; + size_t vol_tab_size_limit; + + rc = peb_new(0, 0, &cmp_peb); + if (rc != 0) + goto err; + + /* @FIXME: Artem creates one volume with 2 LEBs. + * IMO 2 volumes would be more convenient. In order + * to get 2 reserved LEBs from ubigen, I have to + * introduce this stupid mechanism. Until no final + * decision of the VTAB structure is made... Good enough. + */ + rc = ubigen_create(&u, UBI_LAYOUT_VOLUME_ID, UBI_VID_DYNAMIC, + pdd->eb_size, DEFAULT_ERASE_COUNT, + 1, UBI_VERSION, + pdd->vid_hdr_offset, UBI_COMPAT_REJECT, + vol_tab_size, stdin, io->fp_out); + /* @FIXME stdin for fp_in is a hack */ + if (rc != 0) + goto err; + rc = ubigen_get_leb_size(u, &leb_size); + if (rc != 0) + goto err; + ubigen_destroy(&u); + + /* + * The number of supported volumes is restricted by the eraseblock size + * and by the UBI_MAX_VOLUMES constant. + */ + vt_slots = leb_size / UBI_VTBL_RECORD_SIZE; + if (vt_slots > UBI_MAX_VOLUMES) + vt_slots = UBI_MAX_VOLUMES; + vol_tab_size_limit = vt_slots * UBI_VTBL_RECORD_SIZE; + + ptr = (uint8_t*) malloc(leb_size * sizeof(uint8_t)); + if (ptr == NULL) + goto err; + + memset(ptr, 0xff, leb_size); + memcpy(ptr, vol_tab, vol_tab_size_limit); + fp_leb = my_fmemopen(ptr, leb_size, "r"); + + rc = ubigen_create(&u, UBI_LAYOUT_VOLUME_ID, UBI_VID_DYNAMIC, + pdd->eb_size, DEFAULT_ERASE_COUNT, + 1, UBI_VERSION, pdd->vid_hdr_offset, + UBI_COMPAT_REJECT, leb_size * UBI_LAYOUT_VOLUME_EBS, + fp_leb, io->fp_out); + if (rc != 0) + goto err; + rc = ubigen_get_leb_total(u, &leb_total); + if (rc != 0) + goto err; + + long old_file_pos = ftell(fp_leb); + while(j < leb_total) { + rc = fseek(fp_leb, old_file_pos, SEEK_SET); + if (rc != 0) + goto err; + + cmp_peb->num = *ebs_written; + raw_peb = is_in((cmp_func_t)peb_cmp, cmp_peb, + raw_pebs); + if (raw_peb) { + rc = peb_write(io->fp_out, raw_peb); + } + else { + rc = ubigen_write_leb(u, NO_ERROR); + j++; + } + + if (rc != 0) + goto err; + (*ebs_written)++; + } + +err: + free(ptr); + peb_free(&cmp_peb); + ubigen_destroy(&u); + fclose(fp_leb); + return rc; +} + +static int +write_remaining_raw_ebs(pdd_data_t pdd, list_t raw_blocks, size_t *ebs_written, + FILE* fp_out) +{ + int rc = 0; + uint32_t j, delta; + list_t ptr; + peb_t empty_eb, peb; + + /* create an empty 0xff EB (for padding) */ + rc = peb_new(0, pdd->eb_size, &empty_eb); + + foreach(peb, ptr, raw_blocks) { + if (peb->num < *ebs_written) { + continue; /* omit blocks which + are already passed */ + } + + if (peb->num < *ebs_written) { + err_msg("eb_num: %d\n", peb->num); + err_msg("Bug: This should never happen. %d %s", + __LINE__, __FILE__); + goto err; + } + + delta = peb->num - *ebs_written; + if (((delta + *ebs_written) * pdd->eb_size) > pdd->flash_size) { + err_msg("RAW block outside of flash_size."); + goto err; + } + for (j = 0; j < delta; j++) { + rc = peb_write(fp_out, empty_eb); + if (rc != 0) + goto err; + (*ebs_written)++; + } + rc = peb_write(fp_out, peb); + if (rc != 0) + goto err; + (*ebs_written)++; + } + +err: + peb_free(&empty_eb); + return rc; +} + +static int +init_vol_tab(struct ubi_vtbl_record **vol_tab, size_t *vol_tab_size) +{ + uint32_t crc; + size_t i; + struct ubi_vtbl_record* res = NULL; + + *vol_tab_size = UBI_MAX_VOLUMES * UBI_VTBL_RECORD_SIZE; + + res = (struct ubi_vtbl_record*) calloc(1, *vol_tab_size); + if (vol_tab == NULL) { + return -ENOMEM; + } + + for (i = 0; i < UBI_MAX_VOLUMES; i++) { + crc = clc_crc32(crc32_table, UBI_CRC32_INIT, + &(res[i]), UBI_VTBL_RECORD_SIZE_CRC); + res[i].crc = cpu_to_be32(crc); + } + + *vol_tab = res; + return 0; +} + +static int +create_raw(io_t io) +{ + int rc = 0; + size_t ebs_written = 0; /* eraseblocks written already... */ + size_t vol_tab_size; + list_t ptr; + + list_t pfi_raws = mk_empty(); /* list of raw sections from a pfi */ + list_t pfi_ubis = mk_empty(); /* list of ubi sections from a pfi */ + list_t raw_pebs = mk_empty(); /* list of raw eraseblocks */ + + struct ubi_vtbl_record *vol_tab = NULL; + pdd_data_t pdd = NULL; + + rc = init_vol_tab (&vol_tab, &vol_tab_size); + if (rc != 0) { + err_msg("Cannot initialize volume table."); + goto err; + } + + rc = read_pdd_data(io->fp_pdd, &pdd, + err_buf, ERR_BUF_SIZE); + if (rc != 0) { + err_msg("Cannot read necessary pdd_data: %s rc: %d", + err_buf, rc); + goto err; + } + + rc = read_pfi_headers(&pfi_raws, &pfi_ubis, io->fp_pfi, + err_buf, ERR_BUF_SIZE); + if (rc != 0) { + err_msg("Cannot read pfi header: %s rc: %d", + err_buf, rc); + goto err; + } + + pfi_raw_t pfi_raw; + foreach(pfi_raw, ptr, pfi_raws) { + rc = memorize_raw_eb(pfi_raw, pdd, &raw_pebs, + io); + if (rc != 0) { + err_msg("Cannot create raw_block in mem. rc: %d\n", + rc); + goto err; + } + } + + pfi_ubi_t pfi_ubi; + foreach(pfi_ubi, ptr, pfi_ubis) { + rc = convert_ubi_volume(pfi_ubi, pdd, raw_pebs, + vol_tab, &ebs_written, io); + if (rc != 0) { + err_msg("Cannot convert UBI volume. rc: %d\n", rc); + goto err; + } + } + + rc = write_ubi_volume_table(pdd, raw_pebs, vol_tab, vol_tab_size, + &ebs_written, io); + if (rc != 0) { + err_msg("Cannot write UBI volume table. rc: %d\n", rc); + goto err; + } + + rc = write_remaining_raw_ebs(pdd, raw_pebs, &ebs_written, io->fp_out); + if (rc != 0) + goto err; + + if (io->fp_out != stdout) + info_msg("Physical eraseblocks written: %8d\n", ebs_written); +err: + free(vol_tab); + pfi_raws = remove_all((free_func_t)&free_pfi_raw, pfi_raws); + pfi_ubis = remove_all((free_func_t)&free_pfi_ubi, pfi_ubis); + raw_pebs = remove_all((free_func_t)&peb_free, raw_pebs); + free_pdd_data(&pdd); + return rc; +} + + +/* ------------------------------------------------------------------------- */ +static void +open_io_handle(myargs *args, io_t io) +{ + /* set PDD input */ + io->fp_pdd = fopen(args->f_in_pdd, "r"); + if (io->fp_pdd == NULL) { + err_sys("Cannot open: %s", args->f_in_pdd); + } + + /* set PFI input */ + io->fp_pfi = fopen(args->f_in_pfi, "r"); + if (io->fp_pfi == NULL) { + err_sys("Cannot open PFI input file: %s", args->f_in_pfi); + } + + /* set output prefix */ + if (strcmp(args->f_out,"") == 0) + io->fp_out = stdout; + else { + io->fp_out = fopen(args->f_out, "wb"); + if (io->fp_out == NULL) { + err_sys("Cannot open output file: %s", args->f_out); + } + } +} + +static void +close_io_handle(io_t io) +{ + if (fclose(io->fp_pdd) != 0) { + err_sys("Cannot close PDD file."); + } + if (fclose(io->fp_pfi) != 0) { + err_sys("Cannot close PFI file."); + } + if (io->fp_out != stdout) { + if (fclose(io->fp_out) != 0) { + err_sys("Cannot close output file."); + } + } + + io->fp_pdd = NULL; + io->fp_pfi = NULL; + io->fp_out = NULL; +} + +int +main(int argc, char *argv[]) +{ + int rc = 0; + + ubigen_init(); + init_crc32_table(crc32_table); + + struct io io = {NULL, NULL, NULL}; + myargs args = { + .action = ACT_RAW, + .verbose = 0, + + .f_in_pfi = "", + .f_in_pdd = "", + .f_out = "", + + /* arguments */ + .arg1 = NULL, + .options = NULL, + }; + + /* parse arguments */ + parse_opt(argc, argv, &args); + + if (strcmp(args.f_in_pfi, "") == 0) { + err_quit("No PFI input file specified!"); + } + + if (strcmp(args.f_in_pdd, "") == 0) { + err_quit("No PDD input file specified!"); + } + + open_io_handle(&args, &io); + + info_msg("[ Creating RAW..."); + rc = create_raw(&io); + if (rc != 0) { + err_msg("Creating RAW failed."); + goto err; + } + +err: + close_io_handle(&io); + if (rc != 0) { + remove(args.f_out); + } + + return rc; +} diff --git a/ubi-utils/src/pfiflash.c b/ubi-utils/src/pfiflash.c new file mode 100644 index 0000000..754fe33 --- /dev/null +++ b/ubi-utils/src/pfiflash.c @@ -0,0 +1,264 @@ +/* + * 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. + * + * 1.1 fixed output to stderr and stdout in logfile mode. + * 1.2 updated. + * 1.3 removed argp parsing to be able to use uClib. + * 1.4 Minor cleanups. + * 1.5 Forgot to delete raw block before updating it. + * 1.6 Migrated to new libubi. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#undef DEBUG +#include "error.h" +#include "config.h" + +#define PROGRAM_VERSION "1.6" + +static char doc[] = "\nVersion: " PROGRAM_VERSION "\n" + "pfiflash - a tool for updating a controller with PFI files.\n"; + +static const char *optionsstr = +" Standard options:\n" +" -c, --copyright Print copyright information.\n" +" -l, --logfile= Write a logfile to .\n" +" -v, --verbose Be verbose during program execution.\n" +"\n" +" Process options:\n" +" -C, --complete Execute a complete system update. Updates both\n" +" sides.\n" +" -p, --pdd-update= Specify the pdd-update algorithm. is either\n" +" 'keep', 'merge' or 'overwrite'.\n" +" -r, --raw-flash= Flash the raw data. Use the specified mtd device.\n" +" -s, --side= Select the side which shall be updated.\n" +" -x, --compare Only compare on-flash and pfi data, print info if\n" +" an update is neccessary and return appropriate\n" +" error code.\n" +"\n" +" -?, --help Give this help list\n" +" --usage Give a short usage message\n" +" -V, --version Print program version\n"; + +static const char *usage = +"Usage: pfiflash [-cvC?V] [-l ] [-p ] [-r ] [-s ]\n" +" [--copyright] [--logfile=] [--verbose] [--complete]\n" +" [--pdd-update=] [--raw-flash=] [--side=]\n" +" [--compare] [--help] [--usage] [--version] [pfifile]\n"; + +static const char copyright [] __attribute__((unused)) = + "Copyright IBM Corp 2006"; + +struct option long_options[] = { + { .name = "copyright", .has_arg = 0, .flag = NULL, .val = 'c' }, + { .name = "logfile", .has_arg = 1, .flag = NULL, .val = 'l' }, + { .name = "verbose", .has_arg = 0, .flag = NULL, .val = 'v' }, + { .name = "complete", .has_arg = 0, .flag = NULL, .val = 'C' }, + { .name = "pdd-update", .has_arg = 1, .flag = NULL, .val = 'p' }, + { .name = "raw-flash", .has_arg = 1, .flag = NULL, .val = 'r' }, + { .name = "side", .has_arg = 1, .flag = NULL, .val = 's' }, + { .name = "compare", .has_arg = 0, .flag = NULL, .val = 'x' }, + { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' }, + { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 }, + { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, + { NULL, 0, NULL, 0} +}; + +typedef struct myargs { + int verbose; + const char *logfile; + const char *raw_dev; + + pdd_handling_t pdd_handling; + int seqnum; + int compare; + 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 int +parse_opt(int argc, char **argv, myargs *args) +{ + while (1) { + int key; + + key = getopt_long(argc, argv, "cl:vCp:r:s:x?V", + long_options, NULL); + if (key == -1) + break; + + switch (key) { + /* standard options */ + case 'c': + err_msg("%s\n", copyright); + exit(0); + break; + case 'v': + args->verbose = 1; + break; + case 'l': + args->logfile = optarg; + break; + /* process options */ + case 'C': + args->complete = 1; + break; + case 'p': + args->pdd_handling = get_pdd_handling(optarg); + if ((int)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(optarg); + if (args->seqnum < 0) { + err_quit("Unsupported side: %s.\n" + "Supported sides are '0' " + "and '1'\n", optarg); + } + break; + case 'x': + args->compare = 1; + break; + case 'r': + args->raw_dev = optarg; + break; + case '?': /* help */ + err_msg("Usage: pfiflash [OPTION...] [pfifile]"); + err_msg("%s", doc); + err_msg("%s", optionsstr); + err_msg("\nReport bugs to %s\n", + PACKAGE_BUGREPORT); + exit(0); + break; + case 'V': + err_msg("%s", PROGRAM_VERSION); + exit(0); + break; + default: + err_msg("%s", usage); + exit(-1); + + } + } + + if (optind < argc) { + args->fp_in = fopen(argv[optind++], "r"); + if ((args->fp_in) == NULL) { + err_sys("Cannot open PFI file %s for input", + argv[optind]); + } + } + + return 0; +} + +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, + .compare = 0, + .complete = 0, + .logfile = NULL, /* "/tmp/pfiflash.log", */ + .pdd_handling = PDD_KEEP, + .fp_in = stdin, + .raw_dev = NULL, + }; + + parse_opt(argc, argv, &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_with_options(args.fp_in, args.complete, args.seqnum, + args.compare, args.pdd_handling, args.raw_dev, 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("pfiflash: %s\nrc: %d\n", err_buf, rc); + return rc; +} diff --git a/ubi-utils/src/pfiflash.h b/ubi-utils/src/pfiflash.h new file mode 100644 index 0000000..039705d --- /dev/null +++ b/ubi-utils/src/pfiflash.h @@ -0,0 +1,76 @@ +#ifndef __PFIFLASH_H__ +#define __PFIFLASH_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. + */ + +/** + * + * @file pfi.h + * + * @author Oliver Lohmann + * + * @brief The pfiflash library offers an interface for using the + * pfiflash * utility. + */ + +#include /* FILE */ + +#define PFIFLASH_MAX_ERR_BUF_SIZE 1024 + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum pdd_handling_t +{ + PDD_KEEP = 0, + PDD_MERGE, + PDD_OVERWRITE, + PDD_HANDLING_NUM, /* always the last item */ +} pdd_handling_t; /**< Possible PDD handle algorithms. */ + +/** + * @brief Flashes a PFI file to UBI Device 0. + * @param complete [0|1] Do a complete system update. + * @param seqnum Index in a redundant group. + * @param compare [0|1] Compare contents. + * @param pdd_handling The PDD handling algorithm. + * @param rawdev Device to use for raw flashing + * @param err_buf An error buffer. + * @param err_buf_size Size of the error buffer. + */ +int pfiflash_with_options(FILE* pfi, int complete, int seqnum, int compare, + pdd_handling_t pdd_handling, const char* rawdev, + char *err_buf, size_t err_buf_size); + +/** + * @brief Flashes a PFI file to UBI Device 0. + * @param complete [0|1] Do a complete system update. + * @param seqnum Index in a redundant group. + * @param pdd_handling The PDD handling algorithm. + * @param err_buf An error buffer. + * @param err_buf_size Size of the error buffer. + */ +int pfiflash(FILE* pfi, int complete, int seqnum, pdd_handling_t pdd_handling, + char *err_buf, size_t err_buf_size); + +#ifdef __cplusplus +} +#endif + +#endif /* __PFIFLASH_H__ */ diff --git a/ubi-utils/src/pfiflash_error.h b/ubi-utils/src/pfiflash_error.h new file mode 100644 index 0000000..0f27f4a --- /dev/null +++ b/ubi-utils/src/pfiflash_error.h @@ -0,0 +1,75 @@ +#ifndef __PFIFLASH_ERROR_H__ +#define __PFIFLASH_ERROR_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: Drake Dowsett + * Contact: Andreas Arnez + */ + +enum pfiflash_err { + PFIFLASH_ERR_EOF = 1, + PFIFLASH_ERR_FIO, + PFIFLASH_ERR_UBI_OPEN, + PFIFLASH_ERR_UBI_CLOSE, + PFIFLASH_ERR_UBI_MKVOL, + PFIFLASH_ERR_UBI_RMVOL, + PFIFLASH_ERR_UBI_VOL_UPDATE, + PFIFLASH_ERR_UBI_VOL_FOPEN, + PFIFLASH_ERR_UBI_UNKNOWN, + PFIFLASH_ERR_UBI_VID_OOB, + PFIFLASH_ERR_BOOTENV_CREATE, + PFIFLASH_ERR_BOOTENV_READ, + PFIFLASH_ERR_BOOTENV_SIZE, + PFIFLASH_ERR_BOOTENV_WRITE, + PFIFLASH_ERR_PDD_UNKNOWN, + PFIFLASH_ERR_MTD_OPEN, + PFIFLASH_ERR_MTD_CLOSE, + PFIFLASH_ERR_CRC_CHECK, + PFIFLASH_ERR_MTD_ERASE, + PFIFLASH_ERR_COMPARE, + PFIFLASH_CMP_DIFF +}; + +const char *const PFIFLASH_ERRSTR[] = { + "", + "unexpected EOF", + "file I/O error", + "couldn't open UBI", + "couldn't close UBI", + "couldn't make UBI volume %d", + "couldn't remove UBI volume %d", + "couldn't update UBI volume %d", + "couldn't open UBI volume %d", + "unknown UBI operation", + "PFI data contains out of bounds UBI id %d", + "couldn't create bootenv%s", + "couldn't read bootenv", + "couldn't resize bootenv", + "couldn't write bootenv on ubi%d_%d", + "unknown PDD handling algorithm", + "couldn't open MTD device %s", + "couldn't close MTD device %s", + "CRC check failed: given=0x%08x, calculated=0x%08x", + "couldn't erase raw mtd region", + "couldn't compare volumes", + "on-flash data differ from pfi data, update is neccessary" +}; + +#endif /* __PFIFLASH_ERROR_H__ */ diff --git a/ubi-utils/src/reader.c b/ubi-utils/src/reader.c new file mode 100644 index 0000000..0ea8c6d --- /dev/null +++ b/ubi-utils/src/reader.c @@ -0,0 +1,482 @@ +/* + * 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 +#include +#include +#include +#include + +#include "bootenv.h" +#include "reader.h" + +#define __unused __attribute__((unused)) + +/* @FIXME hard coded offsets right now - get them from Artem? */ +#define NAND2048_DEFAULT_VID_HDR_OFF 1984 +#define NAND512_DEFAULT_VID_HDR_OFF 448 +#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) { + + rc = bootenv_get_num(pdd, "flash_page_size", + &(res->flash_page_size)); + if (rc != 0) { + EBUF("Cannot read 'flash_page_size' from pdd."); + goto err; + } + res->flash_type = NAND_FLASH; + + switch (res->flash_page_size) { + case 512: + res->vid_hdr_offset = NAND512_DEFAULT_VID_HDR_OFF; + break; + case 2048: + res->vid_hdr_offset = NAND2048_DEFAULT_VID_HDR_OFF; + break; + default: + EBUF("Unsupported 'flash_page_size' %d.", + res->flash_page_size); + goto err; + } + } + 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 __unused, 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; + size_t size; + + 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_getnumber(pfi_hd, "crc", &(res->crc)); + if (rc != 0) { + EBUF_PFI("Cannot read 'crc' 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, + &size, &(res->starts)); + res->starts_size = size; + + 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 __unused, 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; + size_t size; + + 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_getnumber(pfi_hd, "crc", &(res->crc)); + if (rc != 0) { + EBUF_PFI("Cannot read 'crc' 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, &size, + &(res->ids)); + res->ids_size = size; + 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, &size, + &(tmp_names)); + res->names_size = size; + 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/reader.h b/ubi-utils/src/reader.h new file mode 100644 index 0000000..715e464 --- /dev/null +++ b/ubi-utils/src/reader.h @@ -0,0 +1,87 @@ +#ifndef __READER_H__ +#define __READER_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 + * + * Read Platform Description Data (PDD). + */ + +#include +#include + +#include "pfi.h" +#include "bootenv.h" +#include "list.h" + +typedef enum flash_type_t { + NAND_FLASH = 0, + NOR_FLASH, +} flash_type_t; + +typedef struct pdd_data *pdd_data_t; +typedef struct pfi_raw *pfi_raw_t; +typedef struct pfi_ubi *pfi_ubi_t; + +struct pdd_data { + uint32_t flash_size; + uint32_t flash_page_size; + uint32_t eb_size; + uint32_t vid_hdr_offset; + flash_type_t flash_type; +}; + +struct pfi_raw { + uint32_t data_size; + uint32_t *starts; + uint32_t starts_size; + uint32_t crc; +}; + +struct pfi_ubi { + uint32_t data_size; + uint32_t alignment; + uint32_t *ids; + uint32_t ids_size; + char **names; + uint32_t names_size; + uint32_t size; + enum { pfi_ubi_dynamic, pfi_ubi_static } type; + int curr_seqnum; /* specifies the seqnum taken in an update, + default: 0 (used by pfiflash, ubimirror) */ + uint32_t crc; +}; + +int read_pdd_data(FILE* fp_pdd, pdd_data_t *pdd_data, + char *err_buf, size_t err_buf_size); +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 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); + +/** + * @brief Reads all pfi headers into list structures, separated by + * RAW and UBI sections. + */ +int read_pfi_headers(list_t *pfi_raws, list_t *pfi_ubis, FILE* fp_pfi, + char* err_buf, size_t err_buf_size); +int free_pdd_data(pdd_data_t *pdd_data); +int free_pfi_raw(pfi_raw_t *raw_pfi); +int free_pfi_ubi(pfi_ubi_t *pfi_ubi); + +#endif /* __READER_H__ */ diff --git a/ubi-utils/src/ubiattach.c b/ubi-utils/src/ubiattach.c deleted file mode 100644 index b3d768a..0000000 --- a/ubi-utils/src/ubiattach.c +++ /dev/null @@ -1,205 +0,0 @@ -/* - * Copyright (C) 2007 Nokia Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * 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., 51 - * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -/* - * An utility to attach MTD devices to UBI. - * - * Author: Artem Bityutskiy - */ - -#include -#include -#include -#include -#include - -#include -#include "common.h" - -#define PROGRAM_VERSION "1.0" -#define PROGRAM_NAME "ubiattach" - -/* The variables below are set by command line arguments */ -struct args { - int devn; - int mtdn; - int vidoffs; - const char *node; -}; - -static struct args args = { - .devn = UBI_DEV_NUM_AUTO, - .mtdn = -1, - .vidoffs = 0, - .node = NULL, -}; - -static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION - " - a tool to attach MTD device to UBI."; - -static const char *optionsstr = -"-d, --devn= the number to assign to the newly created UBI device\n" -" (the number is assigned automatically if this is not\n" -" specified\n" -"-m, --mtdn= MTD device number to attach\n" -"-O, --vid-hdr-offset VID header offset (do not specify this unless you\n" -" really know what you do and the optimal defaults will\n" -" be used)\n" -"-h, --help print help message\n" -"-V, --version print program version"; - -static const char *usage = -"Usage: " PROGRAM_NAME " [-m ] [-d ]\n" -"\t\t[--mtdn=] [--devn ]\n" -"Example 1: " PROGRAM_NAME " /dev/ubi_ctrl -m 0 - attach MTD device 0 (mtd0) to UBI\n" -"Example 2: " PROGRAM_NAME " /dev/ubi_ctrl -m 0 -d 3 - attach MTD device 0 (mtd0) to UBI and\n" -" and create UBI device number 3 (ubi3)"; - -static const struct option long_options[] = { - { .name = "devn", .has_arg = 1, .flag = NULL, .val = 'd' }, - { .name = "mtdn", .has_arg = 1, .flag = NULL, .val = 'm' }, - { .name = "vid-hdr-offset", .has_arg = 1, .flag = NULL, .val = 'O' }, - { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' }, - { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, - { NULL, 0, NULL, 0}, -}; - -static int parse_opt(int argc, char * const argv[]) -{ - while (1) { - int key; - char *endp; - - key = getopt_long(argc, argv, "m:d:OhV", long_options, NULL); - if (key == -1) - break; - - switch (key) { - case 'd': - args.devn = strtoul(optarg, &endp, 0); - if (*endp != '\0' || endp == optarg || args.devn < 0) - return errmsg("bad UBI device number: \"%s\"", optarg); - - break; - - case 'm': - args.mtdn = strtoul(optarg, &endp, 0); - if (*endp != '\0' || endp == optarg || args.mtdn < 0) - return errmsg("bad MTD device number: \"%s\"", optarg); - - break; - - case 'o': - args.vidoffs = strtoul(optarg, &endp, 0); - if (*endp != '\0' || endp == optarg || args.vidoffs <= 0) - return errmsg("bad VID header offset: \"%s\"", optarg); - - break; - - case 'h': - fprintf(stderr, "%s\n\n", doc); - fprintf(stderr, "%s\n\n", usage); - fprintf(stderr, "%s\n", optionsstr); - exit(EXIT_SUCCESS); - - case 'V': - fprintf(stderr, "%s\n", PROGRAM_VERSION); - exit(EXIT_SUCCESS); - - case ':': - return errmsg("parameter is missing"); - - default: - fprintf(stderr, "Use -h for help\n"); - return -1; - } - } - - if (optind == argc) - return errmsg("UBI control device name was not specified (use -h for help)"); - else if (optind != argc - 1) - return errmsg("more then one UBI control device specified (use -h for help)"); - - if (args.mtdn == -1) - return errmsg("MTD device number was not specified (use -h for help)"); - - args.node = argv[optind]; - return 0; -} - -int main(int argc, char * const argv[]) -{ - int err; - libubi_t libubi; - struct ubi_info ubi_info; - struct ubi_dev_info dev_info; - struct ubi_attach_request req; - - err = parse_opt(argc, argv); - if (err) - return -1; - - libubi = libubi_open(); - if (libubi == NULL) - return sys_errmsg("cannot open libubi"); - - /* - * Make sure the kernel is fresh enough and this feature is supported. - */ - err = ubi_get_info(libubi, &ubi_info); - if (err) { - sys_errmsg("cannot get UBI information"); - goto out_libubi; - } - - if (ubi_info.ctrl_major == -1) { - errmsg("MTD attach/detach feature is not supported by your kernel"); - goto out_libubi; - } - - req.dev_num = args.devn; - req.mtd_num = args.mtdn; - req.vid_hdr_offset = args.vidoffs; - - err = ubi_attach_mtd(libubi, args.node, &req); - if (err) { - sys_errmsg("cannot attach mtd%d", args.mtdn); - goto out_libubi; - } - - /* Print some information about the new UBI device */ - err = ubi_get_dev_info1(libubi, req.dev_num, &dev_info); - if (err) { - sys_errmsg("cannot get information about newly created UBI device"); - goto out_libubi; - } - - printf("UBI device number %d, total %d LEBs (", dev_info.dev_num, dev_info.total_lebs); - ubiutils_print_bytes(dev_info.total_bytes, 0); - printf("), available %d LEBs (", dev_info.avail_lebs); - ubiutils_print_bytes(dev_info.avail_bytes, 0); - printf("), LEB size "); - ubiutils_print_bytes(dev_info.leb_size, 1); - printf("\n"); - - libubi_close(libubi); - return 0; - -out_libubi: - libubi_close(libubi); - return -1; -} diff --git a/ubi-utils/src/ubicrc32.c b/ubi-utils/src/ubicrc32.c index 5c7a8ba..7e3f045 100644 --- a/ubi-utils/src/ubicrc32.c +++ b/ubi-utils/src/ubicrc32.c @@ -14,12 +14,10 @@ * 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. - */ - -/* - * Calculate CRC32 with UBI start value (0xFFFFFFFF) for a given binary image. * * Author: Oliver Lohmann + * + * Calculate CRC32 with UBI start value for a given binary image. */ #include @@ -28,98 +26,116 @@ #include #include #include +#include #include +#include "config.h" #include "crc32.h" -#include "common.h" #define BUFSIZE 4096 -#define PROGRAM_VERSION "1.2" -#define PROGRAM_NAME "ubicrc32" +const char *argp_program_version = PACKAGE_VERSION; +const char *argp_program_bug_address = PACKAGE_BUGREPORT; +static char doc[] = "\nVersion: " PACKAGE_VERSION "\n" + "ubicrc32 - calculates the UBI CRC32 value and prints it to stdout.\n"; -static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION - " - a tool to calculate CRC32 with UBI start value (0xFFFFFFFF)"; +static const char copyright [] __attribute__((unused)) = + "FIXME: insert license type"; /* FIXME */ -static const char *optionsstr = -"-h, --help print help message\n" -"-V, --version print program version"; -static const char *usage = -"Usage: " PROGRAM_NAME " [-h] [--help]"; +static struct argp_option options[] = { + { name: "copyright", key: 'c', arg: NULL, flags: 0, + doc: "Print copyright information.", + group: 1 }, -static const struct option long_options[] = { - { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' }, - { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, - { NULL, 0, NULL, 0}, + { name: NULL, key: 0, arg: NULL, flags: 0, doc: NULL, group: 0 }, }; -static int parse_opt(int argc, char * const argv[]) +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) { - while (1) { - int key; - - key = getopt_long(argc, argv, "hV", long_options, NULL); - if (key == -1) - break; - - switch (key) { - case 'h': - fprintf(stderr, "%s\n\n", doc); - fprintf(stderr, "%s\n\n", usage); - fprintf(stderr, "%s\n", optionsstr); - exit(EXIT_SUCCESS); - - case 'V': - fprintf(stderr, "%s\n", PROGRAM_VERSION); - exit(EXIT_SUCCESS); - - case ':': - return errmsg("parameter is missing"); - - default: - fprintf(stderr, "Use -h for help\n"); - return -1; + 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; } -int main(int argc, char * const argv[]) -{ - int err = 0; - uint32_t crc = UBI_CRC32_INIT; - char buf[BUFSIZE]; - FILE *fp; - - if (argc > 1) { - fp = fopen(argv[1], "r"); - if (!fp) - return sys_errmsg("cannot open \"%s\"", argv[1]); - } else - fp = stdin; - - err = parse_opt(argc, argv); - if (err) - return err; - - while (!feof(fp)) { - size_t read; - - read = fread(buf, 1, BUFSIZE, fp); - if (ferror(fp)) { - sys_errmsg("cannot read input file"); - err = -1; - goto out_close; +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); } - crc = crc32(crc, buf, read); + crc32 = clc_crc32(crc32_table, crc32, buf, read); } - printf("0x%08x\n", crc); + if (args.fp_in != stdin) { + fclose(args.fp_in); + } -out_close: - if (fp != stdin) - fclose(fp); - return err; + fprintf(stdout, "0x%08x\n", crc32); + return rc; } diff --git a/ubi-utils/src/ubidetach.c b/ubi-utils/src/ubidetach.c deleted file mode 100644 index cd48368..0000000 --- a/ubi-utils/src/ubidetach.c +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright (C) 2007 Nokia Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * 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., 51 - * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -/* - * An utility to delete UBI devices (detach MTD devices from UBI). - * - * Author: Artem Bityutskiy - */ - -#include -#include -#include -#include -#include - -#include -#include "common.h" - -#define PROGRAM_VERSION "1.0" -#define PROGRAM_NAME "ubidetach" - -/* The variables below are set by command line arguments */ -struct args { - int devn; - int mtdn; - const char *node; -}; - -static struct args args = { - .devn = UBI_DEV_NUM_AUTO, - .mtdn = -1, - .node = NULL, -}; - -static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION -" - a tool to remove UBI devices (detach MTD devices from UBI)"; - -static const char *optionsstr = -"-d, --devn= UBI device number to delete\n" -"-m, --mtdn= or altrnatively, MTD device number to detach -\n" -" this will delete corresponding UBI device\n" -"-h, --help print help message\n" -"-V, --version print program version"; - -static const char *usage = -"Usage: " PROGRAM_NAME " [-d ] [-m ]\n" -"\t\t[--devn ] [--mtdn=]\n" -"Example 1: " PROGRAM_NAME " /dev/ubi_ctrl -d 2 - delete UBI device 2 (ubi2)\n" -"Example 2: " PROGRAM_NAME " /dev/ubi_ctrl -m 0 - detach MTD device 0 (mtd0)"; - -static const struct option long_options[] = { - { .name = "devn", .has_arg = 1, .flag = NULL, .val = 'd' }, - { .name = "mtdn", .has_arg = 1, .flag = NULL, .val = 'm' }, - { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' }, - { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, - { NULL, 0, NULL, 0}, -}; - -static int parse_opt(int argc, char * const argv[]) -{ - while (1) { - int key; - char *endp; - - key = getopt_long(argc, argv, "m:d:hV", long_options, NULL); - if (key == -1) - break; - - switch (key) { - case 'd': - args.devn = strtoul(optarg, &endp, 0); - if (*endp != '\0' || endp == optarg || args.devn < 0) - return errmsg("bad UBI device number: \"%s\"", optarg); - - break; - - case 'm': - args.mtdn = strtoul(optarg, &endp, 0); - if (*endp != '\0' || endp == optarg || args.mtdn < 0) - return errmsg("bad MTD device number: \"%s\"", optarg); - - break; - - case 'h': - fprintf(stderr, "%s\n\n", doc); - fprintf(stderr, "%s\n\n", usage); - fprintf(stderr, "%s\n", optionsstr); - exit(EXIT_SUCCESS); - - case 'V': - fprintf(stderr, "%s\n", PROGRAM_VERSION); - exit(EXIT_SUCCESS); - - case ':': - return errmsg("parameter is missing"); - - default: - fprintf(stderr, "Use -h for help\n"); - return -1; - } - } - - if (optind == argc) - return errmsg("UBI control device name was not specified (use -h for help)"); - else if (optind != argc - 1) - return errmsg("more then one UBI control device specified (use -h for help)"); - - if (args.mtdn == -1 && args.devn == -1) - return errmsg("neither MTD nor UBI devices were specified (use -h for help)"); - - if (args.mtdn != -1 && args.devn != -1) - return errmsg("specify either MTD or UBI device (use -h for help)"); - - args.node = argv[optind]; - return 0; -} - -int main(int argc, char * const argv[]) -{ - int err; - libubi_t libubi; - struct ubi_info ubi_info; - - err = parse_opt(argc, argv); - if (err) - return -1; - - libubi = libubi_open(); - if (libubi == NULL) - return sys_errmsg("cannot open libubi"); - - /* - * Make sure the kernel is fresh enough and this feature is supported. - */ - err = ubi_get_info(libubi, &ubi_info); - if (err) { - sys_errmsg("cannot get UBI information"); - goto out_libubi; - } - - if (ubi_info.ctrl_major == -1) { - errmsg("MTD detach/detach feature is not supported by your kernel"); - goto out_libubi; - } - - if (args.devn != -1) { - err = ubi_remove_dev(libubi, args.node, args.devn); - if (err) { - sys_errmsg("cannot remove ubi%d", args.devn); - goto out_libubi; - } - } else { - err = ubi_detach_mtd(libubi, args.node, args.mtdn); - if (err) { - sys_errmsg("cannot detach mtd%d", args.mtdn); - goto out_libubi; - } - } - - libubi_close(libubi); - return 0; - -out_libubi: - libubi_close(libubi); - return -1; -} - diff --git a/ubi-utils/src/ubigen.c b/ubi-utils/src/ubigen.c new file mode 100644 index 0000000..9fcafab --- /dev/null +++ b/ubi-utils/src/ubigen.c @@ -0,0 +1,359 @@ +/* + * 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 + * 1.2 Removed argp because we want to use uClibc. + * 1.3 Minor cleanups + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "ubigen.h" +#include "config.h" + +#define PROGRAM_VERSION "1.3" + +typedef enum action_t { + ACT_NORMAL = 0x00000001, + ACT_BROKEN_UPDATE = 0x00000002, +} action_t; + +static char doc[] = "\nVersion: " PROGRAM_VERSION "\n" + "ubigen - a tool for adding UBI information to a binary input file.\n"; + +static const char *optionsstr = +" Common settings:\n" +" -c, --copyright Print copyright information.\n" +" -d, --debug\n" +" -v, --verbose Print more progress information.\n" +"\n" +" UBI Settings:\n" +" -A, --alignment= Set the alignment size to (default 1).\n" +" Values can be specified as bytes, 'ki' or 'Mi'.\n" +" -B, --blocksize= Set the eraseblock size to (default 128\n" +" KiB).\n" +" Values can be specified as bytes, 'ki' or 'Mi'.\n" +" -E, --erasecount= Set the erase count to (default 0)\n" +" -I, --id= The UBI volume id.\n" +" -O, --offset= Offset from start of an erase block to the UBI\n" +" volume header.\n" +" -T, --type= The UBI volume type:\n" +" 1 = dynamic, 2 = static\n" +" -X, --setver= Set UBI version number to (default 1)\n" +"\n" +" Input/Output:\n" +" -i, --infile= Read input from file.\n" +" -o, --outfile= Write output to file (default is stdout).\n" +"\n" +" Special options:\n" +" -U, --broken-update= Create an ubi image which simulates a broken\n" +" update.\n" +" specifies the logical eraseblock number to\n" +" update.\n" +"\n" +" -?, --help Give this help list\n" +" --usage Give a short usage message\n" +" -V, --version Print program version\n"; + +static const char *usage = +"Usage: ubigen [-cdv?V] [-A ] [-B ] [-E ] [-I ]\n" +" [-O ] [-T ] [-X ] [-i ] [-o ]\n" +" [-U ] [--copyright] [--debug] [--verbose] [--alignment=]\n" +" [--blocksize=] [--erasecount=] [--id=]\n" +" [--offset=] [--type=] [--setver=]\n" +" [--infile=] [--outfile=]\n" +" [--broken-update=] [--help] [--usage] [--version]\n"; + +struct option long_options[] = { + { .name = "copyright", .has_arg = 0, .flag = NULL, .val = 'c' }, + { .name = "debug", .has_arg = 0, .flag = NULL, .val = 'd' }, + { .name = "verbose", .has_arg = 0, .flag = NULL, .val = 'v' }, + { .name = "alignment", .has_arg = 1, .flag = NULL, .val = 'A' }, + { .name = "blocksize", .has_arg = 1, .flag = NULL, .val = 'B' }, + { .name = "erasecount", .has_arg = 1, .flag = NULL, .val = 'E' }, + { .name = "id", .has_arg = 1, .flag = NULL, .val = 'I' }, + { .name = "offset", .has_arg = 1, .flag = NULL, .val = 'O' }, + { .name = "type", .has_arg = 1, .flag = NULL, .val = 'T' }, + { .name = "setver", .has_arg = 1, .flag = NULL, .val = 'X' }, + { .name = "infile", .has_arg = 1, .flag = NULL, .val = 'i' }, + { .name = "outfile", .has_arg = 1, .flag = NULL, .val = 'o' }, + { .name = "broken-update", .has_arg = 1, .flag = NULL, .val = 'U' }, + { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' }, + { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 }, + { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, + { NULL, 0, NULL, 0} +}; + +static const char copyright [] __attribute__((unused)) = + "Copyright IBM Corp 2006"; + +#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 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 int +parse_opt(int argc, char **argv, myargs *args) +{ + int err = 0; + char* endp; + + while (1) { + int key; + + key = getopt_long(argc, argv, "cdvA:B:E:I:O:T:X:i:o:U:?V", + long_options, NULL); + if (key == -1) + break; + + switch (key) { + case 'c': + fprintf(stderr, "%s\n", copyright); + exit(0); + break; + case 'o': /* output */ + args->fp_out = fopen(optarg, "wb"); + if ((args->fp_out) == NULL) { + fprintf(stderr, "Cannot open file %s " + "for output\n", optarg); + exit(1); + } + break; + case 'i': /* input */ + args->fp_in = fopen(optarg, "rb"); + if ((args->fp_in) == NULL) { + fprintf(stderr, "Cannot open file %s " + "for input\n", optarg); + exit(1); + } + break; + case 'v': /* verbose */ + args->verbose = 1; + break; + + case 'B': /* eb_size */ + args->eb_size = + (uint32_t)ustrtoul(optarg, &endp, 0); + CHECK_ENDP("B", endp); + break; + case 'E': /* erasecount */ + args->ec = (uint64_t)strtoul(optarg, &endp, 0); + CHECK_ENDP("E", endp); + break; + case 'I': /* id */ + args->id = (uint16_t)strtoul(optarg, &endp, 0); + CHECK_ENDP("I", endp); + break; + case 'T': /* type */ + args->type = + (uint16_t)strtoul(optarg, &endp, 0); + CHECK_ENDP("T", endp); + break; + case 'X': /* versionnr */ + args->version = + (uint8_t)strtoul(optarg, &endp, 0); + CHECK_ENDP("X", endp); + break; + case 'O': /* offset for volume hdr */ + args->hdr_offset = + (uint32_t) strtoul(optarg, &endp, 0); + CHECK_ENDP("O", endp); + break; + + case 'U': /* broken update */ + args->action = ACT_BROKEN_UPDATE; + args->update_block = + (uint32_t) strtoul(optarg, &endp, 0); + CHECK_ENDP("U", endp); + break; + + case '?': /* help */ + fprintf(stderr, "Usage: ubigen [OPTION...]\n"); + fprintf(stderr, "%s", doc); + fprintf(stderr, "%s", optionsstr); + fprintf(stderr, "\nReport bugs to %s\n", + PACKAGE_BUGREPORT); + exit(0); + break; + + case 'V': + fprintf(stderr, "%s\n", PROGRAM_VERSION); + exit(0); + break; + + default: + fprintf(stderr, "%s", usage); + exit(-1); + } + } + + if (optind < argc) { + if (!args->fp_in) { + args->fp_in = fopen(argv[optind++], "rb"); + if ((args->fp_in) == NULL) { + fprintf(stderr, "Cannot open file %s for " + "input\n", argv[optind]); + exit(1); + } + } + } + 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, "%s", usage); + exit(1); + } + + return 0; +} + + +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 */ + parse_opt(argc, argv, &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/ubigen.h b/ubi-utils/src/ubigen.h new file mode 100644 index 0000000..0f43a46 --- /dev/null +++ b/ubi-utils/src/ubigen.h @@ -0,0 +1,149 @@ +#ifndef __UBIGEN_H__ +#define __UBIGEN_H__ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Frank Haverkamp + * + * An utility to update UBI volumes. + */ + +#include /* FILE */ +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define DEFAULT_BLOCKSIZE (128 * 1024) +#define DEFAULT_PAGESIZE (2*1024) + +#define EUBIGEN_INVALID_TYPE 1 +#define EUBIGEN_INVALID_HDR_OFFSET 2 +#define EUBIGEN_INVALID_ALIGNMENT 3 +#define EUBIGEN_TOO_SMALL_EB 4 +#define EUBIGEN_MAX_ERROR 5 + + +typedef enum action { + NO_ERROR = 0x00000000, + BROKEN_HDR_CRC = 0x00000001, + BROKEN_DATA_CRC = 0x00000002, + BROKEN_DATA_SIZE = 0x00000004, + BROKEN_OMIT_BLK = 0x00000008, + MARK_AS_UPDATE = 0x00000010, +} ubigen_action_t; + +typedef struct ubi_info *ubi_info_t; + +/** + * @brief Initialize the internal CRC32 table. + * @note Necessary because of the used crc32 function in UBI. + * A usage of CRC32, from e.g. zlib will fail. + */ +void ubigen_init(void); + +/** + * @brief Create an ubigen handle. + * @param ... + * @return 0 On sucess. + * else Error. + * @note This parameterlist is ugly. But we have to use + * two big structs and meta information internally, + * filling them would be even uglier. + */ +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); + +/** + * @brief Destroy an ubigen handle. + * @param u Handle to free. + * @return 0 On success. + * else Error. + */ +int ubigen_destroy(ubi_info_t *u); + +/** + * @brief Get number of total logical EBs, necessary for the + * complete storage of data in the handle. + * @param u The handle. + * @return 0 On success. + * else Error. + */ +int ubigen_get_leb_total(ubi_info_t u, size_t* total); + +/** + * @brief Get the size in bytes of one logical EB in the handle. + * @param u The handle. + * @return 0 On success. + * else Error. + */ +int ubigen_get_leb_size(ubi_info_t u, size_t* size); + + +/** + * @brief Write a logical EB (fits exactly into 1 physical EB). + * @param u Handle which holds all necessary data. + * @param action Additional operations which shall be applied on this + * logical eraseblock. Mostly injecting artifical errors. + * @return 0 On success. + * else Error. + */ +int ubigen_write_leb(ubi_info_t u, ubigen_action_t action); + +/** + * @brief Write a complete array of logical eraseblocks at once. + * @param u Handle which holds all necessary data. + * @return 0 On success. + * else Error. + */ +int ubigen_write_complete(ubi_info_t u); + +/** + * @brief Write a single block which is extracted from the + * binary input data. + * @param u Handle which holds all necessary data. + * @param blk Logical eraseblock which shall hold a inc. copy entry + * and a bad data crc. + * @return 0 On success. + * else Error. + */ +int ubigen_write_broken_update(ubi_info_t u, uint32_t blk); + +/** + * @brief Use the current ubi_info data and some additional data + * to set an UBI volume table entry from it. + * @param u Handle which holds some of the necessary data. + * @param res_bytes Number of reserved bytes which is stored in the volume + * table entry. + * @param name A string which shall be used as a volume label. + * @param lvol_r A pointer to a volume table entry. + * @return 0 On success. + * else Error. + */ +int ubigen_set_lvol_rec(ubi_info_t u, size_t reserved_bytes, + const char* name, struct ubi_vtbl_record *lvol_rec); + +#ifdef __cplusplus +} +#endif + +#endif /* __UBIGEN_H__ */ diff --git a/ubi-utils/src/ubimirror.c b/ubi-utils/src/ubimirror.c new file mode 100644 index 0000000..2cc4596 --- /dev/null +++ b/ubi-utils/src/ubimirror.c @@ -0,0 +1,213 @@ +/* + * 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 + * + * 1.2 Removed argp because we want to use uClibc. + * 1.3 Minor cleanups + * 1.4 Migrated to new libubi + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "error.h" +#include "example_ubi.h" +#include "ubimirror.h" + +#define PROGRAM_VERSION "1.4" + +typedef enum action_t { + ACT_NORMAL = 0, + ACT_ARGP_ABORT, + ACT_ARGP_ERR, +} action_t; + +#define ABORT_ARGP do { \ + args->action = ACT_ARGP_ABORT; \ +} while (0) + +#define ERR_ARGP do { \ + args->action = ACT_ARGP_ERR; \ +} while (0) + +#define VOL_ARGS_MAX 2 + +static char doc[] = "\nVersion: " PROGRAM_VERSION "\n" + "ubimirror - mirrors ubi volumes.\n"; + +static const char *optionsstr = +" -c, --copyright Print copyright information.\n" +" -s, --side= Use the side as source.\n" +" -?, --help Give this help list\n" +" --usage Give a short usage message\n" +" -V, --version Print program version\n"; + +static const char *usage = +"Usage: ubimirror [-c?V] [-s ] [--copyright] [--side=]\n" +" [--help] [--usage] [--version] \n"; + +static const char copyright [] __attribute__((unused)) = + "(C) IBM Coorporation 2007"; + +struct option long_options[] = { + { .name = "copyright", .has_arg = 0, .flag = NULL, .val = 'c' }, + { .name = "side", .has_arg = 1, .flag = NULL, .val = 's' }, + { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' }, + { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 }, + { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, + { NULL, 0, NULL, 0} +}; + +typedef struct 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 int +parse_opt(int argc, char **argv, myargs *args) +{ + while (1) { + int key; + + key = getopt_long(argc, argv, "cs:?V", long_options, NULL); + if (key == -1) + break; + + switch (key) { + case 'c': + err_msg("%s", copyright); + ABORT_ARGP; + break; + case 's': + args->side = get_update_side(optarg); + if (args->side < 0) { + err_msg("Unsupported seqnum: %s.\n" + "Supported seqnums are '0' " + "and '1'\n", optarg); + ERR_ARGP; + } + break; + case '?': /* help */ + err_msg("Usage: ubimirror [OPTION...] " + " \n"); + err_msg("%s", doc); + err_msg("%s", optionsstr); + err_msg("\nReport bugs to %s\n", + PACKAGE_BUGREPORT); + exit(0); + break; + case 'V': + err_msg("%s", PROGRAM_VERSION); + exit(0); + break; + default: + err_msg("%s", usage); + exit(-1); + } + } + + while (optind < argc) { + /* only two entries allowed */ + if (args->vol_no >= VOL_ARGS_MAX) { + err_msg("%s", usage); + ERR_ARGP; + } + args->vol[(args->vol_no)++] = argv[optind++]; + } + + return 0; +} + + +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, + }; + + parse_opt(argc, argv, &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/ubimirror.h b/ubi-utils/src/ubimirror.h new file mode 100644 index 0000000..d7ae2ad --- /dev/null +++ b/ubi-utils/src/ubimirror.h @@ -0,0 +1,66 @@ +#ifndef __UBIMIRROR_H__ +#define __UBIMIRROR_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 + * + * An utility to mirror UBI volumes. + */ + +#include + +/** + * @def EUBIMIRROR_SRC_EQ_DST + * @brief Given source volume is also in the set of destination volumes. + */ +#define EUBIMIRROR_SRC_EQ_DST 20 + +/** + * @def EUBIMIRROR_NO_SRC + * @brief The given source volume does not exist. + */ +#define EUBIMIRROR_NO_SRC 21 + +/** + * @def EUBIMIRROR_NO_DST + * @brief One of the given destination volumes does not exist. + */ +#define EUBIMIRROR_NO_DST 22 + +/** + * @brief Mirrors UBI devices from a source device (specified by seqnum) + * to n target devices. + * @param devno Device number used by the UBI operations. + * @param seqnum An index into ids (defines the src_id). + * @param ids An array of ids. + * @param ids_size The number of entries in the ids array. + * @param err_buf A buffer to store verbose error messages. + * @param err_buf_size The size of the error buffer. + * + * @note A seqnum of value < 0 defaults to a seqnum of 0. + * @note A seqnum exceeding the range of ids_size defaults to 0. + * @note An empty ids list results in a empty stmt. + * @pre The UBI volume which shall be used as source volume exists. + * @pre The UBI volumes which are defined as destination volumes exist. + * @post The content of the UBI volume which was defined as source volume + * equals the content of the volumes which were defined as destination. + */ +int ubimirror(uint32_t devno, int seqnum, uint32_t* ids, ssize_t ids_size, + char *err_buf, size_t err_buf_size); + +#endif /* __UBIMIRROR_H__ */ diff --git a/ubi-utils/src/ubimkvol.c b/ubi-utils/src/ubimkvol.c index 75c180d..bff6068 100644 --- a/ubi-utils/src/ubimkvol.c +++ b/ubi-utils/src/ubimkvol.c @@ -19,8 +19,16 @@ /* * An utility to create UBI volumes. * - * Authors: Artem Bityutskiy - * Frank Haverkamp + * Author: Artem B. Bityutskiy + * Frank Haverkamp + * + * 1.0 Initial release + * 1.1 Does not support erase blocks anymore. This is replaced by + * the number of bytes. + * 1.2 Reworked the user-interface to use argp. + * 1.3 Removed argp because we want to use uClibc. + * 1.4 Minor cleanups + * 1.5 Use a different libubi */ #include @@ -28,257 +36,315 @@ #include #include #include +#include +#include #include -#include "common.h" -#define PROGRAM_VERSION "1.6" -#define PROGRAM_NAME "ubimkvol" +#define PROGRAM_VERSION "1.5" -/* The variables below are set by command line arguments */ +/* + * The variables below are set by command line arguments. + */ struct args { + int devn; int vol_id; int vol_type; long long bytes; - int lebs; int alignment; - const char *name; + char *name; int nlen; - const char *node; + char node[256]; int maxavs; + + /* special stuff needed to get additional arguments */ + char *arg1; + char **options; /* [STRING...] */ }; -static struct args args = { +static struct args myargs = { .vol_type = UBI_DYNAMIC_VOLUME, - .bytes = -1, - .lebs = -1, + .devn = -1, + .bytes = 0, .alignment = 1, .vol_id = UBI_VOL_NUM_AUTO, .name = NULL, .nlen = 0, - .node = NULL, .maxavs = 0, }; -static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION - " - a tool to create UBI volumes."; +static int param_sanity_check(struct args *args, libubi_t libubi); + +static char doc[] = "\nVersion: " PROGRAM_VERSION "\n" + "ubinkvol - make UBI Volume.\n"; static const char *optionsstr = -"-a, --alignment= volume alignment (default is 1)\n" -"-n, --vol_id= UBI volume ID, if not specified, the volume ID\n" -" will be assigned automatically\n" -"-N, --name= volume name\n" -"-s, --size= volume size volume size in bytes, kilobytes (KiB)\n" -" or megabytes (MiB)\n" -"-S, --lebs= alternative way to give volume size in logical\n" -" eraseblocks\n" -"-m, --maxavsize set volume size to maximum available size\n" -"-t, --type= volume type (dynamic, static), default is dynamic\n" -"-h, --help print help message\n" -"-V, --version print program version"; +" -a, --alignment= volume alignment (default is 1)\n" +" -d, --devn= UBI device\n" +" -n, --vol_id= UBI volume id, if not specified, the volume ID\n" +" will be assigned automatically\n" +" -N, --name= volume name\n" +" -s, --size= volume size volume size in bytes, kilobytes (KiB)\n" +" or megabytes (MiB)\n" +" -m, --maxavsize set volume size to maximum available size\n" +" -t, --type= volume type (dynamic, static), default is\n" +" dynamic\n" +" -?, --help Give this help list\n" +" --usage Give a short usage message\n" +" -V, --version Print program version\n"; static const char *usage = -"Usage: " PROGRAM_NAME " [-h] [-a ] [-n ] [-N ]\n" -"\t\t\t[-s ] [-S ] [-t ] [-V] [-m]\n" -"\t\t\t[--alignment=][--vol_id=] [--name=]\n" -"\t\t\t[--size=] [--lebs=] [--type=] [--help]\n" -"\t\t\t[--version] [--maxavsize]\n\n" -"Example: " PROGRAM_NAME "/dev/ubi0 -s 20MiB -N config_data - create a 20 Megabytes volume\n" -" named \"config_data\" on UBI device /dev/ubi0."; - -static const struct option long_options[] = { +"Usage: ubimkvol [-?V] [-a ] [-d ] [-n ]\n" +" [-N ] [-s ] [-t ] [-m]\n" +" [--alignment=] [--devn=] [--vol_id=]\n" +" [--name=] [--size=] [--type=] [--help]\n" +" [--usage] [--version] [--maxavsize]\n"; + +struct option long_options[] = { { .name = "alignment", .has_arg = 1, .flag = NULL, .val = 'a' }, - { .name = "vol_id", .has_arg = 1, .flag = NULL, .val = 'n' }, - { .name = "name", .has_arg = 1, .flag = NULL, .val = 'N' }, - { .name = "size", .has_arg = 1, .flag = NULL, .val = 's' }, - { .name = "lebs", .has_arg = 1, .flag = NULL, .val = 'S' }, - { .name = "type", .has_arg = 1, .flag = NULL, .val = 't' }, - { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' }, - { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, + { .name = "devn", .has_arg = 1, .flag = NULL, .val = 'd' }, + { .name = "vol_id", .has_arg = 1, .flag = NULL, .val = 'n' }, + { .name = "name", .has_arg = 1, .flag = NULL, .val = 'N' }, + { .name = "size", .has_arg = 1, .flag = NULL, .val = 's' }, + { .name = "type", .has_arg = 1, .flag = NULL, .val = 't' }, + { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' }, + { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 }, + { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, { .name = "maxavsize", .has_arg = 0, .flag = NULL, .val = 'm' }, - { NULL, 0, NULL, 0}, + { NULL, 0, NULL, 0} }; -static int param_sanity_check(void) +/* + * @brief Parse the arguments passed into the test case. + * + * @param argc The number of arguments + * @param argv The list of arguments + * @param args Pointer to argument structure + * + * @return error + * + */ +static int +parse_opt(int argc, char **argv, struct args *args) { - int len; - - if (args.bytes == -1 && !args.maxavs && args.lebs == -1) - return errmsg("volume size was not specified (use -h for help)"); - - if ((args.bytes != -1 && (args.maxavs || args.lebs != -1)) || - (args.lebs != -1 && (args.maxavs || args.bytes != -1)) || - (args.maxavs && (args.bytes != -1 || args.lebs != -1))) - return errmsg("size specified with more then one option"); - - if (args.name == NULL) - return errmsg("volume name was not specified (use -h for help)"); - - len = strlen(args.name); - if (len > UBI_MAX_VOLUME_NAME) - return errmsg("too long name (%d symbols), max is %d", len, UBI_MAX_VOLUME_NAME); - - return 0; -} + char *endp; -static int parse_opt(int argc, char * const argv[]) -{ while (1) { int key; - char *endp; - key = getopt_long(argc, argv, "a:n:N:s:S:t:hVm", long_options, NULL); + key = getopt_long(argc, argv, "a:d:n:N:s:t:?Vm", long_options, NULL); if (key == -1) break; switch (key) { - case 't': - if (!strcmp(optarg, "dynamic")) - args.vol_type = UBI_DYNAMIC_VOLUME; - else if (!strcmp(optarg, "static")) - args.vol_type = UBI_STATIC_VOLUME; - else - return errmsg("bad volume type: \"%s\"", optarg); - break; - - case 's': - args.bytes = ubiutils_get_bytes(optarg); - if (args.bytes <= 0) - return errmsg("bad volume size: \"%s\"", optarg); - break; - - case 'S': - args.lebs = strtoull(optarg, &endp, 0); - if (endp == optarg || args.lebs <= 0 || *endp != '\0') - return errmsg("bad LEB count: \"%s\"", optarg); - break; - - case 'a': - args.alignment = strtoul(optarg, &endp, 0); - if (*endp != '\0' || endp == optarg || args.alignment <= 0) - return errmsg("bad volume alignment: \"%s\"", optarg); - break; - - case 'n': - args.vol_id = strtoul(optarg, &endp, 0); - if (*endp != '\0' || endp == optarg || args.vol_id < 0) - return errmsg("bad volume ID: " "\"%s\"", optarg); - break; - - case 'N': - args.name = optarg; - args.nlen = strlen(args.name); - break; - - case 'h': - fprintf(stderr, "%s\n\n", doc); - fprintf(stderr, "%s\n\n", usage); - fprintf(stderr, "%s\n", optionsstr); - exit(EXIT_SUCCESS); + case 't': + if (!strcmp(optarg, "dynamic")) + args->vol_type = UBI_DYNAMIC_VOLUME; + else if (!strcmp(optarg, "static")) + args->vol_type = UBI_STATIC_VOLUME; + else { + fprintf(stderr, + "Bad volume type: \"%s\"\n", + optarg); + goto out; + } + break; + case 's': + args->bytes = strtoull(optarg, &endp, 0); + if (endp == optarg || args->bytes < 0) { + fprintf(stderr, + "Bad volume size: \"%s\"\n", + optarg); + goto out; + } + if (endp != '\0') { + if (strcmp(endp, "KiB") == 0) + args->bytes *= 1024; + else if (strcmp(endp, "MiB") == 0) + args->bytes *= 1024*1024; + } + break; + case 'a': + args->alignment = strtoul(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || + args->alignment <= 0) { + fprintf(stderr, "Bad volume alignment: " + "\"%s\"\n", optarg); + goto out; + } + break; + case 'd': /* --devn= */ + args->devn = strtoul(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || + args->devn < 0) { + fprintf(stderr, + "Bad UBI device number: " + "\"%s\"\n", optarg); + goto out; + } + sprintf(args->node, "/dev/ubi%d", args->devn); + break; + case 'n': /* --volid= */ + args->vol_id = strtoul(optarg, &endp, 0); + if (*endp != '\0' || + endp == optarg || + (args->vol_id < 0 && + args->vol_id != UBI_DYNAMIC_VOLUME)) { + fprintf(stderr, "Bad volume ID: " + "\"%s\"\n", optarg); + goto out; + } + break; + case 'N': + args->name = optarg; + args->nlen = strlen(args->name); + break; + + case ':': + fprintf(stderr, "Parameter is missing\n"); + goto out; + + case '?': /* help */ + fprintf(stderr, + "Usage: ubimkvol [OPTION...]\n"); + fprintf(stderr, "%s", doc); + fprintf(stderr, "%s", optionsstr); + fprintf(stderr, "\nReport bugs to %s\n", + PACKAGE_BUGREPORT); + exit(0); + break; + + case 'V': + fprintf(stderr, "%s\n", PROGRAM_VERSION); + exit(0); + break; + + case 'm': + args->maxavs = 1; + break; + + default: + fprintf(stderr, "%s", usage); + exit(-1); + } + } - case 'V': - fprintf(stderr, "%s\n", PROGRAM_VERSION); - exit(EXIT_SUCCESS); + return 0; + out: + return -1; +} - case 'm': - args.maxavs = 1; - break; +static int param_sanity_check(struct args *args, libubi_t libubi) +{ + int err, len; + struct ubi_info ubi; - case ':': - return errmsg("parameter is missing"); + if (args->bytes == 0 && !args->maxavs) { + fprintf(stderr, "Volume size was not specified\n"); + goto out; + } - default: - fprintf(stderr, "Use -h for help\n"); - return -1; - } + if (args->name == NULL) { + fprintf(stderr, "Volume name was not specified\n"); + goto out; } - if (optind == argc) - return errmsg("UBI device name was not specified (use -h for help)"); - else if (optind != argc - 1) - return errmsg("more then one UBI device specified (use -h for help)"); + err = ubi_get_info(libubi, &ubi); + if (err) + return -1; - args.node = argv[optind]; + if (args->devn >= (int)ubi.dev_count) { + fprintf(stderr, "Device %d does not exist\n", args->devn); + goto out; + } - if (param_sanity_check()) - return -1; + len = strlen(args->name); + if (len > UBI_MAX_VOLUME_NAME) { + fprintf(stderr, "Too long name (%d symbols), max is %d\n", + len, UBI_MAX_VOLUME_NAME); + goto out; + } return 0; +out: + errno = EINVAL; + return -1; } int main(int argc, char * const argv[]) { int err; libubi_t libubi; - struct ubi_dev_info dev_info; - struct ubi_vol_info vol_info; struct ubi_mkvol_request req; - err = parse_opt(argc, argv); - if (err) - return err; + err = parse_opt(argc, (char **)argv, &myargs); + if (err) { + fprintf(stderr, "Wrong options ...\n"); + return err == 1 ? 0 : -1; + } - libubi = libubi_open(); - if (!libubi) - return sys_errmsg("cannot open libubi"); + if (myargs.devn == -1) { + fprintf(stderr, "Device number was not specified\n"); + fprintf(stderr, "Use -h option for help\n"); + return -1; + } - err = ubi_node_type(libubi, args.node); - if (err == 2) { - errmsg("\"%s\" is an UBI volume node, not an UBI device node", - args.node); - goto out_libubi; - } else if (err < 0) { - errmsg("\"%s\" is not an UBI device node", args.node); - goto out_libubi; + libubi = libubi_open(); + if (libubi == NULL) { + perror("Cannot open libubi"); + return -1; } - err = ubi_get_dev_info(libubi, args.node, &dev_info); + err = param_sanity_check(&myargs, libubi); if (err) { - sys_errmsg("cannot get information about UBI device \"%s\"", - args.node); + perror("Input parameters check"); + fprintf(stderr, "Use -h option for help\n"); goto out_libubi; } - if (args.maxavs) { - args.bytes = dev_info.avail_bytes; - printf("Set volume size to %lld\n", req.bytes); - } + req.vol_id = myargs.vol_id; + req.alignment = myargs.alignment; - if (args.lebs != -1) { - args.bytes = dev_info.leb_size; - args.bytes -= dev_info.leb_size % args.alignment; - args.bytes *= args.lebs; - } + if (myargs.maxavs) { + struct ubi_dev_info ubi_dev; + + err = ubi_get_dev_info1(libubi, myargs.devn, &ubi_dev); + if (err) { + perror("Can't get UBI device info"); + goto out_libubi; + } + req.bytes = ubi_dev.avail_bytes; + if (!req.bytes) { + fprintf(stderr, "There is no available free space on device!\n"); + goto out_libubi; + } + printf("Setting the volume size to %lld\n", req.bytes); + } else + req.bytes = myargs.bytes; - req.vol_id = args.vol_id; - req.alignment = args.alignment; - req.bytes = args.bytes; - req.vol_type = args.vol_type; - req.name = args.name; + req.vol_type = myargs.vol_type; + req.name = myargs.name; - err = ubi_mkvol(libubi, args.node, &req); + err = ubi_mkvol(libubi, myargs.node, &req); if (err < 0) { - sys_errmsg("cannot UBI create volume"); + perror("Cannot create volume"); + fprintf(stderr, " err=%d\n", err); goto out_libubi; } - args.vol_id = req.vol_id; - - /* Print information about the created device */ - err = ubi_get_vol_info1(libubi, dev_info.dev_num, args.vol_id, &vol_info); - if (err) { - sys_errmsg("cannot get information about newly created UBI volume"); - goto out_libubi; + /* + * This is hacky, but we want to wait until udev has created device + * nodes. There is probably better way do do this, though. + */ + if (system("udevsettle")) { + /* Well, this is to keep GCC silent */ } - printf("Volume ID %d, size %d LEBs (", vol_info.vol_id, vol_info.rsvd_lebs); - ubiutils_print_bytes(vol_info.rsvd_bytes, 0); - printf("), LEB size "); - ubiutils_print_bytes(vol_info.leb_size, 1); - printf(", %s, name \"%s\", alignment %d\n", - req.vol_type == UBI_DYNAMIC_VOLUME ? "dynamic" : "static", - vol_info.name, vol_info.alignment); + /* printf("Created volume %d, %lld bytes, type %s, name %s\n", + vol_id, bytes, vol_type == UBI_DYNAMIC_VOLUME ? + "dynamic" : "static", name); */ + myargs.vol_id = err; libubi_close(libubi); return 0; diff --git a/ubi-utils/src/ubinfo.c b/ubi-utils/src/ubinfo.c deleted file mode 100644 index 185caae..0000000 --- a/ubi-utils/src/ubinfo.c +++ /dev/null @@ -1,409 +0,0 @@ -/* - * Copyright (C) 2007 Nokia Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * 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., 51 - * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -/* - * An utility to get UBI information. - * - * Author: Artem Bityutskiy - */ - -#include -#include -#include -#include -#include - -#include -#include "common.h" - -#define PROGRAM_VERSION "1.0" -#define PROGRAM_NAME "ubinfo" - -/* The variables below are set by command line arguments */ -struct args { - int devn; - int vol_id; - int all; - const char *node; -}; - -static struct args args = { - .vol_id = -1, - .devn = -1, - .all = 0, - .node = NULL, -}; - -static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION - " - a tool to print UBI information."; - -static const char *optionsstr = -"-d, --devn= UBI device number to get information about\n" -"-n, --vol_id= ID of UBI volume to print information about\n" -"-a, --all print information about all devices and volumes,\n" -" or about all volumes if the UBI device was\n" -" specified\n" -"-h, --help print help message\n" -"-V, --version print program version"; - -static const char *usage = -"Usage 1: " PROGRAM_NAME " [-d ] [-n ] [-a] [-h] [-V] [--vol_id=]\n" -"\t\t[--devn ] [--all] [--help] [--version]\n" -"Usage 2: " PROGRAM_NAME " [-a] [-h] [-V] [--all] [--help] [--version]\n" -"Usage 3: " PROGRAM_NAME " [-h] [-V] [--help] [--version]\n\n" -"Example 1: " PROGRAM_NAME " - (no arguments) print general UBI information\n" -"Example 2: " PROGRAM_NAME " -d 1 - print information about UBI device number 1\n" -"Example 3: " PROGRAM_NAME " /dev/ubi0 -a - print information about all volumes of UBI\n" -" device /dev/ubi0\n" -"Example 4: " PROGRAM_NAME " /dev/ubi1_0 - print information about UBI volume /dev/ubi1_0\n" -"Example 5: " PROGRAM_NAME " -a - print all information\n"; - -static const struct option long_options[] = { - { .name = "devn", .has_arg = 1, .flag = NULL, .val = 'd' }, - { .name = "vol_id", .has_arg = 1, .flag = NULL, .val = 'n' }, - { .name = "all", .has_arg = 0, .flag = NULL, .val = 'a' }, - { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' }, - { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, - { NULL, 0, NULL, 0}, -}; - -static int parse_opt(int argc, char * const argv[]) -{ - while (1) { - int key; - char *endp; - - key = getopt_long(argc, argv, "an:d:hV", long_options, NULL); - if (key == -1) - break; - - switch (key) { - case 'a': - args.all = 1; - break; - - case 'n': - args.vol_id = strtoul(optarg, &endp, 0); - if (*endp != '\0' || endp == optarg || args.vol_id < 0) - return errmsg("bad volume ID: " "\"%s\"", optarg); - break; - - case 'd': - args.devn = strtoul(optarg, &endp, 0); - if (*endp != '\0' || endp == optarg || args.devn < 0) - return errmsg("bad UBI device number: \"%s\"", optarg); - - break; - - case 'h': - fprintf(stderr, "%s\n\n", doc); - fprintf(stderr, "%s\n\n", usage); - fprintf(stderr, "%s\n", optionsstr); - exit(EXIT_SUCCESS); - - case 'V': - fprintf(stderr, "%s\n", PROGRAM_VERSION); - exit(EXIT_SUCCESS); - - case ':': - return errmsg("parameter is missing"); - - default: - fprintf(stderr, "Use -h for help\n"); - return -1; - } - } - - if (optind == argc - 1) - args.node = argv[optind]; - else if (optind < argc) - return errmsg("more then one UBI devices specified (use -h for help)"); - - return 0; -} - -static int translate_dev(libubi_t libubi, const char *node) -{ - int err; - - err = ubi_node_type(libubi, node); - if (err == -1) { - if (errno) - return errmsg("unrecognized device node \"%s\"", node); - return errmsg("\"%s\" does not correspond to any UBI device or volume", node); - } - - if (err == 1) { - struct ubi_dev_info dev_info; - - err = ubi_get_dev_info(libubi, node, &dev_info); - if (err) - return sys_errmsg("cannot get information about UBI device \"%s\"", node); - - args.devn = dev_info.dev_num; - } else { - struct ubi_vol_info vol_info; - - err = ubi_get_vol_info(libubi, node, &vol_info); - if (err) - return sys_errmsg("cannot get information about UBI volume \"%s\"", node); - - if (args.vol_id != -1) - return errmsg("both volume character device node (\"%s\") and " - "volume ID (%d) are specify, use only one of them" - "(use -h for help)", node, args.vol_id); - - args.devn = vol_info.dev_num; - args.vol_id = vol_info.vol_id; - } - - return 0; -} - -static int print_vol_info(libubi_t libubi, int dev_num, int vol_id) -{ - int err; - struct ubi_vol_info vol_info; - - err = ubi_get_vol_info1(libubi, dev_num, vol_id, &vol_info); - if (err) - return sys_errmsg("cannot get information about UBI volume %d on ubi%d", - vol_id, dev_num); - - printf("Volume ID: %d (on ubi%d)\n", vol_info.vol_id, vol_info.dev_num); - printf("Type: %s\n", - vol_info.type == UBI_DYNAMIC_VOLUME ? "dynamic" : "static"); - printf("Alignment: %d\n", vol_info.alignment); - - printf("Size: %d LEBs (", vol_info.rsvd_lebs); - ubiutils_print_bytes(vol_info.rsvd_bytes, 0); - printf(")\n"); - - if (vol_info.type == UBI_STATIC_VOLUME) { - printf("Data bytes: "); - ubiutils_print_bytes(vol_info.data_bytes, 1); - } - printf("State: %s\n", vol_info.corrupted ? "corrupted" : "OK"); - printf("Name: %s\n", vol_info.name); - printf("Character device major/minor: %d:%d\n", - vol_info.major, vol_info.minor); - - return 0; -} - -static int print_dev_info(libubi_t libubi, int dev_num, int all) -{ - int i, err, first = 1; - struct ubi_dev_info dev_info; - struct ubi_vol_info vol_info; - - err = ubi_get_dev_info1(libubi, dev_num, &dev_info); - if (err) - return sys_errmsg("cannot get information about UBI device %d", dev_num); - - printf("ubi%d:\n", dev_info.dev_num); - printf("Volumes count: %d\n", dev_info.vol_count); - printf("Logical eraseblock size: %d\n", dev_info.leb_size); - - printf("Total amount of logical eraseblocks: %d (", dev_info.total_lebs); - ubiutils_print_bytes(dev_info.total_bytes, 0); - printf(")\n"); - - printf("Amount of available logical eraseblocks: %d (", dev_info.avail_lebs); - ubiutils_print_bytes(dev_info.avail_bytes, 0); - printf(")\n"); - - printf("Maximum count of volumes %d\n", dev_info.max_vol_count); - printf("Count of bad physical eraseblocks: %d\n", dev_info.bad_count); - printf("Count of reserved physical eraseblocks: %d\n", dev_info.bad_rsvd); - printf("Current maximum erase counter value: %lld\n", dev_info.max_ec); - printf("Minimum input/output unit size: %d bytes\n", dev_info.min_io_size); - printf("Character device major/minor: %d:%d\n", - dev_info.major, dev_info.minor); - - if (dev_info.vol_count == 0) - return 0; - - printf("Present volumes: "); - for (i = dev_info.lowest_vol_num; - i <= dev_info.highest_vol_num; i++) { - err = ubi_get_vol_info1(libubi, dev_info.dev_num, i, &vol_info); - if (err == -1) { - if (errno == ENOENT) - continue; - - return sys_errmsg("libubi failed to probe volume %d on ubi%d", - i, dev_info.dev_num); - } - - if (!first) - printf(", %d", i); - else { - printf("%d", i); - first = 0; - } - } - printf("\n"); - - if (!all) - return 0; - - first = 1; - printf("\n"); - - for (i = dev_info.lowest_vol_num; - i <= dev_info.highest_vol_num; i++) { - if(!first) - printf("-----------------------------------\n"); - err = ubi_get_vol_info1(libubi, dev_info.dev_num, i, &vol_info); - if (err == -1) { - if (errno == ENOENT) - continue; - - return sys_errmsg("libubi failed to probe volume %d on ubi%d", - i, dev_info.dev_num); - } - first = 0; - - err = print_vol_info(libubi, dev_info.dev_num, i); - if (err) - return err; - } - - return 0; -} - -static int print_general_info(libubi_t libubi, int all) -{ - int i, err, first = 1; - struct ubi_info ubi_info; - struct ubi_dev_info dev_info; - - err = ubi_get_info(libubi, &ubi_info); - if (err) - return sys_errmsg("cannot get UBI information"); - - printf("UBI version: %d\n", ubi_info.version); - printf("Count of UBI devices: %d\n", ubi_info.dev_count); - if (ubi_info.ctrl_major != -1) - printf("UBI control device major/minor: %d:%d\n", - ubi_info.ctrl_major, ubi_info.ctrl_minor); - else - printf("UBI control device is not supported by this kernel\n"); - - if (ubi_info.dev_count == 0) - return 0; - - printf("Present UBI devices: "); - for (i = ubi_info.lowest_dev_num; - i <= ubi_info.highest_dev_num; i++) { - err = ubi_get_dev_info1(libubi, i, &dev_info); - if (err == -1) { - if (errno == ENOENT) - continue; - - return sys_errmsg("libubi failed to probe UBI device %d", i); - } - - if (!first) - printf(", ubi%d", i); - else { - printf("ubi%d", i); - first = 0; - } - } - printf("\n"); - - if (!all) - return 0; - - first = 1; - printf("\n"); - - for (i = ubi_info.lowest_dev_num; - i <= ubi_info.highest_dev_num; i++) { - if(!first) - printf("\n===================================\n\n"); - err = ubi_get_dev_info1(libubi, i, &dev_info); - if (err == -1) { - if (errno == ENOENT) - continue; - - return sys_errmsg("libubi failed to probe UBI device %d", i); - } - first = 0; - - err = print_dev_info(libubi, i, all); - if (err) - return err; - } - return 0; -} - -int main(int argc, char * const argv[]) -{ - int err; - libubi_t libubi; - - err = parse_opt(argc, argv); - if (err) - return -1; - - if (!args.node && args.devn != -1) - return errmsg("specify either device number or node file (use -h for help)"); - - libubi = libubi_open(); - if (libubi == NULL) - return sys_errmsg("cannot open libubi"); - - if (args.node) { - /* - * A character device was specified, translate this into UBI - * device number and volume ID. - */ - err = translate_dev(libubi, args.node); - if (err) - goto out_libubi; - } - - if (args.vol_id != -1 && args.devn == -1) { - errmsg("volume ID is specified, but UBI device number is not " - "(use -h for help)\n"); - goto out_libubi; - } - - if (args.devn != -1 && args.vol_id != -1) { - print_vol_info(libubi, args.devn, args.vol_id); - goto out; - } - - if (args.devn == -1 && args.vol_id == -1) - err = print_general_info(libubi, args.all); - else if (args.devn != -1 && args.vol_id == -1) - err = print_dev_info(libubi, args.devn, args.all); - - if (err) - goto out_libubi; - -out: - libubi_close(libubi); - return 0; - -out_libubi: - libubi_close(libubi); - return -1; -} diff --git a/ubi-utils/src/ubinize.c b/ubi-utils/src/ubinize.c deleted file mode 100644 index 47dc571..0000000 --- a/ubi-utils/src/ubinize.c +++ /dev/null @@ -1,549 +0,0 @@ -/* - * Copyright (C) 2008 Nokia Corporation - * 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. - */ - -/* - * Generate UBI images. - * - * Authors: Artem Bityutskiy - * Oliver Lohmann - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include "common.h" - -#define PROGRAM_VERSION "1.5" -#define PROGRAM_NAME "ubinize" - -static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION -" - a tool to generate UBI images. An UBI image may contain one or more UBI " -"volumes which have to be defined in the input configuration ini-file. The " -"ini file defines all the UBI volumes - their characteristics and the and the " -"contents, but it does not define the characteristics of the flash the UBI " -"image is generated for. Instead, the flash characteristics are defined via " -"the command-line options. Note, if not sure about some of the command-line " -"parameters, do not specify them and let the utility to use default values."; - -static const char *optionsstr = -"-o, --output= output file name (default is stdout)\n" -"-p, --peb-size= size of the physical eraseblock of the flash\n" -" this UBI image is created for in bytes,\n" -" kilobytes (KiB), or megabytes (MiB)\n" -" (mandatory parameter)\n" -"-m, --min-io-size= minimum input/output unit size of the flash\n" -" in bytes\n" -"-s, --sub-page-size= minimum input/output unit used for UBI\n" -" headers, e.g. sub-page size in case of NAND\n" -" flash (equivalent to the minimum input/output\n" -" unit size by default)\n" -"-O, --vid-hdr-offset= offset if the VID header from start of the\n" -" physical eraseblock (default is the second\n" -" minimum I/O unit or sub-page, if it was\n" -" specified)\n" -"-e, --erase-counter= the erase counter value to put to EC headers\n" -" (default is 0)\n" -"-x, --ubi-ver= UBI version number to put to EC headers\n" -" (default is 1)\n" -"-v --verbose be verbose\n" -"-h, --help print help message\n" -"-V, --version print program version"; - -static const char *usage = -"Usage: " PROGRAM_NAME " [-o filename] [-h] [-V] [--output=] [--help]\n" -"\t\t[--version] ini-file\n" -"Example: " PROGRAM_NAME "-o fs.raw cfg.ini"; - -static const char *ini_doc = "INI-file format.\n" -"The input configuration ini-file describes all the volumes which have to\n" -"be included to the output UBI image. Each volume is described in its own\n" -"section which may be named arbitrarily. The section consists on\n" -"\"key=value\" pairs, for example:\n\n" -"[jffs2-volume]\n" -"mode=ubi\n" -"image=../jffs2.img\n" -"vol_id=1\n" -"vol_size=30MiB\n" -"vol_type=dynamic\n" -"vol_name=jffs2_volume\n" -"vol_flags=autoresize\n" -"vol_alignment=1\n\n" -"This example configuration file tells the utility to create an UBI image\n" -"with one volume with ID 1, volume size 30MiB, the volume is dynamic, has\n" -"name \"jffs2_volume\", \"autoresize\" volume flag, and alignment 1. The\n" -"\"image=../jffs2.img\" line tells the utility to take the contents of the\n" -"volume from the \"../jffs2.img\" file. The size of the image file has to be\n" -"less or equivalent to the volume size (30MiB). The \"mode=ubi\" line is\n" -"mandatory and just tells that the section describes an UBI volume - other\n" -"section modes may be added in the future.\n" -"Notes:\n" -" * size in vol_size might be specified kilobytes (KiB), megabytes (MiB),\n" -" gigabytes (GiB) or bytes (no modifier);\n" -" * if \"vol_size\" key is absent, the volume size is assumed to be\n" -" equivalent to the size of the image file (defined by \"image\" key);\n" -" * if the \"image\" is absent, the volume is assumed to be empty;\n" -" * volume alignment must not be greater than the logical eraseblock size;\n" -" * one ini file may contain arbitrary number of sections, the utility will\n" -" put all the volumes which are described by these section to the output\n" -" UBI image file."; - -struct option long_options[] = { - { .name = "output", .has_arg = 1, .flag = NULL, .val = 'o' }, - { .name = "peb-size", .has_arg = 1, .flag = NULL, .val = 'p' }, - { .name = "min-io-size", .has_arg = 1, .flag = NULL, .val = 'm' }, - { .name = "sub-page-size", .has_arg = 1, .flag = NULL, .val = 's' }, - { .name = "vid-hdr-offset", .has_arg = 1, .flag = NULL, .val = 'O' }, - { .name = "erase-counter", .has_arg = 1, .flag = NULL, .val = 'e' }, - { .name = "ubi-ver", .has_arg = 1, .flag = NULL, .val = 'x' }, - { .name = "verbose", .has_arg = 1, .flag = NULL, .val = 'v' }, - { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' }, - { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, - { NULL, 0, NULL, 0} -}; - -struct args { - const char *f_in; - const char *f_out; - FILE *fp_out; - int peb_size; - int min_io_size; - int subpage_size; - int vid_hdr_offs; - int ec; - int ubi_ver; - int verbose; - dictionary *dict; -}; - -static struct args args = { - .f_out = NULL, - .peb_size = -1, - .min_io_size = -1, - .subpage_size = -1, - .vid_hdr_offs = 0, - .ec = 0, - .ubi_ver = 1, - .verbose = 0, -}; - -static int parse_opt(int argc, char * const argv[]) -{ - while (1) { - int key; - char *endp; - - key = getopt_long(argc, argv, "o:p:m:s:O:e:x:vhV", long_options, NULL); - if (key == -1) - break; - - switch (key) { - case 'o': - args.fp_out = fopen(optarg, "wb"); - if (!args.fp_out) - return errmsg("cannot open file \"%s\"", optarg); - args.f_out = optarg; - break; - - case 'p': - args.peb_size = ubiutils_get_bytes(optarg); - if (args.peb_size <= 0) - return errmsg("bad physical eraseblock size: \"%s\"", optarg); - break; - - case 'm': - args.min_io_size = ubiutils_get_bytes(optarg); - if (args.min_io_size <= 0) - return errmsg("bad min. I/O unit size: \"%s\"", optarg); - break; - - case 's': - args.subpage_size = ubiutils_get_bytes(optarg); - if (args.subpage_size <= 0) - return errmsg("bad sub-page size: \"%s\"", optarg); - break; - - case 'O': - args.vid_hdr_offs = strtoul(optarg, &endp, 0); - if (endp == optarg || args.vid_hdr_offs < 0) - return errmsg("bad VID header offset: \"%s\"", optarg); - break; - - case 'e': - args.ec = strtoul(optarg, &endp, 0); - if (endp == optarg || args.ec < 0) - return errmsg("bad erase counter value: \"%s\"", optarg); - break; - - case 'x': - args.ubi_ver = strtoul(optarg, &endp, 0); - if (endp == optarg || args.ubi_ver < 0) - return errmsg("bad UBI version: \"%s\"", optarg); - break; - - case 'v': - args.verbose = 1; - break; - - case 'h': - ubiutils_print_text(stderr, doc, 80); - fprintf(stderr, "\n%s\n\n", ini_doc); - fprintf(stderr, "%s\n", usage); - fprintf(stderr, "%s\n", optionsstr); - exit(EXIT_SUCCESS); - - case 'V': - fprintf(stderr, "%s\n", PROGRAM_VERSION); - exit(EXIT_SUCCESS); - - default: - fprintf(stderr, "Use -h for help\n"); - return -1; - } - } - - if (optind == argc) - return errmsg("input PFI file was not specified (use -h for help)"); - - if (optind != argc - 1) - return errmsg("more then one input PFI file was specified (use -h for help)"); - - args.f_in = argv[optind]; - - if (args.peb_size < 0) - return errmsg("physical eraseblock size was not specified (use -h for help)"); - - if (args.min_io_size < 0) - return errmsg("min. I/O unit size was not specified (use -h for help)"); - - if (args.subpage_size < 0) - args.subpage_size = args.min_io_size; - - if (!args.f_out) { - args.f_out = "stdout"; - args.fp_out = stdout; - } - - return 0; -} - -int read_section(const char *sname, struct ubigen_vol_info *vi, - const char **img) -{ - char buf[256]; - const char *p; - - *img = NULL; - - if (strlen(sname) > 128) - return errmsg("too long section name \"%s\"", sname); - - /* Make sure mode is UBI, otherwise ignore this section */ - sprintf(buf, "%s:mode", sname); - p = iniparser_getstring(args.dict, buf, NULL); - if (!p) { - errmsg("\"mode\" key not found in section \"%s\"", sname); - errmsg("the \"mode\" key is mandatory and has to be " - "\"mode=ubi\" if the section describes an UBI volume"); - return -1; - } - - /* If mode is not UBI, skip this section */ - if (strcmp(p, "ubi")) { - verbose(args.verbose, "skip non-ubi section \"%s\"", sname); - return 1; - } - - verbose(args.verbose, "mode=ubi, keep parsing"); - - /* Fetch the name of the volume image file */ - sprintf(buf, "%s:image", sname); - p = iniparser_getstring(args.dict, buf, NULL); - if (p) - *img = p; - - /* Fetch volume id */ - sprintf(buf, "%s:vol_id", sname); - vi->id = iniparser_getint(args.dict, buf, -1); - if (vi->id == -1) - return errmsg("\"vol_id\" key not found in section \"%s\"", sname); - - if (vi->id < 0) - return errmsg("negative volume ID %d", vi->id); - - if (vi->id >= UBI_MAX_VOLUMES) - return errmsg("too high volume ID %d, max. is %d", vi->id, UBI_MAX_VOLUMES); - - verbose(args.verbose, "volume ID: %d", vi->id); - - /* Fetch volume size */ - sprintf(buf, "%s:vol_size", sname); - p = iniparser_getstring(args.dict, buf, NULL); - if (p) { - vi->bytes = ubiutils_get_bytes(p); - if (vi->bytes <= 0) - return errmsg("bad \"vol_size\" key: \"%s\"", p); - - verbose(args.verbose, "volume size: %lld bytes", vi->bytes); - } else { - struct stat st; - - if (!*img) - return errmsg("neither image file (\"image=\") nor volume size (\"vol_size=\") specified"); - - if (stat(*img, &st)) - return sys_errmsg("cannot stat \"%s\"", *img); - - vi->bytes = st.st_size; - - if (vi->bytes == 0) - return errmsg("file \"%s\" referred from section \"%s\" is empty", *img, sname); - - printf(PROGRAM_NAME ": volume size was not specified in" - "section \"%s\", assume ", sname); - ubiutils_print_bytes(vi->bytes, 1); - printf("\n"); - } - - /* Fetch volume type */ - sprintf(buf, "%s:vol_type", sname); - p = iniparser_getstring(args.dict, buf, NULL); - if (!p) { - normsg(": volume type was not specified in " - "section \"%s\", assume \"dynamic\"\n", sname); - vi->type = UBI_VID_DYNAMIC; - } else { - if (!strcmp(p, "static")) - vi->type = UBI_VID_STATIC; - else if (!strcmp(p, "dynamic")) - vi->type = UBI_VID_DYNAMIC; - else - return errmsg("invalid volume type \"%s\"", p); - } - - verbose(args.verbose, "volume type: %s", - vi->type == UBI_VID_DYNAMIC ? "dynamic" : "static"); - - /* Fetch volume name */ - sprintf(buf, "%s:vol_name", sname); - p = iniparser_getstring(args.dict, buf, NULL); - if (!p) - return errmsg("\"vol_name\" key not found in section \"%s\"", sname); - - vi->name = p; - vi->name_len = strlen(p); - if (vi->name_len > UBI_VOL_NAME_MAX) - return errmsg("too long volume name in section \"%s\", max. is %d characters", - vi->name, UBI_VOL_NAME_MAX); - - verbose(args.verbose, "volume name: %s", p); - - /* Fetch volume alignment */ - sprintf(buf, "%s:vol_alignment", sname); - vi->alignment = iniparser_getint(args.dict, buf, -1); - if (vi->alignment == -1) { - normsg("volume alignment was not specified in section " - "\"%s\", assume 1", sname); - vi->alignment = 1; - } else if (vi->id < 0) - return errmsg("negative volume alignement %d", vi->alignment); - - verbose(args.verbose, "volume alignment: %d", vi->alignment); - - /* Fetch volume flags */ - sprintf(buf, "%s:vol_flags", sname); - p = iniparser_getstring(args.dict, buf, NULL); - if (p) { - if (!strcmp(p, "autoresize")) { - verbose(args.verbose, "autoresize flags found"); - vi->flags |= UBI_VTBL_AUTORESIZE_FLG; - } else { - return errmsg("unknown flags \"%s\" in section \"%s\"", p, sname); - } - } - - return 0; -} - -static void init_vol_info(const struct ubigen_info *ui, - struct ubigen_vol_info *vi) -{ - vi->data_pad = ui->leb_size % vi->alignment; - vi->usable_leb_size = ui->leb_size - vi->data_pad; - vi->used_ebs = (vi->bytes + vi->usable_leb_size - 1) / vi->usable_leb_size; - vi->compat = 0; -} - -int main(int argc, char * const argv[]) -{ - int err = -1, sects, i, volumes; - struct ubigen_info ui; - struct ubi_vtbl_record *vtbl; - - err = parse_opt(argc, argv); - if (err) - return -1; - - ubigen_info_init(&ui, args.peb_size, args.min_io_size, - args.subpage_size, args.vid_hdr_offs, - args.ubi_ver, args.ec); - - verbose(args.verbose, "LEB size: %d", ui.leb_size); - verbose(args.verbose, "PEB size: %d", ui.peb_size); - verbose(args.verbose, "min_io_size: %d", ui.min_io_size); - verbose(args.verbose, "VID offset: %d", ui.vid_hdr_offs); - - vtbl = ubigen_create_empty_vtbl(&ui); - if (!vtbl) - goto out; - - args.dict = iniparser_load(args.f_in); - if (!args.dict) { - errmsg("cannot load the input ini file \"%s\"", args.f_in); - goto out_vtbl; - } - - verbose(args.verbose, "loaded the ini-file \"%s\"", args.f_in); - - /* Each section describes one volume */ - sects = iniparser_getnsec(args.dict); - if (sects == -1) { - errmsg("ini-file parsing error (iniparser_getnsec)"); - goto out_dict; - } - - verbose(args.verbose, "count of sections: %d", sects); - if (sects == 0) { - errmsg("no sections found the ini-file \"%s\"", args.f_in); - goto out_dict; - } - - /* - * Skip 2 PEBs at the beginning of the file for the volume table which - * will be written later. - */ - if (fseek(args.fp_out, ui.peb_size * 2, SEEK_SET) == -1) { - errmsg("cannot seek file \"%s\"", args.f_out); - goto out_dict; - } - - for (i = 0; i < sects; i++) { - const char *sname = iniparser_getsecname(args.dict, i); - struct ubigen_vol_info vi; - const char *img = NULL; - struct stat st; - FILE *f; - - if (!sname) { - errmsg("ini-file parsing error (iniparser_getsecname)"); - goto out_dict; - } - - if (args.verbose) - printf("\n"); - verbose(args.verbose, "parsing section \"%s\"", sname); - - err = read_section(sname, &vi, &img); - if (err == -1) - goto out_dict; - if (!err) - volumes += 1; - init_vol_info(&ui, &vi); - - verbose(args.verbose, "adding volume %d", vi.id); - - err = ubigen_add_volume(&ui, &vi, vtbl); - if (err) { - errmsg("cannot add volume for section \"%s\"", sname); - goto out_dict; - } - - if (!img) - continue; - - if (stat(img, &st)) { - sys_errmsg("cannot stat \"%s\"", img); - goto out_dict; - } - - /* - * Make sure the image size is not larger then the volume size. - */ - if (st.st_size > vi.bytes) { - errmsg("error in section \"%s\": size of the image file \"%s\" " - "is %lld, which is larger then the volume size %lld", - sname, img, (long long)st.st_size, vi.bytes); - goto out_dict; - } - - f = fopen(img, "r"); - if (!f) { - sys_errmsg("cannot open \"%s\"", img); - goto out_dict; - } - - verbose(args.verbose, "writing volume %d", vi.id); - verbose(args.verbose, "image file: %s", img); - - err = ubigen_write_volume(&ui, &vi, st.st_size, f, args.fp_out); - fclose(f); - if (err) { - errmsg("cannot write volume for section \"%s\"", sname); - goto out_dict; - } - - if (args.verbose) - printf("\n"); - } - - verbose(args.verbose, "writing layout volume"); - - if (fseek(args.fp_out, 0, SEEK_SET) == -1) { - errmsg("cannot seek file \"%s\"", args.f_out); - goto out_dict; - } - - err = ubigen_write_layout_vol(&ui, vtbl, args.fp_out); - if (err) { - errmsg("cannot write layout volume"); - goto out_dict; - } - - verbose(args.verbose, "done"); - - iniparser_freedict(args.dict); - free(vtbl); - fclose(args.fp_out); - return 0; - -out_dict: - iniparser_freedict(args.dict); -out_vtbl: - free(vtbl); -out: - fclose(args.fp_out); - remove(args.f_out); - return err; -} diff --git a/ubi-utils/src/ubirmvol.c b/ubi-utils/src/ubirmvol.c index c30446a..6dd16ba 100644 --- a/ubi-utils/src/ubirmvol.c +++ b/ubi-utils/src/ubirmvol.c @@ -19,8 +19,13 @@ /* * An utility to remove UBI volumes. * - * Authors: Artem Bityutskiy - * Frank Haverkamp + * Author: Artem B. Bityutskiy + * Frank Haverkamp + * + * 1.1 Reworked the userinterface to use argp. + * 1.2 Removed argp because we want to use uClibc. + * 1.3 Minor cleanups + * 1.4 Use a different libubi */ #include @@ -28,136 +33,190 @@ #include #include #include +#include +#include #include -#include "common.h" -#define PROGRAM_VERSION "1.5" -#define PROGRAM_NAME "ubirmvol" +#define PROGRAM_VERSION "1.4" -/* The variables below are set by command line arguments */ +/* + * The below variables are set by command line options. + */ struct args { + int devn; int vol_id; - const char *node; + char node[256]; + + /* special stuff needed to get additional arguments */ + char *arg1; + char **options; /* [STRING...] */ }; -static struct args args = { +static struct args myargs = { + .devn = -1, .vol_id = -1, - .node = NULL, + + .arg1 = NULL, + .options = NULL, }; -static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION - " - a tool to remove UBI volumes."; +static int param_sanity_check(struct args *args, libubi_t libubi); + +static char doc[] = "\nVersion: " PROGRAM_VERSION "\n" + "ubirmvol - make UBI Volume.\n"; static const char *optionsstr = -" -n, --vol_id= volume ID to remove\n" -" -h, --help print help message\n" -" -V, --version print program version"; +" -d, --devn= UBI device\n" +" -n, --vol_id= UBI volume id, if not specified, the volume ID\n" +" will be assigned automatically\n" +" -?, --help Give this help list\n" +" --usage Give a short usage message\n" +" -V, --version Print program version\n"; static const char *usage = -"Usage: " PROGRAM_NAME " [-n ] [--vol_id=] [-h] [--help]\n\n" -"Example: " PROGRAM_NAME "/dev/ubi0 -n 1 - remove UBI volume 1 from UBI device corresponding\n" -" to the node file /dev/ubi0."; - -static const struct option long_options[] = { - { .name = "vol_id", .has_arg = 1, .flag = NULL, .val = 'n' }, - { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' }, +"Usage: ubirmvol [-?V] [-d ] [-n ] [--devn=]\n" +" [--vol_id=] [--help] [--usage] [--version]\n"; + +struct option long_options[] = { + { .name = "devn", .has_arg = 1, .flag = NULL, .val = 'd' }, + { .name = "vol_id", .has_arg = 1, .flag = NULL, .val = 'n' }, + { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' }, + { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 }, { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, - { NULL, 0, NULL, 0}, + { NULL, 0, NULL, 0} }; -static int param_sanity_check(void) +/* + * @brief Parse the arguments passed into the test case. + * + * @param argc The number of arguments + * @param argv The list of arguments + * @param args Pointer to argument structure + * + * @return error + * + */ +static int +parse_opt(int argc, char **argv, struct args *args) { - if (args.vol_id == -1) { - errmsg("volume ID is was not specified"); - return -1; - } - - return 0; -} + char *endp; -static int parse_opt(int argc, char * const argv[]) -{ while (1) { int key; - char *endp; - key = getopt_long(argc, argv, "n:hV", long_options, NULL); + key = getopt_long(argc, argv, "d:n:?V", long_options, NULL); if (key == -1) break; switch (key) { + case 'd': /* --devn= */ + args->devn = strtoul(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || + args->devn < 0) { + fprintf(stderr, + "Bad UBI device number: " + "\"%s\"\n", optarg); + goto out; + } + sprintf(args->node, "/dev/ubi%d", args->devn); + break; + case 'n': /* --volid= */ + args->vol_id = strtoul(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || + (args->vol_id < 0 && + args->vol_id != UBI_DYNAMIC_VOLUME)) { + fprintf(stderr, "Bad volume ID: " + "\"%s\"\n", optarg); + goto out; + } + break; + case ':': + fprintf(stderr, "Parameter is missing\n"); + goto out; + case '?': /* help */ + fprintf(stderr, + "Usage: ubirmvol [OPTION...]\n"); + fprintf(stderr, "%s", doc); + fprintf(stderr, "%s", optionsstr); + fprintf(stderr, "\nReport bugs to %s\n", + PACKAGE_BUGREPORT); + exit(0); + break; + case 'V': + fprintf(stderr, "%s\n", PROGRAM_VERSION); + exit(0); + break; + default: + fprintf(stderr, "%s", usage); + exit(-1); + } + } - case 'n': - args.vol_id = strtoul(optarg, &endp, 0); - if (*endp != '\0' || endp == optarg || args.vol_id < 0) { - errmsg("bad volume ID: " "\"%s\"", optarg); - return -1; - } - break; - - case 'h': - fprintf(stderr, "%s\n\n", doc); - fprintf(stderr, "%s\n\n", usage); - fprintf(stderr, "%s\n", optionsstr); - exit(EXIT_SUCCESS); - - case 'V': - fprintf(stderr, "%s\n", PROGRAM_VERSION); - exit(EXIT_SUCCESS); + return 0; + out: + return -1; +} - case ':': - errmsg("parameter is missing"); - return -1; +static int param_sanity_check(struct args *args, libubi_t libubi) +{ + int err; + struct ubi_info ubi; - default: - fprintf(stderr, "Use -h for help\n"); - return -1; - } + if (args->vol_id == -1) { + fprintf(stderr, "Volume ID was not specified\n"); + goto out; } - if (optind == argc) { - errmsg("UBI device name was not specified (use -h for help)"); - return -1; - } else if (optind != argc - 1) { - errmsg("more then one UBI device specified (use -h for help)"); + err = ubi_get_info(libubi, &ubi); + if (err) return -1; - } - args.node = argv[optind]; - - if (param_sanity_check()) - return -1; + if (args->devn >= (int)ubi.dev_count) { + fprintf(stderr, "Device %d does not exist\n", args->devn); + goto out; + } return 0; + +out: + errno = EINVAL; + return -1; } int main(int argc, char * const argv[]) { - int err; + int err, old_errno; libubi_t libubi; - err = parse_opt(argc, argv); + err = parse_opt(argc, (char **)argv, &myargs); if (err) + return err == 1 ? 0 : -1; + + if (myargs.devn == -1) { + fprintf(stderr, "Device number was not specified\n"); + fprintf(stderr, "Use -h option for help\n"); return -1; + } libubi = libubi_open(); - if (libubi == NULL) - return sys_errmsg("cannot open libubi"); + if (libubi == NULL) { + perror("Cannot open libubi"); + return -1; + } - err = ubi_node_type(libubi, args.node); - if (err == 2) { - errmsg("\"%s\" is an UBI volume node, not an UBI device node", - args.node); - goto out_libubi; - } else if (err < 0) { - errmsg("\"%s\" is not an UBI device node", args.node); + err = param_sanity_check(&myargs, libubi); + if (err) { + perror("Input parameters check"); + fprintf(stderr, "Use -h option for help\n"); goto out_libubi; } - err = ubi_rmvol(libubi, args.node, args.vol_id); - if (err) { - sys_errmsg("cannot UBI remove volume"); + err = ubi_rmvol(libubi, myargs.node, myargs.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; } diff --git a/ubi-utils/src/ubiupdate.c b/ubi-utils/src/ubiupdate.c deleted file mode 100644 index c17b3c5..0000000 --- a/ubi-utils/src/ubiupdate.c +++ /dev/null @@ -1,295 +0,0 @@ -/* - * 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 update UBI volumes. - * - * Authors: Frank Haverkamp - * Joshua W. Boyer - * Artem Bityutskiy - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include "common.h" - -#define PROGRAM_VERSION "1.3" -#define PROGRAM_NAME "ubiupdate" - -struct args { - int truncate; - const char *node; - const char *img; -}; - -static struct args args = { - .truncate = 0, - .node = NULL, - .img = NULL, -}; - -static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION - " - a tool to write data to UBI volumes."; - -static const char *optionsstr = -"-n, --vol_id= ID of UBI volume to update\n" -"-t, --truncate truncate volume (wipe it out)\n" -"-h, --help print help message\n" -"-V, --version print program version"; - -static const char *usage = -"Usage: " PROGRAM_NAME " [-t] [-h] [-V] [--truncate] [--help]\n" -"\t\t[--version] \n\n" -"Example 1: " PROGRAM_NAME " /dev/ubi0_1 fs.img - write file \"fs.img\" to UBI volume /dev/ubi0_1\n" -"Example 2: " PROGRAM_NAME " /dev/ubi0_1 -t - wipe out UBI volume /dev/ubi0_1"; - -struct option long_options[] = { - { .name = "truncate", .has_arg = 0, .flag = NULL, .val = 't' }, - { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' }, - { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, - { NULL, 0, NULL, 0} -}; - -static int parse_opt(int argc, char * const argv[]) -{ - while (1) { - int key; - - key = getopt_long(argc, argv, "n:thV", long_options, NULL); - if (key == -1) - break; - - switch (key) { - case 't': - args.truncate = 1; - break; - - case 'h': - fprintf(stderr, "%s\n\n", doc); - fprintf(stderr, "%s\n\n", usage); - fprintf(stderr, "%s\n", optionsstr); - exit(EXIT_SUCCESS); - - case 'V': - fprintf(stderr, "%s\n", PROGRAM_VERSION); - exit(EXIT_SUCCESS); - - case ':': - return errmsg("parameter is missing"); - - default: - fprintf(stderr, "Use -h for help\n"); - return -1; - } - } - - if (optind == argc) - return errmsg("UBI device name was not specified (use -h for help)"); - else if (optind != argc - 2) - return errmsg("specify UBI device name and image file name as first 2 " - "parameters (use -h for help)"); - - args.node = argv[optind]; - args.img = argv[optind + 1]; - - return 0; -} - -static int truncate_volume(libubi_t libubi) -{ - int err, fd; - - fd = open(args.node, O_RDWR); - if (fd == -1) - return sys_errmsg("cannot open \"%s\"", args.node); - - err = ubi_update_start(libubi, fd, 0); - if (err) { - sys_errmsg("cannot truncate volume \"%s\"", args.node); - close(fd); - return -1; - } - - close(fd); - return 0; -} - -static int ubi_write(int fd, const void *buf, int len) -{ - int ret; - - while (len) { - ret = write(fd, buf, len); - if (ret < 0) { - if (errno == EINTR) { - warnmsg("do not interrupt me!"); - continue; - } - return sys_errmsg("cannot write %d bytes to volume \"%s\"", - len, args.node); - } - - if (ret == 0) - return errmsg("cannot write %d bytes to volume \"%s\"", len, args.node); - - len -= ret; - buf += ret; - } - - return 0; -} - -static int update_volume(libubi_t libubi, struct ubi_vol_info *vol_info) -{ - int err, fd, ifd; - long long bytes; - struct stat st; - char *buf; - - buf = malloc(vol_info->leb_size); - if (!buf) - return errmsg("cannot allocate %d bytes of memory", vol_info->leb_size); - - err = stat(args.img, &st); - if (err < 0) { - errmsg("stat failed on \"%s\"", args.node); - goto out_free; - } - - bytes = st.st_size; - if (bytes > vol_info->rsvd_bytes) { - errmsg("\"%s\" (size %lld) will not fit volume \"%s\" (size %lld)", - args.img, bytes, args.node, vol_info->rsvd_bytes); - goto out_free; - } - - fd = open(args.node, O_RDWR); - if (fd == -1) { - sys_errmsg("cannot open UBI volume \"%s\"", args.node); - goto out_free; - } - - ifd = open(args.img, O_RDONLY); - if (ifd == -1) { - sys_errmsg("cannot open \"%s\"", args.img); - goto out_close1; - } - - err = ubi_update_start(libubi, fd, bytes); - if (err) { - sys_errmsg("cannot start volume \"%s\" update", args.node); - goto out_close; - } - - while (bytes) { - int tocopy = vol_info->leb_size; - - if (tocopy > bytes) - tocopy = bytes; - - err = read(ifd, buf, tocopy); - if (err != tocopy) { - if (errno == EINTR) { - warnmsg("do not interrupt me!"); - continue; - } else { - sys_errmsg("cannot read %d bytes from \"%s\"", - tocopy, args.img); - goto out_close; - } - } - - err = ubi_write(fd, buf, tocopy); - if (err) - goto out_close; - bytes -= tocopy; - } - - close(ifd); - close(fd); - free(buf); - return 0; - -out_close: - close(ifd); -out_close1: - close(fd); -out_free: - free(buf); - return -1; -} - -int main(int argc, char * const argv[]) -{ - int err; - libubi_t libubi; - struct ubi_vol_info vol_info; - - err = parse_opt(argc, argv); - if (err) - return -1; - - if (!args.img && !args.truncate) - return errmsg("incorrect arguments, use -h for help"); - - libubi = libubi_open(); - if (libubi == NULL) { - sys_errmsg("cannot open libubi"); - goto out_libubi; - } - - err = ubi_node_type(libubi, args.node); - if (err == 1) { - errmsg("\"%s\" is an UBI device node, not an UBI volume node", - args.node); - goto out_libubi; - } else if (err < 0) { - errmsg("\"%s\" is not an UBI volume node", args.node); - goto out_libubi; - } - - err = ubi_get_vol_info(libubi, args.node, &vol_info); - if (err) { - sys_errmsg("cannot get information about UBI volume \"%s\"", - args.node); - goto out_libubi; - } - - if (args.truncate) - err = truncate_volume(libubi); - else - err = update_volume(libubi, &vol_info); - if (err) - goto out_libubi; - - libubi_close(libubi); - return 0; - -out_libubi: - libubi_close(libubi); - return -1; -} diff --git a/ubi-utils/src/ubiupdatevol.c b/ubi-utils/src/ubiupdatevol.c new file mode 100644 index 0000000..807b961 --- /dev/null +++ b/ubi-utils/src/ubiupdatevol.c @@ -0,0 +1,336 @@ +/* + * 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 update UBI volumes. + * + * Author: Frank Haverkamp + * Joshua W. Boyer + * + * 1.0 Reworked the userinterface to use argp. + * 1.1 Removed argp parsing because we want to use uClib. + * 1.2 Minor cleanups + * 1.3 Use a different libubi + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define PROGRAM_VERSION "1.3" + +#define MAXPATH 1024 +#define BUFSIZE 128 * 1024 +#define MIN(x,y) ((x)<(y)?(x):(y)) + +struct args { + int devn; + int vol_id; + int truncate; + int broken_update; + int bufsize; + + /* special stuff needed to get additional arguments */ + char *arg1; + char **options; /* [STRING...] */ +}; + +static struct args myargs = { + .devn = -1, + .vol_id = -1, + .truncate = 0, + .broken_update = 0, + .bufsize = BUFSIZE, + .arg1 = NULL, + .options = NULL, +}; + +static int verbose = 0; + +static char doc[] = "\nVersion: " PROGRAM_VERSION "\n" + "ubiupdatevol - write to UBI Volume.\n"; + +static const char *optionsstr = +" -B, --broken-update broken update, this is for testing\n" +" -d, --devn= UBI device\n" +" -n, --vol_id= UBI volume id\n" +" -t, --truncate truncate volume\n" +" -?, --help Give this help list\n" +" --usage Give a short usage message\n" +" -V, --version Print program version\n"; + +static const char *usage = +"Usage: ubiupdatevol [-Bt?V] [-d ] [-n ] [--broken-update]\n" +" [--devn=] [--vol_id=] [--truncate] [--help]\n" +" [--usage] [--version] \n"; + +struct option long_options[] = { + { .name = "broken-update", .has_arg = 0, .flag = NULL, .val = 'B' }, + { .name = "devn", .has_arg = 1, .flag = NULL, .val = 'd' }, + { .name = "vol_id", .has_arg = 1, .flag = NULL, .val = 'n' }, + { .name = "truncate", .has_arg = 0, .flag = NULL, .val = 't' }, + { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' }, + { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 }, + { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, + { NULL, 0, NULL, 0} +}; + +/* + * @brief Parse the arguments passed into the test case. + */ +static int +parse_opt(int argc, char **argv, struct args *args) +{ + while (1) { + int key; + + key = getopt_long(argc, argv, "Bd:n:t?V", long_options, NULL); + if (key == -1) + break; + + switch (key) { + case 'v': /* --verbose= */ + verbose = strtoul(optarg, (char **)NULL, 0); + break; + + case 'n': /* --vol_id= */ + args->vol_id = strtol(optarg, (char **)NULL, 0); + break; + + case 'd': /* --devn= */ + args->devn = strtol(optarg, (char **)NULL, 0); + break; + + case 'b': /* --bufsize= */ + args->bufsize = strtol(optarg, (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 '?': /* help */ + fprintf(stderr, "Usage: " + "ubiupdatevol [OPTION...] \n%s%s" + "\nReport bugs to %s\n", + doc, optionsstr, PACKAGE_BUGREPORT); + exit(EXIT_SUCCESS); + break; + + case 'V': + fprintf(stderr, "%s\n", PROGRAM_VERSION); + exit(0); + break; + + default: + fprintf(stderr, "%s", usage); + exit(EXIT_FAILURE); + } + } + + if (optind < argc) { + /* only one additional argument required */ + args->arg1 = argv[optind++]; + } + 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,libubi_t libubi) +{ + int rc, ofd; + char path[MAXPATH]; + int old_errno; + + snprintf(path, MAXPATH-1, "/dev/ubi%d_%d", args->devn, args->vol_id); + path[MAXPATH-1] = '\0'; + + ofd = open(path, O_RDWR); + if (ofd < 0) { + fprintf(stderr, "Cannot open volume %s\n", path); + exit(EXIT_FAILURE); + } + rc = ubi_update_start(libubi, ofd, 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, libubi_t libubi) +{ + 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 image 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->devn, args->vol_id); + 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 = ubi_update_start(libubi, ofd, 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; + libubi_t libubi; + + parse_opt(argc, argv, &myargs); + + libubi = libubi_open(); + if (libubi == NULL) { + perror("Cannot open libubi"); + return -1; + } + + if (myargs.truncate) { + rc = ubi_truncate_volume(&myargs, 0LL, libubi); + if (rc < 0) + goto out_libubi; + } + else if (myargs.broken_update) { + rc = ubi_truncate_volume(&myargs, 1LL, libubi); + if (rc < 0) + goto out_libubi; + } else { + rc = ubi_update_volume(&myargs, libubi); + if (rc < 0) + goto out_libubi; + } + + libubi_close(libubi); + return 0; + +out_libubi: + libubi_close(libubi); + return -1; +} diff --git a/ubi-utils/src/unubi.c b/ubi-utils/src/unubi.c new file mode 100644 index 0000000..8a022a3 --- /dev/null +++ b/ubi-utils/src/unubi.c @@ -0,0 +1,1024 @@ +/* + * Copyright (c) International Business Machines Corp., 2006, 2007 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * Authors: Drake Dowsett, dowsett@de.ibm.com + * Frank Haverkamp, haver@vnet.ibm.com + * + * 1.2 Removed argp because we want to use uClibc. + * 1.3 Minor cleanups. + * 1.4 Meanwhile Drake had done a lot of changes, syncing those. + * 1.5 Bugfixes, simplifications + */ + +/* + * unubi reads an image file containing blocks of UBI headers and data + * (such as produced from nand2bin) and rebuilds the volumes within. The + * default operation (when no flags are given) is to rebuild all valid + * volumes found in the image. unubi can also read straight from the + * onboard MTD device (ex. /dev/mtdblock/NAND). + */ + +/* TODO: consideration for dynamic vs. static volumes */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "crc32.h" +#include "unubi_analyze.h" + +#define EXEC "unubi" +#define CONTACT "haver@vnet.ibm.com" +#define VERSION "1.5" + +static char doc[] = "\nVersion: " VERSION "\n"; +static int debug = 0; + +static const char *optionsstr = +"Extract volumes and/or analysis information from an UBI data file.\n" +"When no parameters are flagged or given, the default operation is\n" +"to rebuild all valid complete UBI volumes found within the image.\n" +"\n" +" OPERATIONS\n" +" -a, --analyze Analyze image and create gnuplot graphs\n" +" -i, --info-table Extract volume information tables\n" +" -r, --rebuild= Extract and rebuild volume\n" +"\n" +" OPTIONS\n" +" -b, --blocksize= Specify size of eraseblocks in image in bytes\n" +" (default 128KiB)\n" +" -d, --dir= Specify output directory\n" +" -D, --debug Enable debug output\n" +" -s, --headersize= Specify size reserved for metadata in eraseblock\n" + " in bytes (default 2048 Byte)\n" + /* the -s option might be insufficient when using different vid + offset than what we used when writing this tool ... Better would + probably be --vid-hdr-offset or alike */ +"\n" +" ADVANCED\n" +" -e, --eb-split Generate individual eraseblock images (all\n" +" eraseblocks)\n" +" -v, --vol-split Generate individual eraseblock images (valid\n" +" eraseblocks only)\n" +" -V, --vol-split! Raw split by eraseblock (valid eraseblocks only)\n" +"\n" +" -?, --help Give this help list\n" +" --usage Give a short usage message\n" +" --version Print program version\n" +"\n"; + +static const char *usage = +"Usage: unubi [-aievV?] [-r ] [-b ] [-d ]\n" +" [-s ] [--analyze] [--info-table]\n" +" [--rebuild=] [--blocksize=]\n" +" [--dir=] [--headersize=] [--eb-split]\n" +" [--vol-split] [--vol-split!] [--help] [--usage] [--version]\n" +" image-file\n"; + +#define ERR_MSG(fmt...) \ + fprintf(stderr, EXEC ": " fmt) + +#define SPLIT_DATA 1 +#define SPLIT_RAW 2 + +#define DIR_FMT "unubi_%s" +#define KIB 1024 +#define MIB (KIB * KIB) +#define MAXPATH KIB + +/* filenames */ +#define FN_INVAL "%s/eb%04u%s" /* invalid eraseblock */ +#define FN_NSURE "%s/eb%04u_%03u_%03u_%03x%s" /* unsure eraseblock */ +#define FN_VALID "%s/eb%04u_%03u_%03u_%03x%s" /* valid eraseblock */ +#define FN_VOLSP "%s/vol%03u_%03u_%03u_%04u" /* split volume */ +#define FN_VOLWH "%s/volume%03u" /* whole volume */ +#define FN_VITBL "%s/vol_info_table%u" /* vol info table */ + +static uint32_t crc32_table[256]; + +/* struct args: + * bsize int, blocksize of image blocks + * hsize int, eraseblock header size + * analyze flag, when non-zero produce analysis + * eb_split flag, when non-zero output eb#### + * note: SPLIT_DATA vs. SPLIT_RAW + * vol_split flag, when non-zero output vol###_#### + * note: SPLIT_DATA vs. SPLIT_RAW + * odir_path string, directory to place volumes in + * img_path string, file to read as ubi image + * vols int array of size UBI_MAX_VOLUMES, where a 1 can be + * written for each --rebuild flag in the index specified + * then the array can be counted and collapsed using + * count_set() and collapse() + */ +struct args { + int analyze; + int itable; + uint32_t *vols; + + size_t vid_hdr_offset; + size_t data_offset; + size_t bsize; /* FIXME replace by vid_hdr/data offs? */ + size_t hsize; + + char *odir_path; + int eb_split; + int vol_split; + char *img_path; + + char **options; +}; + +struct option long_options[] = { + { .name = "rebuild", .has_arg = 1, .flag = NULL, .val = 'r' }, + { .name = "dir", .has_arg = 1, .flag = NULL, .val = 'd' }, + { .name = "analyze", .has_arg = 0, .flag = NULL, .val = 'a' }, + { .name = "blocksize", .has_arg = 1, .flag = NULL, .val = 'b' }, + { .name = "eb-split", .has_arg = 0, .flag = NULL, .val = 'e' }, + { .name = "vol-split", .has_arg = 0, .flag = NULL, .val = 'v' }, + { .name = "vol-split!", .has_arg = 0, .flag = NULL, .val = 'e' }, + { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' }, + { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 }, + { .name = "version", .has_arg = 0, .flag = NULL, .val = 'J' }, + { NULL, 0, NULL, 0} +}; + +/** + * parses out a numerical value from a string of numbers followed by: + * k, K, kib, KiB for kibibyte + * m, M, mib, MiB for mebibyte + **/ +static uint32_t +str_to_num(char *str) +{ + char *s; + ulong num; + + s = str; + num = strtoul(s, &s, 0); + + if (*s != '\0') { + if ((strcmp(s, "KiB") == 0) || (strcmp(s, "K") == 0) || + (strcmp(s, "kib") == 0) || (strcmp(s, "k") == 0)) + num *= KIB; + else if ((strcmp(s, "MiB") == 0) || (strcmp(s, "M") == 0) || + (strcmp(s, "mib") == 0) || (strcmp(s, "m") == 0)) + num *= MIB; + else + ERR_MSG("couldn't parse '%s', assuming %lu\n", + s, num); + } + return num; +} + +static int +parse_opt(int argc, char **argv, struct args *args) +{ + uint32_t i; + + while (1) { + int key; + + key = getopt_long(argc, argv, "ab:s:d:Deir:vV?J", + long_options, NULL); + if (key == -1) + break; + + switch (key) { + case 'a': /* --analyze */ + args->analyze = 1; + break; + case 'b': /* --block-size= */ + args->bsize = str_to_num(optarg); + break; + case 's': /* --header-size= */ + args->hsize = str_to_num(optarg); + break; + case 'd': /* --dir= */ + args->odir_path = optarg; + break; + case 'D': /* --debug */ + /* I wanted to use -v but that was already + used ... */ + debug = 1; + break; + case 'e': /* --eb-split */ + args->eb_split = SPLIT_RAW; + break; + case 'i': /* --info-table */ + args->itable = 1; + break; + case 'r': /* --rebuild= */ + i = str_to_num(optarg); + if (i < UBI_MAX_VOLUMES) + args->vols[str_to_num(optarg)] = 1; + else { + ERR_MSG("volume-id out of bounds\n"); + return -1; + } + break; + case 'v': /* --vol-split */ + if (args->vol_split != SPLIT_RAW) + args->vol_split = SPLIT_DATA; + break; + case 'V': /* --vol-split! */ + args->vol_split = SPLIT_RAW; + break; + case '?': /* help */ + fprintf(stderr, "Usage: unubi [OPTION...] " + "image-file\n%s%s\nReport bugs to %s\n", + doc, optionsstr, CONTACT); + exit(0); + break; + case 'J': + fprintf(stderr, "%s\n", VERSION); + exit(0); + break; + default: + fprintf(stderr, "%s", usage); + exit(-1); + } + } + + /* FIXME I suppose hsize should be replaced! */ + args->vid_hdr_offset = args->hsize - UBI_VID_HDR_SIZE; + args->data_offset = args->hsize; + + if (optind < argc) + args->img_path = argv[optind++]; + return 0; +} + + +/** + * counts the number of indicies which are flagged in full_array; + * full_array is an array of flags (1/0); + **/ +static size_t +count_set(uint32_t *full_array, size_t full_len) +{ + size_t count, i; + + if (full_array == NULL) + return 0; + + for (i = 0, count = 0; i < full_len; i++) + if (full_array[i] != 0) + count++; + + return count; +} + + +/** + * generates coll_array from full_array; + * full_array is an array of flags (1/0); + * coll_array is an array of the indicies in full_array which are flagged (1); + **/ +static size_t +collapse(uint32_t *full_array, size_t full_len, + uint32_t *coll_array, size_t coll_len) +{ + size_t i, j; + + if ((full_array == NULL) || (coll_array == NULL)) + return 0; + + for (i = 0, j = 0; (i < full_len) && (j < coll_len); i++) + if (full_array[i] != 0) { + coll_array[j] = i; + j++; + } + + return j; +} + +/** + * data_crc: save the FILE* position, calculate the crc over a span, + * reset the position + * returns non-zero when EOF encountered + **/ +static int +data_crc(FILE* fpin, size_t length, uint32_t *ret_crc) +{ + int rc; + size_t i; + char buf[length]; + uint32_t crc; + fpos_t start; + + rc = fgetpos(fpin, &start); + if (rc < 0) + return -1; + + for (i = 0; i < length; i++) { + int c = fgetc(fpin); + if (c == EOF) { + ERR_MSG("unexpected EOF\n"); + return -1; + } + buf[i] = (char)c; + } + + rc = fsetpos(fpin, &start); + if (rc < 0) + return -1; + + crc = clc_crc32(crc32_table, UBI_CRC32_INIT, buf, length); + *ret_crc = crc; + return 0; +} + + +/** + * reads data of size len from fpin and writes it to path + **/ +static int +extract_data(FILE* fpin, size_t len, const char *path) +{ + int rc; + size_t i; + FILE* fpout; + + rc = 0; + fpout = NULL; + + fpout = fopen(path, "wb"); + if (fpout == NULL) { + ERR_MSG("couldn't open file for writing: %s\n", path); + rc = -1; + goto err; + } + + for (i = 0; i < len; i++) { + int c = fgetc(fpin); + if (c == EOF) { + ERR_MSG("unexpected EOF while writing: %s\n", path); + rc = -2; + goto err; + } + c = fputc(c, fpout); + if (c == EOF) { + ERR_MSG("couldn't write: %s\n", path); + rc = -3; + goto err; + } + } + + err: + if (fpout != NULL) + fclose(fpout); + return rc; +} + + +/** + * extract volume information table from block. saves and reloads fpin + * position + * returns -1 when a fpos set or get fails, otherwise <= -2 on other + * failure and 0 on success + **/ +static int +extract_itable(FILE *fpin, struct eb_info *cur, size_t bsize, size_t num, + const char *path) +{ + char filename[MAXPATH + 1]; + int rc; + size_t i, max; + fpos_t temp; + FILE* fpout = NULL; + struct ubi_vtbl_record rec; + + if (fpin == NULL || cur == NULL || path == NULL) + return -2; + + /* remember position */ + rc = fgetpos(fpin, &temp); + if (rc < 0) + return -1; + + /* jump to top of eraseblock, skip to data section */ + fsetpos(fpin, &cur->eb_top); + if (rc < 0) + return -1; + fseek(fpin, be32_to_cpu(cur->ec.data_offset), SEEK_CUR); + + /* prepare output file */ + if (be32_to_cpu(cur->vid.vol_id) != UBI_LAYOUT_VOLUME_ID) + return -2; + memset(filename, 0, MAXPATH + 1); + snprintf(filename, MAXPATH, FN_VITBL, path, num); + fpout = fopen(filename, "w"); + if (fpout == NULL) + return -2; + + /* loop through entries */ + fprintf(fpout, + "index\trpebs\talign\ttype\tcrc\t\tname\n"); + max = bsize - be32_to_cpu(cur->ec.data_offset); + for (i = 0; i < (max / sizeof(rec)); i++) { + int blank = 1; + char *ptr, *base; + char name[UBI_VOL_NAME_MAX + 1]; + const char *type = "unknown\0"; + uint32_t crc; + + /* read record */ + rc = fread(&rec, 1, sizeof(rec), fpin); + if (rc == 0) + break; + if (rc != sizeof(rec)) { + ERR_MSG("reading volume information " + "table record failed\n"); + rc = -3; + goto exit; + } + + /* check crc */ + crc = clc_crc32(crc32_table, UBI_CRC32_INIT, &rec, + UBI_VTBL_RECORD_SIZE_CRC); + if (crc != be32_to_cpu(rec.crc)) + continue; + + /* check for empty */ + base = (char *)&rec; + ptr = base; + while (blank && + ((unsigned)(ptr - base) < UBI_VTBL_RECORD_SIZE_CRC)) { + if (*ptr != 0) + blank = 0; + ptr++; + } + + if (blank) + continue; + + /* prep type string */ + if (rec.vol_type == UBI_VID_DYNAMIC) + type = "dynamic\0"; + else if (rec.vol_type == UBI_VID_STATIC) + type = "static\0"; + + /* prep name string */ + rec.name[be16_to_cpu(rec.name_len)] = '\0'; + sprintf(name, "%s", rec.name); + + /* print record line to fpout */ + fprintf(fpout, "%u\t%u\t%u\t%s\t0x%08x\t%s\n", + i, + be32_to_cpu(rec.reserved_pebs), + be32_to_cpu(rec.alignment), + type, + be32_to_cpu(rec.crc), + name); + } + + exit: + /* reset position */ + if (fsetpos(fpin, &temp) < 0) + rc = -1; + + if (fpout != NULL) + fclose(fpout); + + return rc; +} + + +/** + * using eb chain, tries to rebuild the data of volume at vol_id, or for all + * the known volumes, if vol_id is NULL; + **/ +static int +rebuild_volume(FILE * fpin, uint32_t *vol_id, struct eb_info **head, + const char *path, size_t block_size, size_t header_size) +{ + char filename[MAXPATH]; + int rc; + uint32_t vol, num, data_size; + FILE* fpout; + struct eb_info *cur; + + rc = 0; + + if ((fpin == NULL) || (head == NULL) || (*head == NULL)) + return 0; + + /* when vol_id is null, then do all */ + if (vol_id == NULL) { + cur = *head; + vol = be32_to_cpu(cur->vid.vol_id); + } else { + vol = *vol_id; + eb_chain_position(head, vol, NULL, &cur); + if (cur == NULL) { + if (debug) + ERR_MSG("no valid volume %d was found\n", vol); + return -1; + } + } + + num = 0; + snprintf(filename, MAXPATH, FN_VOLWH, path, vol); + fpout = fopen(filename, "wb"); + if (fpout == NULL) { + ERR_MSG("couldn't open file for writing: %s\n", filename); + return -1; + } + + while (cur != NULL) { + size_t i; + + if (be32_to_cpu(cur->vid.vol_id) != vol) { + /* close out file */ + fclose(fpout); + + /* only stay around if that was the only volume */ + if (vol_id != NULL) + goto out; + + /* begin with next */ + vol = be32_to_cpu(cur->vid.vol_id); + num = 0; + snprintf(filename, MAXPATH, FN_VOLWH, path, vol); + fpout = fopen(filename, "wb"); + if (fpout == NULL) { + ERR_MSG("couldn't open file for writing: %s\n", + filename); + return -1; + } + } + + while (num < be32_to_cpu(cur->vid.lnum)) { + /* FIXME haver: I hope an empty block is + written out so that the binary has no holes + ... */ + if (debug) + ERR_MSG("missing valid block %d for volume %d\n", + num, vol); + num++; + } + + rc = fsetpos(fpin, &(cur->eb_top)); + if (rc < 0) + goto out; + fseek(fpin, be32_to_cpu(cur->ec.data_offset), SEEK_CUR); + + if (cur->vid.vol_type == UBI_VID_DYNAMIC) + /* FIXME It might be that alignment has influence */ + data_size = block_size - header_size; + else + data_size = be32_to_cpu(cur->vid.data_size); + + for (i = 0; i < data_size; i++) { + int c = fgetc(fpin); + if (c == EOF) { + ERR_MSG("unexpected EOF while writing: %s\n", + filename); + rc = -2; + goto out; + } + c = fputc(c, fpout); + if (c == EOF) { + ERR_MSG("couldn't write: %s\n", filename); + rc = -3; + goto out; + } + } + + cur = cur->next; + num++; + } + + out: + if (vol_id == NULL) + fclose(fpout); + return rc; +} + + +/** + * traverses FILE* trying to load complete, valid and accurate header data + * into the eb chain; + **/ +static int +unubi_volumes(FILE* fpin, uint32_t *vols, size_t vc, struct args *a) +{ + char filename[MAXPATH + 1]; + char reason[MAXPATH + 1]; + int rc; + size_t i, count, itable_num; + /* relations: + * cur ~ head + * next ~ first */ + struct eb_info *head, *cur, *first, *next; + struct eb_info **next_ptr; + + rc = 0; + count = 0; + itable_num = 0; + head = NULL; + first = NULL; + next = NULL; + cur = malloc(sizeof(*cur)); + if (cur == NULL) { + ERR_MSG("out of memory\n"); + rc = -ENOMEM; + goto err; + } + memset(cur, 0, sizeof(*cur)); + + fgetpos(fpin, &(cur->eb_top)); + while (1) { + const char *raw_path; + uint32_t crc; + + cur->phys_addr = ftell(fpin); + cur->phys_block = cur->phys_addr / a->bsize; + cur->data_crc_ok = 0; + cur->ec_crc_ok = 0; + cur->vid_crc_ok = 0; + + memset(filename, 0, MAXPATH + 1); + memset(reason, 0, MAXPATH + 1); + + /* in case of an incomplete ec header */ + raw_path = FN_INVAL; + + /* read erasecounter header */ + rc = fread(&cur->ec, 1, sizeof(cur->ec), fpin); + if (rc == 0) + goto out; /* EOF */ + if (rc != sizeof(cur->ec)) { + ERR_MSG("reading ec-hdr failed\n"); + rc = -1; + goto err; + } + + /* check erasecounter header magic */ + if (be32_to_cpu(cur->ec.magic) != UBI_EC_HDR_MAGIC) { + snprintf(reason, MAXPATH, ".invalid.ec_magic"); + goto invalid; + } + + /* check erasecounter header crc */ + crc = clc_crc32(crc32_table, UBI_CRC32_INIT, &(cur->ec), + UBI_EC_HDR_SIZE_CRC); + if (be32_to_cpu(cur->ec.hdr_crc) != crc) { + snprintf(reason, MAXPATH, ".invalid.ec_hdr_crc"); + goto invalid; + } + + /* read volume id header */ + rc = fsetpos(fpin, &(cur->eb_top)); + if (rc != 0) + goto err; + fseek(fpin, be32_to_cpu(cur->ec.vid_hdr_offset), SEEK_CUR); + rc = fread(&cur->vid, 1, sizeof(cur->vid), fpin); + if (rc == 0) + goto out; /* EOF */ + if (rc != sizeof(cur->vid)) { + ERR_MSG("reading vid-hdr failed\n"); + rc = -1; + goto err; + } + + /* if the magic number is 0xFFFFFFFF, then it's very likely + * that the volume is empty */ + if (be32_to_cpu(cur->vid.magic) == 0xffffffff) { + snprintf(reason, MAXPATH, ".empty"); + goto invalid; + } + + /* vol_id should be in bounds */ + if ((be32_to_cpu(cur->vid.vol_id) >= UBI_MAX_VOLUMES) && + (be32_to_cpu(cur->vid.vol_id) < + UBI_INTERNAL_VOL_START)) { + snprintf(reason, MAXPATH, ".invalid"); + goto invalid; + } else + raw_path = FN_NSURE; + + /* check volume id header magic */ + if (be32_to_cpu(cur->vid.magic) != UBI_VID_HDR_MAGIC) { + snprintf(reason, MAXPATH, ".invalid.vid_magic"); + goto invalid; + } + cur->ec_crc_ok = 1; + + /* check volume id header crc */ + crc = clc_crc32(crc32_table, UBI_CRC32_INIT, &(cur->vid), + UBI_VID_HDR_SIZE_CRC); + if (be32_to_cpu(cur->vid.hdr_crc) != crc) { + snprintf(reason, MAXPATH, ".invalid.vid_hdr_crc"); + goto invalid; + } + cur->vid_crc_ok = 1; + + /* check data crc, but only for a static volume */ + if (cur->vid.vol_type == UBI_VID_STATIC) { + rc = data_crc(fpin, be32_to_cpu(cur->vid.data_size), + &crc); + if (rc < 0) + goto err; + if (be32_to_cpu(cur->vid.data_crc) != crc) { + snprintf(reason, MAXPATH, ".invalid.data_crc"); + goto invalid; + } + cur->data_crc_ok = 1; + } + + /* enlist this vol, it's valid */ + raw_path = FN_VALID; + cur->linear = count; + rc = eb_chain_insert(&head, cur); + if (rc < 0) { + if (rc == -ENOMEM) { + ERR_MSG("out of memory\n"); + goto err; + } + ERR_MSG("unknown and unexpected error, please contact " + CONTACT "\n"); + goto err; + } + + /* extract info-table */ + if (a->itable && + (be32_to_cpu(cur->vid.vol_id) == UBI_LAYOUT_VOLUME_ID)) { + extract_itable(fpin, cur, a->bsize, + itable_num, a->odir_path); + itable_num++; + } + + /* split volumes */ + if (a->vol_split) { + size_t size = 0; + + rc = fsetpos(fpin, &(cur->eb_top)); + if (rc != 0) + goto err; + + /* + * FIXME For dynamic UBI volumes we must write + * the maximum available data. The + * vid.data_size field is not used in this + * case. The dynamic volume user is + * responsible for the content. + */ + if (a->vol_split == SPLIT_DATA) { + /* Write only data section */ + if (cur->vid.vol_type == UBI_VID_DYNAMIC) { + /* FIXME Formular is not + always right ... */ + size = a->bsize - a->hsize; + } else + size = be32_to_cpu(cur->vid.data_size); + + fseek(fpin, + be32_to_cpu(cur->ec.data_offset), + SEEK_CUR); + } + else if (a->vol_split == SPLIT_RAW) + /* write entire eraseblock */ + size = a->bsize; + + snprintf(filename, MAXPATH, FN_VOLSP, + a->odir_path, + be32_to_cpu(cur->vid.vol_id), + be32_to_cpu(cur->vid.lnum), + be32_to_cpu(cur->vid.leb_ver), count); + rc = extract_data(fpin, size, filename); + if (rc < 0) + goto err; + } + + invalid: + /* split eraseblocks */ + if (a->eb_split) { + /* jump to top of block */ + rc = fsetpos(fpin, &(cur->eb_top)); + if (rc != 0) + goto err; + + if (strcmp(raw_path, FN_INVAL) == 0) + snprintf(filename, MAXPATH, raw_path, + a->odir_path, count, reason); + else + snprintf(filename, MAXPATH, raw_path, + a->odir_path, + count, + be32_to_cpu(cur->vid.vol_id), + be32_to_cpu(cur->vid.lnum), + be32_to_cpu(cur->vid.leb_ver), + reason); + + rc = extract_data(fpin, a->bsize, filename); + if (rc < 0) + goto err; + } + + /* append to simple linked list */ + if (first == NULL) + next_ptr = &first; + else + next_ptr = &next->next; + + *next_ptr = malloc(sizeof(**next_ptr)); + if (*next_ptr == NULL) { + ERR_MSG("out of memory\n"); + rc = -ENOMEM; + goto err; + } + memset(*next_ptr, 0, sizeof(**next_ptr)); + + next = *next_ptr; + memcpy(next, cur, sizeof(*next)); + next->next = NULL; + + count++; + rc = fsetpos(fpin, &(cur->eb_top)); + if (rc != 0) + goto err; + fseek(fpin, a->bsize, SEEK_CUR); + memset(cur, 0, sizeof(*cur)); + + fgetpos(fpin, &(cur->eb_top)); + } + + out: + for (i = 0; i < vc; i++) { + rc = rebuild_volume(fpin, &vols[i], &head, a->odir_path, + a->bsize, a->hsize); + if (rc < 0) + goto err; + } + + /* if there were no volumes specified, rebuild them all, + * UNLESS eb_ or vol_ split or analyze was specified */ + if ((vc == 0) && (!a->eb_split) && (!a->vol_split) && + (!a->analyze) && (!a->itable)) { + rc = rebuild_volume(fpin, NULL, &head, a->odir_path, a->bsize, + a->hsize); + if (rc < 0) + goto err; + } + + err: + free(cur); + + if (a->analyze) { + char fname[PATH_MAX]; + FILE *fp; + + unubi_analyze(&head, first, a->odir_path); + + /* prepare output files */ + memset(fname, 0, PATH_MAX + 1); + snprintf(fname, PATH_MAX, "%s/%s", a->odir_path, FN_EH_STAT); + fp = fopen(fname, "w"); + if (fp != NULL) { + eb_chain_print(fp, head); + fclose(fp); + } + } + eb_chain_destroy(&head); + eb_chain_destroy(&first); + + return rc; +} + + +/** + * handles command line arguments, then calls unubi_volumes + **/ +int +main(int argc, char *argv[]) +{ + int rc, free_a_odir; + size_t vols_len; + uint32_t *vols; + FILE* fpin; + struct args a; + + rc = 0; + free_a_odir = 0; + vols_len = 0; + vols = NULL; + fpin = NULL; + init_crc32_table(crc32_table); + + /* setup struct args a */ + memset(&a, 0, sizeof(a)); + a.bsize = 128 * KIB; + a.hsize = 2 * KIB; + a.vols = malloc(sizeof(*a.vols) * UBI_MAX_VOLUMES); + if (a.vols == NULL) { + ERR_MSG("out of memory\n"); + rc = ENOMEM; + goto err; + } + memset(a.vols, 0, sizeof(*a.vols) * UBI_MAX_VOLUMES); + + /* parse args and check for validity */ + parse_opt(argc, argv, &a); + if (a.img_path == NULL) { + ERR_MSG("no image file specified\n"); + rc = EINVAL; + goto err; + } + else if (a.odir_path == NULL) { + char *ptr; + int len; + + ptr = strrchr(a.img_path, '/'); + if (ptr == NULL) + ptr = a.img_path; + else + ptr++; + + len = strlen(DIR_FMT) + strlen(ptr); + free_a_odir = 1; + a.odir_path = malloc(sizeof(*a.odir_path) * len); + if (a.odir_path == NULL) { + ERR_MSG("out of memory\n"); + rc = ENOMEM; + goto err; + } + snprintf(a.odir_path, len, DIR_FMT, ptr); + } + + fpin = fopen(a.img_path, "rb"); + if (fpin == NULL) { + ERR_MSG("couldn't open file for reading: " + "%s\n", a.img_path); + rc = EINVAL; + goto err; + } + + rc = mkdir(a.odir_path, 0777); + if ((rc < 0) && (errno != EEXIST)) { + ERR_MSG("couldn't create ouput directory: " + "%s\n", a.odir_path); + rc = -rc; + goto err; + } + + /* fill in vols array */ + vols_len = count_set(a.vols, UBI_MAX_VOLUMES); + if (vols_len > 0) { + vols = malloc(sizeof(*vols) * vols_len); + if (vols == NULL) { + ERR_MSG("out of memory\n"); + rc = ENOMEM; + goto err; + } + collapse(a.vols, UBI_MAX_VOLUMES, vols, vols_len); + } + + /* unubi volumes */ + rc = unubi_volumes(fpin, vols, vols_len, &a); + if (rc < 0) { + /* ERR_MSG("error encountered while working on image file: " + "%s\n", a.img_path); */ + rc = -rc; + goto err; + } + + err: + free(a.vols); + if (free_a_odir != 0) + free(a.odir_path); + if (fpin != NULL) + fclose(fpin); + if (vols_len > 0) + free(vols); + return rc; +} diff --git a/ubi-utils/src/unubi_analyze.c b/ubi-utils/src/unubi_analyze.c new file mode 100644 index 0000000..182100e --- /dev/null +++ b/ubi-utils/src/unubi_analyze.c @@ -0,0 +1,463 @@ +/* + * Copyright (c) International Business Machines Corp., 2006, 2007 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * Authors: Drake Dowsett, dowsett@de.ibm.com + * Contact: Andreas Arnez, arnez@de.ibm.com + * + * unubi uses the following functions to generate analysis output based on + * the header information in a raw-UBI image + */ + +/* + * TODO: use OOB data to check for eraseblock validity in NAND images + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "unubi_analyze.h" +#include "crc32.h" + +#define EC_X_INT 50 + +/** + * intcmp - function needed by qsort to order integers + **/ +int intcmp(const void *a, const void *b) +{ + int A = *(int *)a; + int B = *(int *)b; + return A - B; +} + +int longcmp(const void *a, const void *b) +{ + long long A = *(long long *)a; + long long B = *(long long *)b; + return A - B; +} + + +/** + * unubi_analyze_group_index - finds the normalized index in an array + * item: look for this item in the array + * array: array to search through + * size: length of the array + * array should be sorted for this algorithm to perform properly; + * if the item is not found returns -1, otherwise return value is the + * index in the array (note this contricts the array size to 2^32-1); + **/ +int +norm_index(uint32_t item, uint32_t *array, size_t length) +{ + size_t i, index; + + for (index = 0, i = 0; i < length; i++) { + if ((i != 0) && (array[i] != array[i - 1])) + index++; + + if (item == array[i]) + return index; + } + + return -1; +} + + +/** + * unubi_analyze_ec_hdr - generate data table and plot script + * first: head of simple linked list + * path: folder to write into + * generates a data file containing the eraseblock index in the image + * and the erase counter found in its ec header; + * if the crc check fails, the line is commented out in the data file; + * also generates a simple gnuplot sript for quickly viewing one + * display of the data file; + **/ +int +unubi_analyze_ec_hdr(struct eb_info *first, const char *path) +{ + char filename[PATH_MAX + 1]; + size_t count, eraseblocks; + uint32_t crc, crc32_table[256]; + uint64_t *erase_counts; + FILE* fpdata; + FILE* fpplot; + struct eb_info *cur; + + if (first == NULL) + return -1; + + /* crc check still needed for `first' linked list */ + init_crc32_table(crc32_table); + + /* prepare output files */ + memset(filename, 0, PATH_MAX + 1); + snprintf(filename, PATH_MAX, "%s/%s", path, FN_EH_DATA); + fpdata = fopen(filename, "w"); + if (fpdata == NULL) + return -1; + + memset(filename, 0, PATH_MAX + 1); + snprintf(filename, PATH_MAX, "%s/%s", path, FN_EH_PLOT); + fpplot = fopen(filename, "w"); + if (fpplot == NULL) { + fclose(fpdata); + return -1; + } + + /* make executable */ + chmod(filename, 0755); + + /* first run: count elements */ + count = 0; + cur = first; + while (cur != NULL) { + cur = cur->next; + count++; + } + eraseblocks = count; + + erase_counts = malloc(eraseblocks * sizeof(*erase_counts)); + if (!erase_counts) { + perror("out of memory"); + exit(EXIT_FAILURE); + } + + memset(erase_counts, 0, eraseblocks * sizeof(*erase_counts)); + + /* second run: populate array to sort */ + count = 0; + cur = first; + while (cur != NULL) { + erase_counts[count] = be64_to_cpu(cur->ec.ec); + cur = cur->next; + count++; + } + qsort(erase_counts, eraseblocks, sizeof(*erase_counts), + (void *)longcmp); + + /* third run: generate data file */ + count = 0; + cur = first; + fprintf(fpdata, "# eraseblock_no actual_erase_count " + "sorted_erase_count\n"); + while (cur != NULL) { + crc = clc_crc32(crc32_table, UBI_CRC32_INIT, &cur->ec, + UBI_EC_HDR_SIZE_CRC); + + if ((be32_to_cpu(cur->ec.magic) != UBI_EC_HDR_MAGIC) || + (crc != be32_to_cpu(cur->ec.hdr_crc))) + fprintf(fpdata, "# "); + + fprintf(fpdata, "%u %llu %llu", count, + be64_to_cpu(cur->ec.ec), + erase_counts[count]); + + if (be32_to_cpu(cur->ec.magic) != UBI_EC_HDR_MAGIC) + fprintf(fpdata, " ## bad magic: %08x", + be32_to_cpu(cur->ec.magic)); + + if (crc != be32_to_cpu(cur->ec.hdr_crc)) + fprintf(fpdata, " ## CRC mismatch: given=%08x, " + "calc=%08x", be32_to_cpu(cur->ec.hdr_crc), + crc); + + fprintf(fpdata, "\n"); + + cur = cur->next; + count++; + } + fclose(fpdata); + + fprintf(fpplot, "#!/usr/bin/gnuplot -persist\n"); + fprintf(fpplot, "set xlabel \"eraseblock\"\n"); + + /* fourth run: generate plot file xtics */ + count = 0; + cur = first; + fprintf(fpplot, "set xtics ("); + while (cur != NULL) { + if ((count % EC_X_INT) == 0) { + if (count > 0) + fprintf(fpplot, ", "); + fprintf(fpplot, "%d", count); + } + + cur = cur->next; + count++; + } + fprintf(fpplot, ")\n"); + + fprintf(fpplot, "set ylabel \"erase count\"\n"); + fprintf(fpplot, "set xrange [-1:%u]\n", eraseblocks + 1); + fprintf(fpplot, "# set yrange [-1:%llu]\n", + erase_counts[eraseblocks - 1] + 1); + fprintf(fpplot, "plot \"%s\" u 1:2 t \"unsorted: %s\" with boxes\n", + FN_EH_DATA, FN_EH_DATA); + fprintf(fpplot, "# replot \"%s\" u 1:3 t \"sorted: %s\" with lines\n", + FN_EH_DATA, FN_EH_DATA); + fprintf(fpplot, "pause -1 \"press ENTER\"\n"); + + fclose(fpplot); + + return 0; +} + + +/** + * unubi_analyze_vid_hdr - generate data table and plot script + * head: head of complex linked list (eb_chain) + * path: folder to write into + * generates a data file containing the volume id, logical number, leb version, + * and data size from the vid header; + * all eraseblocks listed in the eb_chain are valid (checked in unubi); + * also generates a simple gnuplot sript for quickly viewing one + * display of the data file; + **/ +int +unubi_analyze_vid_hdr(struct eb_info **head, const char *path) +{ + char filename[PATH_MAX + 1]; + int rc, y1, y2; + size_t count, step, breadth; + uint32_t *leb_versions, *data_sizes; + FILE* fpdata; + FILE* fpplot; + struct eb_info *cur; + + if (head == NULL || *head == NULL) + return -1; + + rc = 0; + fpdata = NULL; + fpplot = NULL; + data_sizes = NULL; + leb_versions = NULL; + + /* prepare output files */ + memset(filename, 0, PATH_MAX + 1); + snprintf(filename, PATH_MAX, "%s/%s", path, FN_VH_DATA); + fpdata = fopen(filename, "w"); + if (fpdata == NULL) { + rc = -1; + goto exit; + } + + memset(filename, 0, PATH_MAX + 1); + snprintf(filename, PATH_MAX, "%s/%s", path, FN_VH_PLOT); + fpplot = fopen(filename, "w"); + if (fpplot == NULL) { + rc = -1; + goto exit; + } + + /* make executable */ + chmod(filename, 0755); + + /* first run: count elements */ + count = 0; + cur = *head; + while (cur != NULL) { + cur = cur->next; + count++; + } + breadth = count; + + leb_versions = malloc(breadth * sizeof(uint32_t)); + if (leb_versions == NULL) { + rc = -1; + goto exit; + } + memset(leb_versions, 0, breadth * sizeof(uint32_t)); + + data_sizes = malloc(breadth * sizeof(uint32_t)); + if (data_sizes == NULL) { + rc = -1; + goto exit; + } + memset(data_sizes, 0, breadth * sizeof(*data_sizes)); + + /* second run: populate arrays to sort */ + count = 0; + cur = *head; + while (cur != NULL) { + leb_versions[count] = be32_to_cpu(cur->vid.leb_ver); + data_sizes[count] = be32_to_cpu(cur->vid.data_size); + cur = cur->next; + count++; + } + qsort(leb_versions, breadth, sizeof(*leb_versions), (void *)intcmp); + qsort(data_sizes, breadth, sizeof(*data_sizes), (void *)intcmp); + + /* third run: generate data file */ + count = 0; + cur = *head; + fprintf(fpdata, "# x_axis vol_id lnum y1_axis leb_ver " + "y2_axis data_size\n"); + while (cur != NULL) { + y1 = norm_index(be32_to_cpu(cur->vid.leb_ver), leb_versions, + breadth); + y2 = norm_index(be32_to_cpu(cur->vid.data_size), data_sizes, + breadth); + + if ((y1 == -1) || (y2 == -1)) { + rc = -1; + goto exit; + } + + fprintf(fpdata, "%u %u %u %u %u %u %u\n", + count, + be32_to_cpu(cur->vid.vol_id), + be32_to_cpu(cur->vid.lnum), + y1, + be32_to_cpu(cur->vid.leb_ver), + y2, + be32_to_cpu(cur->vid.data_size)); + cur = cur->next; + count++; + } + + fprintf(fpplot, "#!/usr/bin/gnuplot -persist\n"); + fprintf(fpplot, "set xlabel \"volume\"\n"); + + /* fourth run: generate plot file xtics */ + count = 0; + step = 0; + cur = *head; + fprintf(fpplot, "set xtics ("); + while (cur != NULL) { + if (count > 0) + fprintf(fpplot, ", "); + if (step != be32_to_cpu(cur->vid.vol_id)) { + step = be32_to_cpu(cur->vid.vol_id); + fprintf(fpplot, "\"%d\" %d 0", step, count); + } + else + fprintf(fpplot, "\"%d\" %d 1", + be32_to_cpu(cur->vid.lnum), count); + cur = cur->next; + count++; + } + fprintf(fpplot, ")\n"); + fprintf(fpplot, "set nox2tics\n"); + + /* fifth run: generate plot file ytics */ + count = 0; + cur = *head; + fprintf(fpplot, "set ylabel \"leb version\"\n"); + fprintf(fpplot, "set ytics ("); + while (cur->next != NULL) { + y1 = norm_index(be32_to_cpu(cur->vid.leb_ver), leb_versions, + breadth); + + if (y1 == -1) { + rc = -1; + goto exit; + } + + if (count > 0) + fprintf(fpplot, ", "); + + fprintf(fpplot, "\"%u\" %u", be32_to_cpu(cur->vid.leb_ver), + y1); + + cur = cur->next; + count++; + } + fprintf(fpplot, ")\n"); + + /* sixth run: generate plot file y2tics */ + count = 0; + cur = *head; + fprintf(fpplot, "set y2label \"data size\"\n"); + fprintf(fpplot, "set y2tics ("); + while (cur != NULL) { + y2 = norm_index(be32_to_cpu(cur->vid.data_size), + data_sizes, breadth); + + if (y2 == -1) { + rc = -1; + goto exit; + } + + if (count > 0) + fprintf(fpplot, ", "); + + fprintf(fpplot, "\"%u\" %u", be32_to_cpu(cur->vid.data_size), + y2); + + cur = cur->next; + count++; + } + fprintf(fpplot, ")\n"); + + y1 = norm_index(leb_versions[breadth - 1], leb_versions, breadth); + y2 = norm_index(data_sizes[breadth - 1], data_sizes, breadth); + fprintf(fpplot, "set xrange [-1:%u]\n", count + 1); + fprintf(fpplot, "set yrange [-1:%u]\n", y1 + 1); + fprintf(fpplot, "set y2range [-1:%u]\n", y2 + 1); + fprintf(fpplot, "plot \"%s\" u 1:4 t \"leb version: %s\" " + "axes x1y1 with lp\n", FN_VH_DATA, FN_VH_DATA); + fprintf(fpplot, "replot \"%s\" u 1:6 t \"data size: %s\" " + "axes x1y2 with lp\n", FN_VH_DATA, FN_VH_DATA); + fprintf(fpplot, "pause -1 \"press ENTER\"\n"); + + exit: + if (fpdata != NULL) + fclose(fpdata); + if (fpplot != NULL) + fclose(fpplot); + if (data_sizes != NULL) + free(data_sizes); + if (leb_versions != NULL) + free(leb_versions); + + return rc; +} + + +/** + * unubi_analyze - run all analyses + * head: eb_chain head + * first: simple linked list of eraseblock headers (use .next) + * path: directory (without trailing slash) to output to + * returns 0 upon successful completion, or -1 otherwise + **/ +int +unubi_analyze(struct eb_info **head, struct eb_info *first, const char *path) +{ + int ec_rc, vid_rc; + + if (path == NULL) + return -1; + + ec_rc = unubi_analyze_ec_hdr(first, path); + vid_rc = unubi_analyze_vid_hdr(head, path); + if (ec_rc < 0 || vid_rc < 0) + return -1; + + return 0; +} diff --git a/ubi-utils/src/unubi_analyze.h b/ubi-utils/src/unubi_analyze.h new file mode 100644 index 0000000..8588219 --- /dev/null +++ b/ubi-utils/src/unubi_analyze.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) International Business Machines Corp., 2006, 2007 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __UNUBI_ANALYZE_H__ +#define __UNUBI_ANALYZE_H__ + +/* + * Author: Drake Dowsett + * Contact: Andreas Arnez (arnez@de.ibm.com) + * + * Eraseblock Chain + * + * A linked list structure to order eraseblocks by volume and logical number + * and to update by version number. Doesn't contain actual eraseblock data + * but rather the erasecounter and volume id headers as well as a position + * indicator. + * + * Diagram Example: + * + * [V1.0v0]->[V1.1v2]->[V1.2v1]->[V2.0v2]->[V2.1v0]->[V2.2v1]->NULL + * | | | | | | + * NULL [V1.1v1] [V1.2v0] [V2.0v1] NULL [V2.2v0] + * | | | | + * [V1.1v0] NULL [V2.0v0] NULL + * | | + * NULL NULL + * + * [VA.BvC] represents the eb_info for the eraseblock with the vol_id A, + * lnum B and leb_ver C + * -> represents the `next' pointer + * | represents the `older' pointer + */ + +#include +#include +#include + +#define FN_EH_STAT "analysis_blocks.txt" +#define FN_EH_DATA "analysis_ec_hdr.data" +#define FN_EH_PLOT "analysis_ec_hdr.plot" +#define FN_VH_DATA "analysis_vid_hdr.data" +#define FN_VH_PLOT "analysis_vid_hdr.plot" + +struct eb_info { + struct ubi_ec_hdr ec; + struct ubi_vid_hdr vid; + + fpos_t eb_top; + uint32_t linear; + int ec_crc_ok; + int vid_crc_ok; + int data_crc_ok; + uint32_t phys_addr; + int phys_block; + + struct eb_info *next; + struct eb_info *older; +}; + +int eb_chain_insert(struct eb_info **head, struct eb_info *item); + +int eb_chain_position(struct eb_info **head, uint32_t vol_id, uint32_t *lnum, + struct eb_info **pos); + +int eb_chain_print(FILE *stream, struct eb_info *head); + +int eb_chain_destroy(struct eb_info **head); + +int unubi_analyze(struct eb_info **head, struct eb_info *first, + const char *path); + +#endif /* __UNUBI_ANALYZE_H__ */ -- cgit v1.2.3