diff options
author | Artem Bityutskiy <Artem.Bityutskiy@nokia.com> | 2008-01-23 19:42:44 +0200 |
---|---|---|
committer | Artem Bityutskiy <Artem.Bityutskiy@nokia.com> | 2008-01-23 20:01:03 +0200 |
commit | 45941c0018cc0937beeb9d4aa8e6a6070c7a3466 (patch) | |
tree | e65cad8bac744151b047136e9643abf1e343dd97 /ubi-utils/old-tools/src | |
parent | 3ad29ab55ad71d706152781b70a43d9f6be407b9 (diff) |
ubi-utils: return old tools
Signed-off-by: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
Diffstat (limited to 'ubi-utils/old-tools/src')
46 files changed, 13483 insertions, 0 deletions
diff --git a/ubi-utils/old-tools/src/bin2nand.c b/ubi-utils/old-tools/src/bin2nand.c new file mode 100644 index 0000000..83f50cc --- /dev/null +++ b/ubi-utils/old-tools/src/bin2nand.c @@ -0,0 +1,366 @@ +/* + * 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) + * 1.7 Made NAND ECC layout configurable (haver) + */ + +#include <unistd.h> +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> +#include <getopt.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> + +#include "error.h" +#include "config.h" +#include "nandecc.h" +#include "ecclayouts.h" + +#define PROGRAM_VERSION "1.7" + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) + +#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=<num> Padding in Byte/Mi/ki. Default = no padding\n" +" -l, --ecc-placement=<MTD,IBM> OOB placement scheme (default is IBM).\n" +" -p, --pagesize=<num> Pagesize in Byte/Mi/ki. Default = 2048\n" +" -o, --output=<fname> Output filename. Interleaved Data/OOB if\n" +" output-oob not specified.\n" +" -q, --output-oob=<fname> 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 <num>] [-p <num>] [-o <fname>] [-q <fname>]\n" +" [--copyright] [--padding=<num>] [--pagesize=<num>]\n" +" [--output=<fname>] [--output-oob=<fname>] [--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 = "ecc-layout", .has_arg = 1, .flag = NULL, .val = 'l' }, + { .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} +}; + +#define __unused __attribute__((unused)) +static const char copyright [] __unused = "Copyright IBM Corp. 2007"; + +struct args { + action_t action; + + size_t pagesize; + size_t oobsize; + size_t padding; + + FILE* fp_in; + const char *file_out_data; /* Either: Data and OOB interleaved + or plain data */ + const char *file_out_oob; /* OOB Data only. */ + struct nand_ecclayout *nand_oob; + + /* special stuff needed to get additional arguments */ + char *arg1; + char **options; /* [STRING...] */ +}; + + +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, struct args *args) +{ + const char *ecc_layout = NULL; + unsigned int i, oob_idx = 0; + char* endp; + + while (1) { + int key; + + key = getopt_long(argc, argv, "cj:l: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 'l': /* --ecc-layout=<...> */ + ecc_layout = optarg; + break; + case '?': /* help */ + printf("%s%s", doc, 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++]); + } + } + + switch (args->pagesize) { + case 512: args->oobsize = 16; oob_idx = 0; break; + case 2048: args->oobsize = 64; oob_idx = 1; break; + default: + err_msg("Unsupported page size: %d\n", args->pagesize); + return -EINVAL; + } + + /* Figure out correct oob layout if it differs from default */ + if (ecc_layout) { + for (i = 0; i < ARRAY_SIZE(oob_placement); i++) + if (strcmp(ecc_layout, oob_placement[i].name) == 0) + args->nand_oob = + oob_placement[i].nand_oob[oob_idx]; + } + return 0; +} + +static int +process_page(struct args *args, uint8_t *buf, FILE *fp_data, FILE *fp_oob, + size_t *written) +{ + int eccpoi; + size_t i; + uint8_t oobbuf[64]; + uint8_t ecc_code[3] = { 0, }; /* temp */ + + /* Calculate ECC for each subpage of 256 bytes */ + memset(oobbuf, 0xff, sizeof(oobbuf)); + for (eccpoi = 0, i = 0; i < args->pagesize; i += 256, eccpoi += 3) { + int j; + nand_calculate_ecc(&buf[i], ecc_code); + for (j = 0; j < 3; j++) + oobbuf[args->nand_oob->eccpos[eccpoi + j]] = ecc_code[j]; + } + + /* write data */ + *written += fwrite(buf, 1, args->pagesize, fp_data); + + /* either separate oob or interleave with data */ + if (fp_oob) { + i = fwrite(oobbuf, 1, args->oobsize, fp_oob); + if (ferror(fp_oob)) { + err_msg("IO error\n"); + return -EIO; + } + } + else { + i = fwrite(oobbuf, 1, args->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; + struct args args = { + .action = ACT_NORMAL, + .pagesize = PAGESIZE, + .padding = PADDING, + .fp_in = NULL, + .file_out_data = NULL, + .file_out_oob = NULL, + .nand_oob = &ibm_nand_oob_64, + }; + + 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(&args, buf, fp_out_data, fp_out_oob, + &written); + if (res != 0) + goto err; + } + + while (written < args.padding) { + memset(buf, 0xff, args.pagesize); + res = process_page(&args, buf, 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/old-tools/src/bootenv.c b/ubi-utils/old-tools/src/bootenv.c new file mode 100644 index 0000000..a6dd4de --- /dev/null +++ b/ubi-utils/old-tools/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 <ctype.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <errno.h> +#include <netinet/in.h> +#include <sys/stat.h> +#include <bootenv.h> + +#include "hashmap.h" +#include "error.h" + +#include <mtd/ubi-header.h> +#include "crc32.h" + +#define ubi_unused __attribute__((unused)) + +#define BOOTENV_MAXLINE 512 /* max line size of a bootenv.txt file */ + +/* Structures */ +struct bootenv { + hashmap_t map; ///< Pointer to hashmap which holds data structure. +}; + +struct bootenv_list { + hashmap_t head; ///< Pointer to list which holds the data structure. +}; + +/** + * @brief Remove the '\n' from a given line. + * @param line Input/Output line. + * @param size Size of the line. + * @param fp File Pointer. + * @return 0 + * @return or error + */ +static int +remove_lf(char *line, size_t size, FILE* fp) +{ + size_t i; + + for (i = 0; i < size; i++) { + if (line[i] == '\n') { + line[i] = '\0'; + return 0; + } + } + + if (!feof(fp)) { + return BOOTENV_EINVAL; + } + + return 0; +} + +/** + * @brief Determine if a line contains only WS. + * @param line The line to process. + * @param size Size of input line. + * @return 1 Yes, only WS. + * @return 0 No, contains data. + */ +static int +is_ws(const char *line, size_t size) +{ + size_t i = 0; + + while (i < size) { + switch (line[i]) { + case '\n': + return 1; + case '#': + return 1; + case ' ': + i++; + continue; + case '\t': + i++; + continue; + default: /* any other char -> no cmnt */ + return 0; + } + } + + return 0; +} + + +/* ------------------------------------------------------------------------- */ + +/** + * @brief Build a list from a comma seperated value string. + * @param list Pointer to hashmap structure which shall store + * the list. + * @param value Comma seperated value string. + * @return 0 + * @return or error. + */ +static int +build_list_definition(hashmap_t list, const char *value) +{ + int rc = 0; + char *str = NULL; + char *ptr = NULL; + size_t len, i, j; + + /* str: val1,val2 , val4,...,valN */ + len = strlen(value); + str = (char*) malloc((len+1) * sizeof(char)); + + /* 1. reformat string: remove spaces */ + for (i = 0, j = 0; i < len; i++) { + if (value[i] == ' ') + continue; + + str[j] = value[i]; + j++; + } + str[j] = '\0'; + + /* str: val1,val2,val4,...,valN\0*/ + /* 2. replace ',' seperator with '\0' */ + len = strlen(str); + for (i = 0; i < len; i++) { + if (str[i] == ',') { + str[i] = '\0'; + } + } + + /* str: val1\0val2\0val4\0...\0valN\0*/ + /* 3. insert definitions into a hash map, using it like a list */ + i = j = 0; + ptr = str; + while (((i = strlen(ptr)) > 0) && (j < len)) { + rc = hashmap_add(list, ptr, ""); + if (rc != 0) { + free(str); + return rc; + } + j += i+1; + if (j < len) + ptr += i+1; + } + + free(str); + return rc; +} + +/** + * @brief Extract a key value pair and add it to a hashmap + * @param str Input string which contains a key value pair. + * @param env The updated handle which contains the new pair. + * @return 0 + * @return or error + * @note The input string format is: "key=value" + */ +static int +extract_pair(const char *str, bootenv_t env) +{ + int rc = 0; + char *key = NULL; + char *val = NULL; + + key = strdup(str); + if (key == NULL) + return -ENOMEM; + + val = strstr(key, "="); + if (val == NULL) { + rc = BOOTENV_EBADENTRY; + goto err; + } + + *val = '\0'; /* split strings */ + val++; + + rc = bootenv_set(env, key, val); + + err: + free(key); + return rc; +} + +int +bootenv_destroy(bootenv_t* env) +{ + int rc = 0; + + if (env == NULL || *env == NULL) + return -EINVAL; + + bootenv_t tmp = *env; + + rc = hashmap_free(tmp->map); + if (rc != 0) + return rc; + + free(tmp); + return rc; +} + +int +bootenv_create(bootenv_t* env) +{ + bootenv_t res; + res = (bootenv_t) calloc(1, sizeof(struct bootenv)); + + if (res == NULL) + return -ENOMEM; + + res->map = hashmap_new(); + + if (res->map == NULL) { + free(res); + return -ENOMEM; + } + + *env = res; + + return 0; +} + + +/** + * @brief Read a formatted buffer and scan it for valid bootenv + * key/value pairs. Add those pairs into a hashmap. + * @param env Hashmap which shall be used to hold the data. + * @param buf Formatted buffer. + * @param size Size of the buffer. + * @return 0 + * @return or error + */ +static int +rd_buffer(bootenv_t env, const char *buf, size_t size) +{ + const char *curr = buf; /* ptr to current key/value pair */ + uint32_t i, j; /* current length, chars processed */ + + if (buf[size - 1] != '\0') /* must end in '\0' */ + return BOOTENV_EFMT; + + for (j = 0; j < size; j += i, curr += i) { + /* strlen returns the size of the string upto + but not including the null terminator; + adding 1 to account for '\0' */ + i = strlen(curr) + 1; + + if (i == 1) + return 0; /* no string found */ + + if (extract_pair(curr, env) != 0) + return BOOTENV_EINVAL; + } + + return 0; +} + + +int +bootenv_read_crc(FILE* fp, bootenv_t env, size_t size, uint32_t* ret_crc) +{ + int rc; + char *buf = NULL; + size_t i = 0; + uint32_t crc32_table[256]; + + if ((fp == NULL) || (env == NULL)) + return -EINVAL; + + /* allocate temp buffer */ + buf = (char*) calloc(1, size * sizeof(char)); + if (buf == NULL) + return -ENOMEM; + + /* FIXME Andreas, please review this I removed size-1 and + * replaced it by just size, I saw the kernel image starting + * with a 0x0060.... and not with the 0x60.... what it should + * be. Is this a tools problem or is it a problem here where + * fp is moved not to the right place due to the former size-1 + * here. + */ + while((i < size) && (!feof(fp))) { + int c = fgetc(fp); + if (c == EOF) { + /* FIXME isn't this dangerous, to update + the boot envs with incomplete data? */ + buf[i++] = '\0'; + break; /* we have enough */ + } + if (ferror(fp)) { + rc = -EIO; + goto err; + } + + buf[i++] = (char)c; + } + + /* calculate crc to return */ + if (ret_crc != NULL) { + init_crc32_table(crc32_table); + *ret_crc = clc_crc32(crc32_table, UBI_CRC32_INIT, buf, size); + } + + /* transfer to hashmap */ + rc = rd_buffer(env, buf, size); + +err: + free(buf); + return rc; +} + + +/** + * If we have a single file containing the boot-parameter size should + * be specified either as the size of the file or as BOOTENV_MAXSIZE. + * If the bootparameter are in the middle of a file we need the exact + * length of the data. + */ +int +bootenv_read(FILE* fp, bootenv_t env, size_t size) +{ + return bootenv_read_crc(fp, env, size, NULL); +} + + +int +bootenv_read_txt(FILE* fp, bootenv_t env) +{ + int rc = 0; + char *buf = NULL; + char *line = NULL; + char *lstart = NULL; + char *curr = NULL; + size_t len; + size_t size; + + if ((fp == NULL) || (env == NULL)) + return -EINVAL; + + size = BOOTENV_MAXSIZE; + + /* allocate temp buffers */ + buf = (char*) calloc(1, size * sizeof(char)); + lstart = line = (char*) calloc(1, size * sizeof(char)); + if ((buf == NULL) || (line == NULL)) { + rc = -ENOMEM; + goto err; + } + + curr = buf; + while ((line = fgets(line, size, fp)) != NULL) { + if (is_ws(line, size)) { + continue; + } + rc = remove_lf(line, BOOTENV_MAXSIZE, fp); + if (rc != 0) { + goto err; + } + + /* copy new line to binary buffer */ + len = strlen(line); + if (len > size) { + rc = -EFBIG; + goto err; + } + size -= len; /* track remaining space */ + + memcpy(curr, line, len); + curr += len + 1; /* for \0 seperator */ + } + + rc = rd_buffer(env, buf, BOOTENV_MAXSIZE); +err: + if (buf != NULL) + free(buf); + if (lstart != NULL) + free(lstart); + return rc; +} + +static int +fill_output_buffer(bootenv_t env, char *buf, size_t buf_size_max ubi_unused, + size_t *written) +{ + int rc = 0; + size_t keys_size, i; + size_t wr = 0; + const char **keys = NULL; + const char *val = NULL; + + rc = bootenv_get_key_vector(env, &keys_size, 1, &keys); + if (rc != 0) + goto err; + + for (i = 0; i < keys_size; i++) { + if (wr > BOOTENV_MAXSIZE) { + rc = -ENOSPC; + goto err; + } + + rc = bootenv_get(env, keys[i], &val); + if (rc != 0) + goto err; + + wr += snprintf(buf + wr, BOOTENV_MAXSIZE - wr, + "%s=%s", keys[i], val); + wr++; /* for \0 */ + } + + *written = wr; + +err: + if (keys != NULL) + free(keys); + + return rc; +} + +int +bootenv_write_crc(FILE* fp, bootenv_t env, uint32_t* ret_crc) +{ + int rc = 0; + size_t size = 0; + char *buf = NULL; + uint32_t crc32_table[256]; + + if ((fp == NULL) || (env == NULL)) + return -EINVAL; + + buf = (char*) calloc(1, BOOTENV_MAXSIZE * sizeof(char)); + if (buf == NULL) + return -ENOMEM; + + + rc = fill_output_buffer(env, buf, BOOTENV_MAXSIZE, &size); + if (rc != 0) + goto err; + + /* calculate crc to return */ + if (ret_crc != NULL) { + init_crc32_table(crc32_table); + *ret_crc = clc_crc32(crc32_table, UBI_CRC32_INIT, buf, size); + } + + if (fwrite(buf, size, 1, fp) != 1) { + rc = -EIO; + goto err; + } + +err: + if (buf != NULL) + free(buf); + return rc; +} + +int +bootenv_write(FILE* fp, bootenv_t env) +{ + return bootenv_write_crc(fp, env, NULL); +} + +int +bootenv_compare(bootenv_t first, bootenv_t second) +{ + int rc; + size_t written_first, written_second; + char *buf_first, *buf_second; + + if (first == NULL || second == NULL) + return -EINVAL; + + buf_first = malloc(BOOTENV_MAXSIZE); + if (!buf_first) + return -ENOMEM; + buf_second = malloc(BOOTENV_MAXSIZE); + if (!buf_second) { + rc = -ENOMEM; + goto err; + } + + rc = fill_output_buffer(first, buf_first, BOOTENV_MAXSIZE, + &written_first); + if (rc < 0) + goto err; + rc = fill_output_buffer(second, buf_second, BOOTENV_MAXSIZE, + &written_second); + if (rc < 0) + goto err; + + if (written_first != written_second) { + rc = 1; + goto err; + } + + rc = memcmp(buf_first, buf_second, written_first); + if (rc != 0) { + rc = 2; + goto err; + } + +err: + if (buf_first) + free(buf_first); + if (buf_second) + free(buf_second); + + return rc; +} + +int +bootenv_size(bootenv_t env, size_t *size) +{ + int rc = 0; + char *buf = NULL; + + if (env == NULL) + return -EINVAL; + + buf = (char*) calloc(1, BOOTENV_MAXSIZE * sizeof(char)); + if (buf == NULL) + return -ENOMEM; + + rc = fill_output_buffer(env, buf, BOOTENV_MAXSIZE, size); + if (rc != 0) + goto err; + +err: + if (buf != NULL) + free(buf); + return rc; +} + +int +bootenv_write_txt(FILE* fp, bootenv_t env) +{ + int rc = 0; + size_t size, wr, i; + const char **keys = NULL; + const char *key = NULL; + const char *val = NULL; + + if ((fp == NULL) || (env == NULL)) + return -EINVAL; + + rc = bootenv_get_key_vector(env, &size, 1, &keys); + if (rc != 0) + goto err; + + for (i = 0; i < size; i++) { + key = keys[i]; + rc = bootenv_get(env, key, &val); + if (rc != 0) + goto err; + + wr = fprintf(fp, "%s=%s\n", key, val); + if (wr != strlen(key) + strlen(val) + 2) { + rc = -EIO; + goto err; + } + } + +err: + if (keys != NULL) + free(keys); + return rc; +} + +int +bootenv_valid(bootenv_t env ubi_unused) +{ + /* @FIXME No sanity check implemented. */ + return 0; +} + +int +bootenv_copy_bootenv(bootenv_t in, bootenv_t *out) +{ + int rc = 0; + const char *tmp = NULL; + const char **keys = NULL; + size_t vec_size, i; + + if ((in == NULL) || (out == NULL)) + return -EINVAL; + + /* purge output var for sure... */ + rc = bootenv_destroy(out); + if (rc != 0) + return rc; + + /* create the new map */ + rc = bootenv_create(out); + if (rc != 0) + goto err; + + /* get the key list from the input map */ + rc = bootenv_get_key_vector(in, &vec_size, 0, &keys); + if (rc != 0) + goto err; + + if (vec_size != hashmap_size(in->map)) { + rc = BOOTENV_ECOPY; + goto err; + } + + /* make a deep copy of the hashmap */ + for (i = 0; i < vec_size; i++) { + rc = bootenv_get(in, keys[i], &tmp); + if (rc != 0) + goto err; + + rc = bootenv_set(*out, keys[i], tmp); + if (rc != 0) + goto err; + } + +err: + if (keys != NULL) + free(keys); + + return rc; +} + +/* ------------------------------------------------------------------------- */ + + +int +bootenv_pdd_keep(bootenv_t env_old, bootenv_t env_new, bootenv_t *env_res, + int *warnings, char *err_buf ubi_unused, + size_t err_buf_size ubi_unused) +{ + bootenv_list_t l_old = NULL; + bootenv_list_t l_new = NULL; + const char *pdd_old = NULL; + const char *pdd_new = NULL; + const char *tmp = NULL; + const char **vec_old = NULL; + const char **vec_new = NULL; + const char **pdd_up_vec = NULL; + size_t vec_old_size, vec_new_size, pdd_up_vec_size, i; + int rc = 0; + + if ((env_old == NULL) || (env_new == NULL) || (env_res == NULL)) + return -EINVAL; + + /* get the pdd strings, e.g.: + * pdd_old=a,b,c + * pdd_new=a,c,d,e */ + rc = bootenv_get(env_old, "pdd", &pdd_old); + if (rc != 0) + goto err; + rc = bootenv_get(env_new, "pdd", &pdd_new); + if (rc != 0) + goto err; + + /* put it into a list and then convert it to an vector */ + rc = bootenv_list_create(&l_old); + if (rc != 0) + goto err; + rc = bootenv_list_create(&l_new); + if (rc != 0) + goto err; + + rc = bootenv_list_import(l_old, pdd_old); + if (rc != 0) + goto err; + + rc = bootenv_list_import(l_new, pdd_new); + if (rc != 0) + goto err; + + rc = bootenv_list_to_vector(l_old, &vec_old_size, &vec_old); + if (rc != 0) + goto err; + + rc = bootenv_list_to_vector(l_new, &vec_new_size, &vec_new); + if (rc != 0) + goto err; + + rc = bootenv_copy_bootenv(env_new, env_res); + if (rc != 0) + goto err; + + /* calculate the update vector between the old and new pdd */ + pdd_up_vec = hashmap_get_update_key_vector(vec_old, vec_old_size, + vec_new, vec_new_size, &pdd_up_vec_size); + + if (pdd_up_vec == NULL) { + rc = -ENOMEM; + goto err; + } + + if (pdd_up_vec_size != 0) { + /* need to warn the user about the unset of + * some pdd/bootenv values */ + *warnings = BOOTENV_WPDD_STRING_DIFFERS; + + /* remove all entries in the new bootenv load */ + for (i = 0; i < pdd_up_vec_size; i++) { + bootenv_unset(*env_res, pdd_up_vec[i]); + } + } + + /* generate the keep array and copy old pdd values to new bootenv */ + for (i = 0; i < vec_old_size; i++) { + rc = bootenv_get(env_old, vec_old[i], &tmp); + if (rc != 0) { + rc = BOOTENV_EPDDINVAL; + goto err; + } + rc = bootenv_set(*env_res, vec_old[i], tmp); + if (rc != 0) { + goto err; + } + } + /* put the old pdd string into the result map */ + rc = bootenv_set(*env_res, "pdd", pdd_old); + if (rc != 0) { + goto err; + } + + +err: + if (vec_old != NULL) + free(vec_old); + if (vec_new != NULL) + free(vec_new); + if (pdd_up_vec != NULL) + free(pdd_up_vec); + + bootenv_list_destroy(&l_old); + bootenv_list_destroy(&l_new); + return rc; +} + + +int +bootenv_pdd_overwrite(bootenv_t env_old, bootenv_t env_new, + bootenv_t *env_res, int *warnings ubi_unused, + char *err_buf ubi_unused, size_t err_buf_size ubi_unused) +{ + if ((env_old == NULL) || (env_new == NULL) || (env_res == NULL)) + return -EINVAL; + + return bootenv_copy_bootenv(env_new, env_res); +} + +int +bootenv_pdd_merge(bootenv_t env_old, bootenv_t env_new, bootenv_t *env_res, + int *warnings ubi_unused, char *err_buf, size_t err_buf_size) +{ + if ((env_old == NULL) || (env_new == NULL) || (env_res == NULL)) + return -EINVAL; + + snprintf(err_buf, err_buf_size, "The PDD merge operation is not " + "implemented. Contact: <oliloh@de.ibm.com>"); + + return BOOTENV_ENOTIMPL; +} + +/* ------------------------------------------------------------------------- */ + +int +bootenv_get(bootenv_t env, const char *key, const char **value) +{ + if (env == NULL) + return -EINVAL; + + *value = hashmap_lookup(env->map, key); + if (*value == NULL) + return BOOTENV_ENOTFOUND; + + return 0; +} + +int +bootenv_get_num(bootenv_t env, const char *key, uint32_t *value) +{ + char *endptr = NULL; + const char *str; + + if (env == NULL) + return 0; + + str = hashmap_lookup(env->map, key); + if (!str) + return -EINVAL; + + *value = strtoul(str, &endptr, 0); + + if (*endptr == '\0') { + return 0; + } + + return -EINVAL; +} + +int +bootenv_set(bootenv_t env, const char *key, const char *value) +{ + if (env == NULL) + return -EINVAL; + + return hashmap_add(env->map, key, value); +} + +int +bootenv_unset(bootenv_t env, const char *key) +{ + if (env == NULL) + return -EINVAL; + + return hashmap_remove(env->map, key); +} + +int +bootenv_get_key_vector(bootenv_t env, size_t* size, int sort, + const char ***vector) +{ + if ((env == NULL) || (size == NULL)) + return -EINVAL; + + *vector = hashmap_get_key_vector(env->map, size, sort); + + if (*vector == NULL) + return -EINVAL; + + return 0; +} + +int +bootenv_dump(bootenv_t env) +{ + if (env == NULL) + return -EINVAL; + + return hashmap_dump(env->map); +} + +int +bootenv_list_create(bootenv_list_t *list) +{ + bootenv_list_t res; + res = (bootenv_list_t) calloc(1, sizeof(struct bootenv_list)); + + if (res == NULL) + return -ENOMEM; + + res->head = hashmap_new(); + + if (res->head == NULL) { + free(res); + return -ENOMEM; + } + + *list = res; + return 0; +} + +int +bootenv_list_destroy(bootenv_list_t *list) +{ + int rc = 0; + + if (list == NULL) + return -EINVAL; + + bootenv_list_t tmp = *list; + if (tmp == 0) + return 0; + + rc = hashmap_free(tmp->head); + if (rc != 0) + return rc; + + free(tmp); + *list = NULL; + return 0; +} + +int +bootenv_list_import(bootenv_list_t list, const char *str) +{ + if (list == NULL) + return -EINVAL; + + return build_list_definition(list->head, str); +} + +int +bootenv_list_export(bootenv_list_t list, char **string) +{ + size_t size, i, j, bufsize, tmp, rc = 0; + const char **items; + + if (list == NULL) + return -EINVAL; + + bufsize = BOOTENV_MAXLINE; + char *res = (char*) malloc(bufsize * sizeof(char)); + if (res == NULL) + return -ENOMEM; + + rc = bootenv_list_to_vector(list, &size, &items); + if (rc != 0) { + goto err; + } + + j = 0; + for (i = 0; i < size; i++) { + tmp = strlen(items[i]); + if (j >= bufsize) { + bufsize += BOOTENV_MAXLINE; + res = (char*) realloc(res, bufsize * sizeof(char)); + if (res == NULL) { + rc = -ENOMEM; + goto err; + } + } + memcpy(res + j, items[i], tmp); + j += tmp; + if (i < (size - 1)) { + res[j] = ','; + j++; + } + } + j++; + res[j] = '\0'; + free(items); + *string = res; + return 0; +err: + free(items); + return rc; +} + +int +bootenv_list_add(bootenv_list_t list, const char *item) +{ + if ((list == NULL) || (item == NULL)) + return -EINVAL; + + return hashmap_add(list->head, item, ""); +} + +int +bootenv_list_remove(bootenv_list_t list, const char *item) +{ + if ((list == NULL) || (item == NULL)) + return -EINVAL; + + return hashmap_remove(list->head, item); +} + +int +bootenv_list_is_in(bootenv_list_t list, const char *item) +{ + if ((list == NULL) || (item == NULL)) + return -EINVAL; + + return hashmap_lookup(list->head, item) != NULL ? 1 : 0; +} + +int +bootenv_list_to_vector(bootenv_list_t list, size_t *size, const char ***vector) +{ + if ((list == NULL) || (size == NULL)) + return -EINVAL; + + *vector = hashmap_get_key_vector(list->head, size, 1); + if (*vector == NULL) + return -ENOMEM; + + return 0; +} + +int +bootenv_list_to_num_vector(bootenv_list_t list, size_t *size, + uint32_t **vector) +{ + int rc = 0; + size_t i; + uint32_t* res = NULL; + char *endptr = NULL; + const char **a = NULL; + + rc = bootenv_list_to_vector(list, size, &a); + if (rc != 0) + goto err; + + res = (uint32_t*) malloc (*size * sizeof(uint32_t)); + if (!res) + goto err; + + for (i = 0; i < *size; i++) { + res[i] = strtoul(a[i], &endptr, 0); + if (*endptr != '\0') + goto err; + } + + if (a) + free(a); + *vector = res; + return 0; + +err: + if (a) + free(a); + if (res) + free(res); + return rc; +} diff --git a/ubi-utils/old-tools/src/bootenv.h b/ubi-utils/old-tools/src/bootenv.h new file mode 100644 index 0000000..8fecdbf --- /dev/null +++ b/ubi-utils/old-tools/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 <stdio.h> /* FILE */ +#include <stdint.h> +#include <pfiflash.h> + +/* DOXYGEN DOCUMENTATION */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file bootenv.h + * @author oliloh@de.ibm.com + * @version 1.3 + * + * 1.3 Some renaming + */ + +/** + * @mainpage Usage + * + * @section intro Introduction + * This library provides all functionality to handle with the so-called + * platform description data (PDD) and the bootparameters defined in + * U-Boot. It is able to apply the defined PDD operations in PDD update + * scenarios. For more information about the PDD and bootparameter + * environment "bootenv" confer the PDD documentation. + * + * @section ret Return codes + * This library defines some return codes which will be delivered classified + * as warnings or errors. See the "Defines" section for details and numeric + * values. + * + * @section benv Bootenv format description + * There are two different input formats: + * - text files + * - binary files + * + * @subsection txt Text Files + * Text files have to be specified like: + * @verbatim key1=value1,value2,value7\n key2=value55,value1\n key4=value1\n@endverbatim + * + * @subsection bin Binary files + * Binary files have to be specified like: + * @verbatim<CRC32-bit>key1=value1,value2,value7\0key2=value55,value1\0... @endverbatim + * You can confer the U-Boot documentation for more details. + * + * @section benvlists Bootenv lists format description. + * Values referenced in the preceeding subsection can be + * defined like lists: + * @verbatim value1,value2,value3 @endverbatim + * There are some situation where a conversion of a comma + * seperated list can be useful, e.g. to get a list + * of defined PDD entries. + */ + +#define BOOTENV_MAXSIZE (1024 * 100) /* max 100kiB space for bootenv */ + +/** + * @def BOOTENV_ECRC + * @brief Given binary file is to large. + * @def BOOTENV_EFMT + * @brief Given bootenv section has an invalid format + * @def BOOTENV_EBADENTRY + * @brief Bad entry in the bootenv section. + * @def BOOTENV_EINVAL + * @brief Invalid bootenv defintion. + * @def BOOTENV_ENOPDD + * @brief Given bootenv sectoin has no PDD defintion string (pdd=...). + * @def BOOTENV_EPDDINVAL + * @brief Given bootenv section has an invalid PDD defintion. + * @def BOOTENV_ENOTIMPL + * @brief Functionality not implemented. + * @def BOOTENV_ECOPY + * @brief Bootenv memory copy error + * @def BOOTENV_ENOTFOUND + * @brief Given key has has no value. + * @def BOOTENV_EMAX + * @brief Highest error value. + */ +#define BOOTENV_ETOOBIG 1 +#define BOOTENV_EFMT 2 +#define BOOTENV_EBADENTRY 3 +#define BOOTENV_EINVAL 4 +#define BOOTENV_ENOPDD 5 +#define BOOTENV_EPDDINVAL 6 +#define BOOTENV_ENOTIMPL 7 +#define BOOTENV_ECOPY 8 +#define BOOTENV_ENOTFOUND 9 +#define BOOTENV_EMAX 10 + +/** + * @def BOOTENV_W + * @brief A warning which is handled internally as an error + * but can be recovered by manual effort. + * @def BOOTENV_WPDD_STRING_DIFFERS + * @brief The PDD strings of old and new PDD differ and + * can cause update problems, because new PDD values + * are removed from the bootenv section completely. + */ +#define BOOTENV_W 20 +#define BOOTENV_WPDD_STRING_DIFFERS 21 +#define BOOTENV_WMAX 22 /* highest warning value */ + + +typedef struct bootenv *bootenv_t; + /**< A bootenv library handle. */ + +typedef struct bootenv_list *bootenv_list_t; + /**< A handle for a value list. */ + +typedef int(*pdd_func_t)(bootenv_t, bootenv_t, bootenv_t*, + int*, char*, size_t); + + +/** + * @brief Get a new handle. + * @return 0 + * @return or error + * */ +int bootenv_create(bootenv_t *env); + +/** + * @brief Cleanup structure. + * @param env Bootenv structure which shall be destroyed. + * @return 0 + * @return or error + */ +int bootenv_destroy(bootenv_t *env); + +/** + * @brief Copy a bootenv handle. + * @param in The input bootenv. + * @param out The copied output bootenv. Discards old data. + * @return 0 + * @return or error + */ +int bootenv_copy_bootenv(bootenv_t in, bootenv_t *out); + +/** + * @brief Looks for a value inside the bootenv data. + * @param env Handle to a bootenv structure. + * @param key The key. + * @return NULL key not found + * @return !NULL ptr to value + */ +int bootenv_get(bootenv_t env, const char *key, const char **value); + + +/** + * @brief Looks for a value inside the bootenv data and converts it to num. + * @param env Handle to a bootenv structure. + * @param key The key. + * @param value A pointer to the resulting numerical value + * @return NULL key not found + * @return !NULL ptr to value + */ +int bootenv_get_num(bootenv_t env, const char *key, uint32_t *value); + +/** + * @brief Set a bootenv value by key. + * @param env Handle to a bootenv structure. + * @param key Key. + * @param value Value to set. + * @return 0 + * @return or error + */ +int bootenv_set(bootenv_t env, const char *key, const char *value); + +/** + * @brief Remove the given key (and its value) from a bootenv structure. + * @param env Handle to a bootenv structure. + * @param key Key. + * @return 0 + * @return or error + */ +int bootenv_unset(bootenv_t env, const char *key); + + +/** + * @brief Get a vector of all keys which are currently set + * within a bootenv handle. + * @param env Handle to a bootenv structure. + * @param size The size of the allocated array structure. + * @param sort Flag, if set the vector is sorted ascending. + * @return NULL on error. + * @return !NULL a pointer to the first element the allocated vector. + * @warning Free the allocate memory yourself! + */ +int bootenv_get_key_vector(bootenv_t env, size_t *size, int sort, + const char ***vector); + +/** + * @brief Calculate the size in bytes which are necessary to write the + * current bootenv section in a *binary file. + * @param env bootenv handle. + * @param size The size in bytes of the bootenv handle. + * @return 0 + * @return or ERROR. + */ +int bootenv_size(bootenv_t env, size_t *size); + +/** + * @brief Read a binary bootenv file. + * @param fp File pointer to input stream. + * @param env bootenv handle. + * @param size maximum data size. + * @return 0 + * @return or ERROR. + */ +int bootenv_read(FILE* fp, bootenv_t env, size_t size); + +/** + * @param ret_crc return value of crc of read data + */ +int bootenv_read_crc(FILE* fp, bootenv_t env, size_t size, uint32_t *ret_crc); + +/** + * @brief Read bootenv data from an text/ascii file. + * @param fp File pointer to ascii PDD file. + * @param env bootenv handle + * @return 0 + * @return or ERROR. + */ +int bootenv_read_txt(FILE* fp, bootenv_t env); + +/** + * @brief Write a bootenv structure to the given location (binary). + * @param fp Filepointer to binary file. + * @param env Bootenv structure which shall be written. + * @return 0 + * @return or error + */ +int bootenv_write(FILE* fp, bootenv_t env); + +/** + * @param ret_crc return value of crc of read data + */ +int bootenv_write_crc(FILE* fp, bootenv_t env, uint32_t* ret_crc); + +/** + * @brief Write a bootenv structure to the given location (text). + * @param fp Filepointer to text file. + * @param env Bootenv structure which shall be written. + * @return 0 + * @return or error + */ +int bootenv_write_txt(FILE* fp, bootenv_t env); + +/** + * @brief Compare bootenvs using memcmp(). + * @param first First bootenv. + * @param second Second bootenv. + * @return 0 if bootenvs are equal + * @return < 0 if error + * @return > 0 if unequal + */ +int bootenv_compare(bootenv_t first, bootenv_t second); + +/** + * @brief Prototype for a PDD handling funtion + */ + +/** + * @brief The PDD keep operation. + * @param env_old The old bootenv structure. + * @param env_new The new bootenv structure. + * @param env_res The result of PDD keep. + * @param warnings A flag which marks any warnings. + * @return 0 + * @return or error + * @note For a complete documentation about the algorithm confer the + * PDD documentation. + */ +int bootenv_pdd_keep(bootenv_t env_old, bootenv_t env_new, + bootenv_t *env_res, int *warnings, + char *err_buf, size_t err_buf_size); + + +/** + * @brief The PDD merge operation. + * @param env_old The old bootenv structure. + * @param env_new The new bootenv structure. + * @param env_res The result of merge-pdd. + * @param warnings A flag which marks any warnings. + * @return 0 + * @return or error + * @note For a complete documentation about the algorithm confer the + * PDD documentation. + */ +int bootenv_pdd_merge(bootenv_t env_old, bootenv_t env_new, + bootenv_t *env_res, int *warnings, + char *err_buf, size_t err_buf_size); + +/** + * @brief The PDD overwrite operation. + * @param env_old The old bootenv structure. + * @param env_new The new bootenv structure. + * @param env_res The result of overwrite-pdd. + * @param warnings A flag which marks any warnings. + * @return 0 + * @return or error + * @note For a complete documentation about the algorithm confer the + * PDD documentation. + */ +int bootenv_pdd_overwrite(bootenv_t env_new, + bootenv_t env_old, bootenv_t *env_res, int *warnings, + char *err_buf, size_t err_buf_size); + +/** + * @brief Dump a bootenv structure to stdout. (Debug) + * @param env Handle to a bootenv structure. + * @return 0 + * @return or error + */ +int bootenv_dump(bootenv_t env); + +/** + * @brief Validate a bootenv structure. + * @param env Handle to a bootenv structure. + * @return 0 + * @return or error + */ +int bootenv_valid(bootenv_t env); + +/** + * @brief Create a new bootenv list structure. + * @return NULL on error + * @return or a new list handle. + * @note This structure is used to store values in a list. + * A useful addition when handling PDD strings. + */ +int bootenv_list_create(bootenv_list_t *list); + +/** + * @brief Destroy a bootenv list structure + * @param list Handle to a bootenv list structure. + * @return 0 + * @return or error + */ +int bootenv_list_destroy(bootenv_list_t *list); + +/** + * @brief Import a list from a comma seperated string + * @param list Handle to a bootenv list structure. + * @param str Comma seperated string list. + * @return 0 + * @return or error + */ +int bootenv_list_import(bootenv_list_t list, const char *str); + +/** + * @brief Export a list to a string of comma seperated values. + * @param list Handle to a bootenv list structure. + * @return NULL one error + * @return or pointer to a newly allocated string. + * @warning Free the allocated memory by yourself! + */ +int bootenv_list_export(bootenv_list_t list, char **string); + +/** + * @brief Add an item to the list. + * @param list A handle of a list structure. + * @param item An item. + * @return 0 + * @return or error + */ +int bootenv_list_add(bootenv_list_t list, const char *item); + +/** + * @brief Remove an item from the list. + * @param list A handle of a list structure. + * @param item An item. + * @return 0 + * @return or error + */ +int bootenv_list_remove(bootenv_list_t list, const char *item); + +/** + * @brief Check if a given item is in a given list. + * @param list A handle of a list structure. + * @param item An item. + * @return 1 Item is in list. + * @return 0 Item is not in list. + */ +int bootenv_list_is_in(bootenv_list_t list, const char *item); + + +/** + * @brief Convert a list into a vector of all values inside the list. + * @param list Handle to a bootenv structure. + * @param size The size of the allocated vector structure. + * @return 0 + * @return or error + * @warning Free the allocate memory yourself! + */ +int bootenv_list_to_vector(bootenv_list_t list, size_t *size, + const char ***vector); + +/** + * @brief Convert a list into a vector of all values inside the list. + * @param list Handle to a bootenv structure. + * @param size The size of the allocated vector structure. + * @return 0 + * @return or error + * @warning Free the allocate memory yourself! + */ +int bootenv_list_to_num_vector(bootenv_list_t list, size_t *size, + uint32_t **vector); + +#ifdef __cplusplus +} +#endif +#endif /*__BOOTENV_H__ */ diff --git a/ubi-utils/old-tools/src/common.c b/ubi-utils/old-tools/src/common.c new file mode 100644 index 0000000..7ed1ade --- /dev/null +++ b/ubi-utils/old-tools/src/common.c @@ -0,0 +1,89 @@ +/* + * 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. + */ + +/* + * This file contains various common stuff used by UBI utilities. + * + * Author: Artem Bityutskiy + */ + +#include <stdio.h> +#include <string.h> + +/** + * ubiutils_bytes_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. + */ +int ubiutils_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_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) + printf("%s%.1f KiB", p, (double)bytes / 1024); + else + return; + + if (bracket) + printf(")"); +} diff --git a/ubi-utils/old-tools/src/common.h b/ubi-utils/old-tools/src/common.h new file mode 100644 index 0000000..06ae623 --- /dev/null +++ b/ubi-utils/old-tools/src/common.h @@ -0,0 +1,43 @@ +/* + * 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__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Error messages */ +#define errmsg(fmt, ...) do { \ + fprintf(stderr, PROGRAM_NAME " error: " fmt "\n", ##__VA_ARGS__); \ +} while(0) + +/* Warnings */ +#define warnmsg(fmt, ...) do { \ + fprintf(stderr, PROGRAM_NAME " warning: " fmt "\n", ##__VA_ARGS__); \ +} while(0) + +int ubiutils_get_multiplier(const char *str); +void ubiutils_print_bytes(long long bytes, int bracket); + +#ifdef __cplusplus +} +#endif + +#endif /* !__UBI_UTILS_COMMON_H__ */ diff --git a/ubi-utils/old-tools/src/config.h b/ubi-utils/old-tools/src/config.h new file mode 100644 index 0000000..55e60f3 --- /dev/null +++ b/ubi-utils/old-tools/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/old-tools/src/crc32.c b/ubi-utils/old-tools/src/crc32.c new file mode 100644 index 0000000..666e217 --- /dev/null +++ b/ubi-utils/old-tools/src/crc32.c @@ -0,0 +1,83 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Thomas Gleixner + */ + +/* + * CRC32 functions + * + * Can be compiled as seperate object, but is included into the ipl source + * so gcc can inline the functions. We optimize for size so the omission of + * the function frame is helpful. + * + */ + +#include <stdint.h> +#include <crc32.h> + +/* CRC polynomial */ +#define CRC_POLY 0xEDB88320 + +/** + * init_crc32_table - Initialize crc table + * + * @table: pointer to the CRC table which must be initialized + * + * Create CRC32 table for given polynomial. The table is created with + * the lowest order term in the highest order bit. So the x^32 term + * has to implied in the crc calculation function. + */ +void init_crc32_table(uint32_t *table) +{ + uint32_t crc; + int i, j; + + for (i = 0; i < 256; i++) { + crc = i; + for (j = 8; j > 0; j--) { + if (crc & 1) + crc = (crc >> 1) ^ CRC_POLY; + else + crc >>= 1; + } + table[i] = crc; + } +} + +/** + * clc_crc32 - Calculate CRC32 over a buffer + * + * @table: pointer to the CRC table + * @crc: initial crc value + * @buf: pointer to the buffer + * @len: number of bytes to calc + * + * Returns the updated crc value. + * + * The algorithm resembles a hardware shift register, but calculates 8 + * bit at once. + */ +uint32_t clc_crc32(uint32_t *table, uint32_t crc, void *buf, + int len) +{ + const unsigned char *p = buf; + + while(--len >= 0) + crc = table[(crc ^ *p++) & 0xff] ^ (crc >> 8); + return crc; +} diff --git a/ubi-utils/old-tools/src/crc32.h b/ubi-utils/old-tools/src/crc32.h new file mode 100644 index 0000000..31362b0 --- /dev/null +++ b/ubi-utils/old-tools/src/crc32.h @@ -0,0 +1,36 @@ +#ifndef __CRC32_H__ +#define __CRC32_H__ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * Author: Thomas Gleixner + * + * CRC32 functions + * + * Can be compiled as seperate object, but is included into the ipl source + * so gcc can inline the functions. We optimize for size so the omission of + * the function frame is helpful. + * + */ +#include <stdint.h> + +void init_crc32_table(uint32_t *table); +uint32_t clc_crc32(uint32_t *table, uint32_t crc, void *buf, int len); + +#endif /* __CRC32_H__ */ diff --git a/ubi-utils/old-tools/src/eb_chain.c b/ubi-utils/old-tools/src/eb_chain.c new file mode 100644 index 0000000..a018ae6 --- /dev/null +++ b/ubi-utils/old-tools/src/eb_chain.c @@ -0,0 +1,280 @@ +/* + * Copyright (c) International Business Machines Corp., 2006, 2007 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * Author: Drake Dowsett, dowsett@de.ibm.com + * Contact: Andreas Arnez, arnez@de.ibm.com + */ + +/* see eb_chain.h */ + +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include "unubi_analyze.h" +#include "crc32.h" + +#define COPY(dst, src) \ + do { \ + dst = malloc(sizeof(*dst)); \ + if (dst == NULL) \ + return -ENOMEM; \ + memcpy(dst, src, sizeof(*dst)); \ + } while (0) + + +/** + * inserts an eb_info into the chain starting at head, then searching + * linearly for the correct position; + * new should contain valid vid and ec headers and the data_crc should + * already have been checked before insertion, otherwise the chain + * could be have un an undesired manner; + * returns -ENOMEM if alloc fails, otherwise SHOULD always return 0, + * if not, the code reached the last line and returned -EAGAIN, + * meaning there is a bug or a case not being handled here; + **/ +int +eb_chain_insert(struct eb_info **head, struct eb_info *new) +{ + uint32_t vol, num, ver; + uint32_t new_vol, new_num, new_ver; + struct eb_info *prev, *cur, *hist, *ins; + struct eb_info **prev_ptr; + + if ((head == NULL) || (new == NULL)) + return 0; + + if (*head == NULL) { + COPY(*head, new); + (*head)->next = NULL; + return 0; + } + + new_vol = __be32_to_cpu(new->vid.vol_id); + new_num = __be32_to_cpu(new->vid.lnum); + new_ver = __be32_to_cpu(new->vid.leb_ver); + + /** TRAVERSE HORIZONTALY **/ + + cur = *head; + prev = NULL; + + /* traverse until vol_id/lnum align */ + vol = __be32_to_cpu(cur->vid.vol_id); + num = __be32_to_cpu(cur->vid.lnum); + while ((new_vol > vol) || ((new_vol == vol) && (new_num > num))) { + /* insert new at end of chain */ + if (cur->next == NULL) { + COPY(ins, new); + ins->next = NULL; + cur->next = ins; + return 0; + } + + prev = cur; + cur = cur->next; + vol = __be32_to_cpu(cur->vid.vol_id); + num = __be32_to_cpu(cur->vid.lnum); + } + + if (prev == NULL) + prev_ptr = head; + else + prev_ptr = &(prev->next); + + /* insert new into the middle of chain */ + if ((new_vol != vol) || (new_num != num)) { + COPY(ins, new); + ins->next = cur; + *prev_ptr = ins; + return 0; + } + + /** TRAVERSE VERTICALY **/ + + hist = cur; + prev = NULL; + + /* traverse until versions align */ + ver = __be32_to_cpu(cur->vid.leb_ver); + while (new_ver < ver) { + /* insert new at bottom of history */ + if (hist->older == NULL) { + COPY(ins, new); + ins->next = NULL; + ins->older = NULL; + hist->older = ins; + return 0; + } + + prev = hist; + hist = hist->older; + ver = __be32_to_cpu(hist->vid.leb_ver); + } + + if (prev == NULL) { + /* replace active version */ + COPY(ins, new); + ins->next = hist->next; + *prev_ptr = ins; + + /* place cur in vertical histroy */ + ins->older = hist; + hist->next = NULL; + return 0; + } + + /* insert between versions, beneath active version */ + COPY(ins, new); + ins->next = NULL; + ins->older = prev->older; + prev->older = ins; + return 0; +} + + +/** + * sets the pointer at pos to the position of the first entry in the chain + * with of vol_id and, if given, with the same lnum as *lnum; + * if there is no entry in the chain, then *pos is NULL on return; + * always returns 0; + **/ +int +eb_chain_position(struct eb_info **head, uint32_t vol_id, uint32_t *lnum, + struct eb_info **pos) +{ + uint32_t vol, num; + struct eb_info *cur; + + if ((head == NULL) || (*head == NULL) || (pos == NULL)) + return 0; + + *pos = NULL; + + cur = *head; + while (cur != NULL) { + vol = __be32_to_cpu(cur->vid.vol_id); + num = __be32_to_cpu(cur->vid.lnum); + + if ((vol_id == vol) && ((lnum == NULL) || (*lnum == num))) { + *pos = cur; + return 0; + } + + cur = cur->next; + } + + return 0; +} + + +/** + * prints to stream, the vol_id, lnum and leb_ver for each entry in the + * chain, starting at head; + * this is intended for debuging purposes; + * always returns 0; + * + * FIXME I do not like the double list traversion ... + **/ +int +eb_chain_print(FILE* stream, struct eb_info *head) +{ + struct eb_info *cur; + + if (stream == NULL) + stream = stdout; + + if (head == NULL) { + fprintf(stream, "EMPTY\n"); + return 0; + } + /* 012345678012345678012345678012301230123 0123 01234567 0123457 01234567*/ + fprintf(stream, "VOL_ID LNUM LEB_VER EC VID DAT PBLK PADDR DSIZE EC\n"); + cur = head; + while (cur != NULL) { + struct eb_info *hist; + + fprintf(stream, "%08x %-8u %08x %-4s%-4s", + __be32_to_cpu(cur->vid.vol_id), + __be32_to_cpu(cur->vid.lnum), + __be32_to_cpu(cur->vid.leb_ver), + cur->ec_crc_ok ? "ok":"bad", + cur->vid_crc_ok ? "ok":"bad"); + if (cur->vid.vol_type == UBI_VID_STATIC) + fprintf(stream, "%-4s", cur->data_crc_ok ? "ok":"bad"); + else fprintf(stream, "%-4s", cur->data_crc_ok ? "ok":"ign"); + fprintf(stream, " %-4d %08x %-8u %-8llu\n", cur->phys_block, + cur->phys_addr, __be32_to_cpu(cur->vid.data_size), + __be64_to_cpu(cur->ec.ec)); + + hist = cur->older; + while (hist != NULL) { + fprintf(stream, "%08x %-8u %08x %-4s%-4s", + __be32_to_cpu(hist->vid.vol_id), + __be32_to_cpu(hist->vid.lnum), + __be32_to_cpu(hist->vid.leb_ver), + hist->ec_crc_ok ? "ok":"bad", + hist->vid_crc_ok ? "ok":"bad"); + if (hist->vid.vol_type == UBI_VID_STATIC) + fprintf(stream, "%-4s", hist->data_crc_ok ? "ok":"bad"); + else fprintf(stream, "%-4s", hist->data_crc_ok ? "ok":"ign"); + fprintf(stream, " %-4d %08x %-8u %-8llu (*)\n", + hist->phys_block, hist->phys_addr, + __be32_to_cpu(hist->vid.data_size), + __be64_to_cpu(hist->ec.ec)); + + hist = hist->older; + } + cur = cur->next; + } + + return 0; +} + + +/** + * frees the memory of the entire chain, starting at head; + * head will be NULL on return; + * always returns 0; + **/ +int +eb_chain_destroy(struct eb_info **head) +{ + if (head == NULL) + return 0; + + while (*head != NULL) { + struct eb_info *cur; + struct eb_info *hist; + + cur = *head; + *head = (*head)->next; + + hist = cur->older; + while (hist != NULL) { + struct eb_info *temp; + + temp = hist; + hist = hist->older; + free(temp); + } + free(cur); + } + return 0; +} + diff --git a/ubi-utils/old-tools/src/ecclayouts.h b/ubi-utils/old-tools/src/ecclayouts.h new file mode 100644 index 0000000..a1c7823 --- /dev/null +++ b/ubi-utils/old-tools/src/ecclayouts.h @@ -0,0 +1,66 @@ +#ifndef __ECCLAYOUTS_H__ +#define __ECCLAYOUTS_H__ +/* + * 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. + */ + +#include <stdint.h> +#include <mtd/mtd-abi.h> + +/* Define default oob placement schemes for large and small page devices */ +static struct nand_ecclayout mtd_nand_oob_16 = { + .eccbytes = 6, + .eccpos = { 0, 1, 2, 3, 6, 7 }, + .oobfree = {{ .offset = 8, .length = 8 }} +}; + +static struct nand_ecclayout mtd_nand_oob_64 = { + .eccbytes = 24, + .eccpos = { 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63 }, + .oobfree = {{ .offset = 2, .length = 38 }} +}; + +/* Define IBM oob placement schemes */ +static struct nand_ecclayout ibm_nand_oob_16 = { + .eccbytes = 6, + .eccpos = { 9, 10, 11, 13, 14, 15 }, + .oobfree = {{ .offset = 8, .length = 8 }} +}; + +static struct nand_ecclayout ibm_nand_oob_64 = { + .eccbytes = 24, + .eccpos = { 33, 34, 35, 37, 38, 39, 41, 42, + 43, 45, 46, 47, 49, 50, 51, 53, + 54, 55, 57, 58, 59, 61, 62, 63 }, + .oobfree = {{ .offset = 2, .length = 30 }} +}; + +struct oob_placement { + const char *name; + struct nand_ecclayout *nand_oob[2]; +}; + +static struct oob_placement oob_placement[] = { + { .name = "IBM", + .nand_oob = { &ibm_nand_oob_16, &ibm_nand_oob_64 }}, + { .name = "MTD", + .nand_oob = { &mtd_nand_oob_16, &mtd_nand_oob_64 }}, +}; + +#endif diff --git a/ubi-utils/old-tools/src/error.c b/ubi-utils/old-tools/src/error.c new file mode 100644 index 0000000..4aaedad --- /dev/null +++ b/ubi-utils/old-tools/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 <stdio.h> +#include <stdarg.h> +#include <syslog.h> +#include <stdlib.h> +#include <sys/errno.h> +#include <string.h> +#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/old-tools/src/error.h b/ubi-utils/old-tools/src/error.h new file mode 100644 index 0000000..05d8078 --- /dev/null +++ b/ubi-utils/old-tools/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 <stdio.h> + +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/old-tools/src/example_ubi.h b/ubi-utils/old-tools/src/example_ubi.h new file mode 100644 index 0000000..23c7b54 --- /dev/null +++ b/ubi-utils/old-tools/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/old-tools/src/hashmap.c b/ubi-utils/old-tools/src/hashmap.c new file mode 100644 index 0000000..3511d56 --- /dev/null +++ b/ubi-utils/old-tools/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 <errno.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include "error.h" +#include "hashmap.h" +#define DEFAULT_BUCKETS 4096 + +#if 0 +#define INFO_MSG(fmt...) do { \ + info_msg(fmt); \ +} while (0) +#else +#define INFO_MSG(fmt...) +#endif + +struct hashentry { + char* key; /* key '0' term. str */ + char* value; /* payload '0' term. str */ + + hashentry_t next; +}; + +struct hashmap { + size_t entries; /* current #entries */ + size_t maxsize; /* no. of hash buckets */ + hashentry_t* data; /* array of buckets */ +}; + +static int +is_empty(hashentry_t l) +{ + return l == NULL ? 1 : 0; +} + +hashmap_t +hashmap_new(void) +{ + hashmap_t res; + res = (hashmap_t) calloc(1, sizeof(struct hashmap)); + + if (res == NULL) + return NULL; + + res->maxsize = DEFAULT_BUCKETS; + res->entries = 0; + + res->data = (hashentry_t*) + calloc(1, res->maxsize * sizeof(struct hashentry)); + + if (res->data == NULL) + return NULL; + + return res; +} + +static hashentry_t +new_entry(const char* key, const char* value) +{ + hashentry_t res; + + res = (hashentry_t) calloc(1, sizeof(struct hashentry)); + + if (res == NULL) + return NULL; + + /* allocate key and value and copy them */ + res->key = strdup(key); + if (res->key == NULL) { + free(res); + return NULL; + } + + res->value = strdup(value); + if (res->value == NULL) { + free(res->key); + free(res); + return NULL; + } + + res->next = NULL; + + return res; +} + +static hashentry_t +free_entry(hashentry_t e) +{ + if (!is_empty(e)) { + if(e->key != NULL) { + free(e->key); + } + if(e->value != NULL) + free(e->value); + free(e); + } + + return NULL; +} + +static hashentry_t +remove_entry(hashentry_t l, const char* key, size_t* entries) +{ + hashentry_t lnext; + if (is_empty(l)) + return NULL; + + if(strcmp(l->key,key) == 0) { + lnext = l->next; + l = free_entry(l); + (*entries)--; + return lnext; + } + + l->next = remove_entry(l->next, key, entries); + + return l; +} + +static hashentry_t +insert_entry(hashentry_t l, hashentry_t e, size_t* entries) +{ + if (is_empty(l)) { + (*entries)++; + return e; + } + + /* check for update */ + if (strcmp(l->key, e->key) == 0) { + e->next = l->next; + l = free_entry(l); + return e; + } + + l->next = insert_entry(l->next, e, entries); + return l; +} + +static hashentry_t +remove_all(hashentry_t l, size_t* entries) +{ + hashentry_t lnext; + if (is_empty(l)) + return NULL; + + lnext = l->next; + free_entry(l); + (*entries)--; + + return remove_all(lnext, entries); +} + +static const char* +value_lookup(hashentry_t l, const char* key) +{ + if (is_empty(l)) + return NULL; + + if (strcmp(l->key, key) == 0) + return l->value; + + return value_lookup(l->next, key); +} + +static void +print_all(hashentry_t l) +{ + if (is_empty(l)) { + printf("\n"); + return; + } + + printf("%s=%s", l->key, l->value); + if (!is_empty(l->next)) { + printf(","); + } + + print_all(l->next); +} + +static void +keys_to_array(hashentry_t l, const char** a, size_t* i) +{ + if (is_empty(l)) + return; + + a[*i] = l->key; + (*i)++; + + keys_to_array(l->next, a, i); +} + +uint32_t +hash_str(const char* str, uint32_t mapsize) +{ + uint32_t hash = 0; + uint32_t x = 0; + uint32_t i = 0; + size_t len = strlen(str); + + for(i = 0; i < len; str++, i++) { + hash = (hash << 4) + (*str); + if((x = hash & 0xF0000000L) != 0) { + hash ^= (x >> 24); + hash &= ~x; + } + } + + return (hash & 0x7FFFFFFF) % mapsize; +} + + +int +hashmap_is_empty(hashmap_t map) +{ + if (map == NULL) + return -EINVAL; + + return map->entries > 0 ? 1 : 0; +} + +const char* +hashmap_lookup(hashmap_t map, const char* key) +{ + uint32_t i; + + if ((map == NULL) || (key == NULL)) + return NULL; + + i = hash_str(key, map->maxsize); + + return value_lookup(map->data[i], key); +} + +int +hashmap_add(hashmap_t map, const char* key, const char* value) +{ + uint32_t i; + hashentry_t entry; + + if ((map == NULL) || (key == NULL) || (value == NULL)) + return -EINVAL; + + i = hash_str(key, map->maxsize); + entry = new_entry(key, value); + if (entry == NULL) + return -ENOMEM; + + map->data[i] = insert_entry(map->data[i], + entry, &map->entries); + + INFO_MSG("HASH_ADD: chain[%d] key:%s val:%s",i, key, value); + return 0; +} + +int +hashmap_remove(hashmap_t map, const char* key) +{ + uint32_t i; + + if ((map == NULL) || (key == NULL)) + return -EINVAL; + + i = hash_str(key, map->maxsize); + map->data[i] = remove_entry(map->data[i], key, &map->entries); + + return 0; +} + +size_t +hashmap_size(hashmap_t map) +{ + if (map != NULL) + return map->entries; + else + return 0; +} + +int +hashmap_free(hashmap_t map) +{ + size_t i; + + if (map == NULL) + return -EINVAL; + + /* "children" first */ + for(i = 0; i < map->maxsize; i++) { + map->data[i] = remove_all(map->data[i], &map->entries); + } + free(map->data); + free(map); + + return 0; +} + +int +hashmap_dump(hashmap_t map) +{ + size_t i; + if (map == NULL) + return -EINVAL; + + for(i = 0; i < map->maxsize; i++) { + if (map->data[i] != NULL) { + printf("[%zd]: ", i); + print_all(map->data[i]); + } + } + + return 0; +} + +static const char** +sort_key_vector(const char** a, size_t size) +{ + /* uses bubblesort */ + size_t i, j; + const char* tmp; + + if (size <= 0) + return a; + + for (i = size - 1; i > 0; i--) { + for (j = 0; j < i; j++) { + if (strcmp(a[j], a[j+1]) > 0) { + tmp = a[j]; + a[j] = a[j+1]; + a[j+1] = tmp; + } + } + } + return a; +} + +const char** +hashmap_get_key_vector(hashmap_t map, size_t* size, int sort) +{ + const char** res; + size_t i, j; + *size = map->entries; + + res = (const char**) malloc(*size * sizeof(char*)); + if (res == NULL) + return NULL; + + j = 0; + for(i=0; i < map->maxsize; i++) { + keys_to_array(map->data[i], res, &j); + } + + if (sort) + res = sort_key_vector(res, *size); + + return res; +} + +int +hashmap_key_is_in_vector(const char** vec, size_t size, const char* key) +{ + size_t i; + for (i = 0; i < size; i++) { + if (strcmp(vec[i], key) == 0) /* found */ + return 1; + } + + return 0; +} + +const char** +hashmap_get_update_key_vector(const char** vec1, size_t vec1_size, + const char** vec2, size_t vec2_size, size_t* res_size) +{ + const char** res; + size_t i, j; + + *res_size = vec2_size; + + res = (const char**) malloc(*res_size * sizeof(char*)); + if (res == NULL) + return NULL; + + /* get all keys from vec2 which are not set in vec1 */ + j = 0; + for (i = 0; i < vec2_size; i++) { + if (!hashmap_key_is_in_vector(vec1, vec1_size, vec2[i])) + res[j++] = vec2[i]; + } + + *res_size = j; + return res; +} diff --git a/ubi-utils/old-tools/src/hashmap.h b/ubi-utils/old-tools/src/hashmap.h new file mode 100644 index 0000000..1b13e95 --- /dev/null +++ b/ubi-utils/old-tools/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 <stdlib.h> +#include <stdint.h> + +typedef struct hashentry *hashentry_t; +typedef struct hashmap *hashmap_t; + +hashmap_t hashmap_new(void); +int hashmap_free(hashmap_t map); + +int hashmap_add(hashmap_t map, const char* key, const char* value); +int hashmap_update(hashmap_t map, const char* key, const char* value); +int hashmap_remove(hashmap_t map, const char* key); +const char* hashmap_lookup(hashmap_t map, const char* key); + +const char** hashmap_get_key_vector(hashmap_t map, size_t* size, int sort); +int hashmap_key_is_in_vector(const char** vec, size_t size, const char* key); +const char** hashmap_get_update_key_vector(const char** vec1, size_t vec1_size, + const char** vec2, size_t vec2_size, size_t* res_size); + +int hashmap_dump(hashmap_t map); + +int hashmap_is_empty(hashmap_t map); +size_t hashmap_size(hashmap_t map); + +uint32_t hash_str(const char* str, uint32_t mapsize); + +#endif /* __HASHMAP_H__ */ diff --git a/ubi-utils/old-tools/src/libpfiflash.c b/ubi-utils/old-tools/src/libpfiflash.c new file mode 100644 index 0000000..7e3d3b3 --- /dev/null +++ b/ubi-utils/old-tools/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 <oliloh@de.ibm.com> + * Drake Dowsett <dowsett@de.ibm.com> + * Contact: Andreas Arnez <anrez@de.ibm.com> + */ + +/* 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 <stdio.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#define __USE_GNU +#include <string.h> +#include <stdlib.h> +#include <limits.h> +#include <sys/ioctl.h> + +#include <libubi.h> +#include <pfiflash.h> + +#include <mtd/ubi-user.h> /* FIXME Is this ok here? */ +#include <mtd/mtd-user.h> + +#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 <mtd/ubi-header.h> /* 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/old-tools/src/libubi.c b/ubi-utils/old-tools/src/libubi.c new file mode 100644 index 0000000..be06f70 --- /dev/null +++ b/ubi-utils/old-tools/src/libubi.c @@ -0,0 +1,1171 @@ +/* + * 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: Artem B. Bityutskiy + * + * UBI (Unsorted Block Images) library. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <dirent.h> +#include <errno.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <limits.h> +#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) { + errmsg("cannot allocate %d bytes", len1 + len2 + 2); + perror("malloc"); + 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) { + errmsg("cannot read \"%s\"", file); + perror("read"); + 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)) { + errmsg("close failed on \"%s\"", file); + perror("close"); + return -1; + } + + 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) { + errmsg("cannot read \"%s\"", file); + perror("read"); + goto out_error; + } + + /* Make sure all data is read */ + tmp1 = read(fd, &tmp, 1); + if (tmp1 == 1) { + errmsg("cannot read \"%s\"", file); + perror("read"); + 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)) { + errmsg("close failed on \"%s\"", file); + perror("close"); + 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) { + errmsg("\"%s\" does not have major:minor format", file); + errno = EINVAL; + return -1; + } + + if (*major < 0 || *minor < 0) { + errmsg("bad major:minor %d:%d in \"%s\"", + *major, *minor, file); + errno = EINVAL; + return -1; + } + + 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); +} + +/** + * dent_is_dir - check if a file is a directory. + * @dir: the base directory path of the file + * @name: file name + * + * This function returns %1 if file @name in directory @dir is a directoru, and + * %0 if not. + */ +static int dent_is_dir(const char *dir, const char *name) +{ + int ret; + struct stat st; + char full_path[strlen(dir) + strlen(name) + 2]; + + sprintf(full_path, "%s/%s", dir, name); + ret = lstat(full_path, &st); + if (ret) { + errmsg("lstat failed on \"%s\"", full_path); + perror("lstat"); + return -1; + } + + return !!S_ISDIR(st.st_mode); +} + +/** + * 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)) { + errmsg("\"%s\" is not a character device", node); + errno = EINVAL; + return -1; + } + + major = major(st.st_rdev); + minor = minor(st.st_rdev); + + if (minor == 0) { + errmsg("\"%s\" is not a volume character device", node); + errno = EINVAL; + return -1; + } + + 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)) { + errmsg("\"%s\" is not a character device", node); + errno = EINVAL; + return -1; + } + + major = major(stat.st_rdev); + minor = minor(stat.st_rdev); + + if (minor != 0) { + errmsg("\"%s\" is not an UBI character device", node); + errno = EINVAL; + return -1; + } + + 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; + struct libubi *lib; + + lib = calloc(1, sizeof(struct libubi)); + if (!lib) + return NULL; + + /* 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; + + lib->sysfs_ubi = mkpath(lib->sysfs, SYSFS_UBI); + if (!lib->sysfs_ubi) + goto out_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)) { + errmsg("close failed on \"%s\"", lib->sysfs_ubi); + perror("close"); + goto out_error; + } + + lib->ubi_dev = mkpath(lib->sysfs_ubi, UBI_DEV_NAME_PATT); + if (!lib->ubi_dev) + goto out_error; + + lib->ubi_version = mkpath(lib->sysfs_ubi, UBI_VER); + if (!lib->ubi_version) + goto out_error; + + lib->dev_dev = mkpath(lib->ubi_dev, DEV_DEV); + if (!lib->dev_dev) + goto out_error; + + lib->dev_avail_ebs = mkpath(lib->ubi_dev, DEV_AVAIL_EBS); + if (!lib->dev_avail_ebs) + goto out_error; + + lib->dev_total_ebs = mkpath(lib->ubi_dev, DEV_TOTAL_EBS); + if (!lib->dev_total_ebs) + goto out_error; + + lib->dev_bad_count = mkpath(lib->ubi_dev, DEV_BAD_COUNT); + if (!lib->dev_bad_count) + goto out_error; + + lib->dev_eb_size = mkpath(lib->ubi_dev, DEV_EB_SIZE); + if (!lib->dev_eb_size) + goto out_error; + + lib->dev_max_ec = mkpath(lib->ubi_dev, DEV_MAX_EC); + if (!lib->dev_max_ec) + goto out_error; + + lib->dev_bad_rsvd = mkpath(lib->ubi_dev, DEV_MAX_RSVD); + if (!lib->dev_bad_rsvd) + goto out_error; + + lib->dev_max_vols = mkpath(lib->ubi_dev, DEV_MAX_VOLS); + if (!lib->dev_max_vols) + goto out_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; + + lib->ubi_vol = mkpath(lib->sysfs_ubi, UBI_VOL_NAME_PATT); + if (!lib->ubi_vol) + goto out_error; + + lib->vol_type = mkpath(lib->ubi_vol, VOL_TYPE); + if (!lib->vol_type) + goto out_error; + + lib->vol_dev = mkpath(lib->ubi_vol, VOL_DEV); + if (!lib->vol_dev) + goto out_error; + + lib->vol_alignment = mkpath(lib->ubi_vol, VOL_ALIGNMENT); + if (!lib->vol_alignment) + goto out_error; + + lib->vol_data_bytes = mkpath(lib->ubi_vol, VOL_DATA_BYTES); + if (!lib->vol_data_bytes) + goto out_error; + + lib->vol_rsvd_ebs = mkpath(lib->ubi_vol, VOL_RSVD_EBS); + if (!lib->vol_rsvd_ebs) + goto out_error; + + lib->vol_eb_size = mkpath(lib->ubi_vol, VOL_EB_SIZE); + if (!lib->vol_eb_size) + goto out_error; + + lib->vol_corrupted = mkpath(lib->ubi_vol, VOL_CORRUPTED); + if (!lib->vol_corrupted) + goto out_error; + + lib->vol_name = mkpath(lib->ubi_vol, VOL_NAME); + if (!lib->vol_name) + goto out_error; + + if (read_positive_int(lib->ubi_version, &version)) + goto out_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; + } + + return lib; + +out_error: + libubi_close((libubi_t)lib); + return NULL; +} + +void libubi_close(libubi_t desc) +{ + struct libubi *lib = (struct libubi *)desc; + + free(lib->vol_name); + 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_mtd_num); + 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->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 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); + close(fd); + if (ret == -1) + return -1; + + req->dev_num = r.ubi_num; + +#ifdef UDEV_SETTLE_HACK + if (system("udevsettle") == -1) + return -1; + if (system("udevsettle") == -1) + return -1; +#endif + + return ret; +} + +int ubi_detach_mtd(libubi_t desc, const char *node, int mtd_num) +{ + int ret, ubi_dev; + + ret = mtd_num2ubi_dev(desc, mtd_num, &ubi_dev); + if (ret == -1) + return ret; + + return ubi_remove_dev(desc, node, ubi_dev); +} + +int ubi_remove_dev(libubi_t desc, const char *node, int ubi_dev) +{ + int fd, ret; + + desc = desc; + + fd = open(node, O_RDONLY); + if (fd == -1) + return -1; + ret = ioctl(fd, UBI_IOCDET, &ubi_dev); + if (ret == -1) + goto out_close; + +#ifdef UDEV_SETTLE_HACK + if (system("udevsettle") == -1) + return -1; +#endif + +out_close: + close(fd); + return ret; +} + +int ubi_node_type(libubi_t desc, const char *node) +{ + struct stat st; + struct ubi_info info; + int i, fd, major, minor; + struct libubi *lib = (struct libubi *)desc; + char file[strlen(lib->ubi_vol) + 100]; + + if (lstat(node, &st)) + return -1; + + if (!S_ISCHR(st.st_mode)) { + errno = EINVAL; + return -1; + } + + major = major(st.st_rdev); + minor = minor(st.st_rdev); + + 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) { + /* + * 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; + return -1; + } + + if (minor == 0) + return 1; + + /* 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; + return -1; + } + + return 2; +} + +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)); + + 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; + } + + /* + * 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) { + errmsg("cannot open %s", lib->sysfs_ubi); + perror("opendir"); + return -1; + } + + info->lowest_dev_num = INT_MAX; + while (1) { + int dev_num, ret; + + errno = 0; + dirent = readdir(sysfs_ubi); + if (!dirent) + break; + /* + * Make sure this direntry is a directory and not a symlink - + * Linux puts symlinks to UBI volumes on this UBI device to the + * same sysfs directory. + */ + if (!dent_is_dir(lib->sysfs_ubi, dirent->d_name)) + continue; + + ret = sscanf(dirent->d_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 (!dirent && errno) { + errmsg("readdir failed on \"%s\"", lib->sysfs_ubi); + perror("readdir"); + goto out_close; + } + + if (closedir(sysfs_ubi)) { + errmsg("closedir failed on \"%s\"", lib->sysfs_ubi); + perror("closedir"); + return -1; + } + + if (info->lowest_dev_num == INT_MAX) + info->lowest_dev_num = 0; + + if (read_positive_int(lib->ubi_version, &info->version)) + return -1; + + return 0; + +out_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; + + memset(&r, sizeof(struct ubi_mkvol_req), '\0'); + + 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 == -1) + goto out_close; + + req->vol_id = r.vol_id; + +#ifdef UDEV_SETTLE_HACK + if (system("udevsettle") == -1) + return -1; +#endif + +out_close: + close(fd); + return ret; +} + +int ubi_rmvol(libubi_t desc, const char *node, int vol_id) +{ + int fd, ret; + + desc = desc; + fd = open(node, O_RDONLY); + if (fd == -1) + return -1; + + ret = ioctl(fd, UBI_IOCRMVOL, &vol_id); + if (ret == -1) + goto out_close; + +#ifdef UDEV_SETTLE_HACK + if (system("udevsettle") == -1) + return -1; +#endif + +out_close: + close(fd); + return ret; +} + +int ubi_rsvol(libubi_t desc, const char *node, int vol_id, long long bytes) +{ + int fd, ret; + struct ubi_rsvol_req req; + + desc = desc; + fd = open(node, O_RDONLY); + if (fd == -1) + return -1; + + req.bytes = bytes; + req.vol_id = vol_id; + + ret = ioctl(fd, UBI_IOCRSVOL, &req); + close(fd); + return ret; +} + +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_info1(libubi_t desc, int dev_num, struct ubi_dev_info *info) +{ + DIR *sysfs_ubi; + struct dirent *dirent; + struct libubi *lib = (struct libubi *)desc; + + memset(info, '\0', sizeof(struct ubi_dev_info)); + info->dev_num = dev_num; + + sysfs_ubi = opendir(lib->sysfs_ubi); + if (!sysfs_ubi) + return -1; + + info->lowest_vol_num = INT_MAX; + + while (1) { + int vol_id, ret, devno; + + errno = 0; + dirent = readdir(sysfs_ubi); + if (!dirent) + break; + ret = sscanf(dirent->d_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; + } + } + + if (!dirent && errno) { + errmsg("readdir failed on \"%s\"", lib->sysfs_ubi); + perror("readdir"); + goto out_close; + } + + if (closedir(sysfs_ubi)) { + errmsg("closedir failed on \"%s\"", lib->sysfs_ubi); + perror("closedir"); + return -1; + } + + if (info->lowest_vol_num == INT_MAX) + info->lowest_vol_num = 0; + + if (dev_get_major(lib, dev_num, &info->major, &info->minor)) + 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)) + return -1; + if (dev_read_int(lib->dev_min_io_size, dev_num, &info->min_io_size)) + return -1; + + info->avail_bytes = info->avail_lebs * info->leb_size; + info->total_bytes = info->total_lebs * info->leb_size; + + return 0; + +out_close: + closedir(sysfs_ubi); + 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; + + if (dev_node2num(lib, node, &dev_num)) + return -1; + + return ubi_get_dev_info1(desc, dev_num, info); +} + +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]; + + 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; + + ret = vol_read_data(lib->vol_type, dev_num, vol_id, buf, 50); + if (ret < 0) + 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); + 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) + 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) + return -1; + + info->name[ret - 1] = '\0'; + return 0; +} + +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; + + if (vol_node2nums(lib, node, &dev_num, &vol_id)) + return -1; + + return ubi_get_vol_info1(desc, dev_num, vol_id, info); +} diff --git a/ubi-utils/old-tools/src/libubi_int.h b/ubi-utils/old-tools/src/libubi_int.h new file mode 100644 index 0000000..6490864 --- /dev/null +++ b/ubi-utils/old-tools/src/libubi_int.h @@ -0,0 +1,135 @@ +/* + * 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: Artem B. Bityutskiy + * + * UBI (Unsorted Block Images) library. + */ + +#ifndef __LIBUBI_INT_H__ +#define __LIBUBI_INT_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Error messages */ +#define errmsg(fmt, ...) do { \ + fprintf(stderr, "libubi error: " fmt "\n", ##__VA_ARGS__); \ +} while(0) + +/* + * 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 DEV_DEV "dev" +#define DEV_AVAIL_EBS "avail_eraseblocks" +#define DEV_TOTAL_EBS "total_eraseblocks" +#define DEV_BAD_COUNT "bad_peb_count" +#define DEV_EB_SIZE "eraseblock_size" +#define DEV_MAX_EC "max_ec" +#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" +#define VOL_DATA_BYTES "data_bytes" +#define VOL_RSVD_EBS "reserved_ebs" +#define VOL_EB_SIZE "usable_eb_size" +#define VOL_CORRUPTED "corrupted" +#define VOL_NAME "name" + +/** + * 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 + */ +struct libubi +{ + char *sysfs; + char *sysfs_ctrl; + char *ctrl_dev; + char *sysfs_ubi; + char *ubi_dev; + char *ubi_version; + char *dev_dev; + char *dev_avail_ebs; + char *dev_total_ebs; + char *dev_bad_count; + char *dev_eb_size; + char *dev_max_ec; + 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; + char *vol_alignment; + char *vol_data_bytes; + char *vol_rsvd_ebs; + char *vol_eb_size; + char *vol_corrupted; + char *vol_name; + char *vol_max_count; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* !__LIBUBI_INT_H__ */ diff --git a/ubi-utils/old-tools/src/libubigen.c b/ubi-utils/old-tools/src/libubigen.c new file mode 100644 index 0000000..87c8c40 --- /dev/null +++ b/ubi-utils/old-tools/src/libubigen.c @@ -0,0 +1,486 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Oliver Lohmann + * + * Add UBI headers to binary data. + */ + +#include <stdlib.h> +#include <stdint.h> +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <mtd/ubi-header.h> + +#include "config.h" +#include "ubigen.h" +#include "crc32.h" + +#define UBI_NAME_SIZE 256 +#define DEFAULT_VID_OFFSET ((DEFAULT_PAGESIZE) - (UBI_VID_HDR_SIZE)) +#define MIN(a,b) ((a) < (b) ? (a) : (b)) + +static uint32_t crc32_table[256]; + +struct ubi_info { + struct ubi_vid_hdr* v; /* Volume ID header */ + struct ubi_ec_hdr* ec; /* Erase count header */ + + FILE* fp_in; /* Input Stream */ + FILE* fp_out; /* Output stream */ + + size_t peb_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 peb_size) +{ + return (byte % peb_size) == 0 + ? (byte / peb_size) + : (byte / peb_size) + 1; +} + +static int +validate_ubi_info(ubi_info_t u) +{ + if ((u->v->vol_type != UBI_VID_DYNAMIC) && + (u->v->vol_type != UBI_VID_STATIC)) { + return EUBIGEN_INVALID_TYPE; + } + + if (__be32_to_cpu(u->ec->vid_hdr_offset) < UBI_VID_HDR_SIZE) { + return EUBIGEN_INVALID_HDR_OFFSET; + } + + return 0; +} + +static int +skip_blks(ubi_info_t u, uint32_t blks) +{ + uint32_t i; + size_t read = 0, to_read = 0; + + /* Step to a maximum of leb_total - 1 to keep the + restrictions. */ + for (i = 0; i < MIN(blks, u->leb_total-1); i++) { + /* Read in data */ + to_read = MIN(u->leb_size, + (u->bytes_total - u->bytes_read)); + read = fread(u->ptr_data, 1, to_read, u->fp_in); + if (read != to_read) { + return -EIO; + } + u->bytes_read += read; + u->blks_written++; + } + + return 0; +} + +static void +clear_buf(ubi_info_t u) +{ + memset(u->buf, 0xff, u->peb_size); +} + +static void +write_ec_hdr(ubi_info_t u) +{ + memcpy(u->ptr_ec_hdr, u->ec, UBI_EC_HDR_SIZE); +} + +static int +fill_data_buffer_from_file(ubi_info_t u, size_t* read) +{ + size_t to_read = 0; + + if (u-> fp_in == NULL) + return -EIO; + + to_read = MIN(u->leb_size, (u->bytes_total - u->bytes_read)); + *read = fread(u->ptr_data, 1, to_read, u->fp_in); + if (*read != to_read) { + return -EIO; + } + return 0; +} + +static void +add_static_info(ubi_info_t u, size_t data_size, ubigen_action_t action) +{ + uint32_t crc = clc_crc32(crc32_table, UBI_CRC32_INIT, + u->ptr_data, data_size); + + u->v->data_size = __cpu_to_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); + } +} + +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); +} + +static int +write_to_output_stream(ubi_info_t u) +{ + size_t written; + + written = fwrite(u->buf, 1, u->peb_size, u->fp_out); + if (written != u->peb_size) { + return -EIO; + } + return 0; +} + +int +ubigen_write_leb(ubi_info_t u, ubigen_action_t action) +{ + int rc = 0; + size_t read = 0; + + clear_buf(u); + write_ec_hdr(u); + + rc = fill_data_buffer_from_file(u, &read); + if (rc != 0) + return rc; + + if (u->v->vol_type == UBI_VID_STATIC) { + add_static_info(u, read, action); + } + + u->v->lnum = __cpu_to_be32(u->blks_written); + + if (action & MARK_AS_UPDATE) { + u->v->copy_flag = (u->v->copy_flag)++; + } + + write_vid_hdr(u, action); + rc = write_to_output_stream(u); + if (rc != 0) + return rc; + + /* Update current handle */ + u->bytes_read += read; + u->blks_written++; + return 0; +} + +int +ubigen_write_complete(ubi_info_t u) +{ + size_t i; + int rc = 0; + + for (i = 0; i < u->leb_total; i++) { + rc = ubigen_write_leb(u, NO_ERROR); + if (rc != 0) + return rc; + } + + return 0; +} + +int +ubigen_write_broken_update(ubi_info_t u, uint32_t blk) +{ + int rc = 0; + + rc = skip_blks(u, blk); + if (rc != 0) + return rc; + + rc = ubigen_write_leb(u, MARK_AS_UPDATE | BROKEN_DATA_CRC); + if (rc != 0) + return rc; + + + return 0; +} + +void +dump_info(ubi_info_t u ubi_unused) +{ +#ifdef DEBUG + int err = 0; + if (!u) { + fprintf(stderr, "<empty>"); + return; + } + if (!u->ec) { + fprintf(stderr, "<ec-empty>"); + err = 1; + } + if (!u->v) { + fprintf(stderr, "<v-empty>"); + err = 1; + } + if (err) return; + + fprintf(stderr, "ubi volume\n"); + fprintf(stderr, "version : %8d\n", u->v->version); + fprintf(stderr, "vol_id : %8d\n", __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, "peb_size : 0x%08x\n", u->peb_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 peb_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->peb_size = peb_size ? peb_size : DEFAULT_BLOCKSIZE; + res->data_pad = (res->peb_size - data_offset) % alignment; + res->leb_size = res->peb_size - data_offset - res->data_pad; + res->leb_total = byte_to_blk(data_size, res->leb_size); + res->alignment = alignment; + + if ((res->peb_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->peb_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/old-tools/src/libubimirror.c b/ubi-utils/old-tools/src/libubimirror.c new file mode 100644 index 0000000..d06770e --- /dev/null +++ b/ubi-utils/old-tools/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 <stdio.h> +#include <errno.h> +#include <unistd.h> +#include <memory.h> +#include <limits.h> +#include <fcntl.h> + +#include <libubi.h> +#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/old-tools/src/list.c b/ubi-utils/old-tools/src/list.c new file mode 100644 index 0000000..6eb716b --- /dev/null +++ b/ubi-utils/old-tools/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 <stdlib.h> +#include <assert.h> +#include <stdio.h> + +#include "list.h" + +list_t +mk_empty(void) +{ + return (list_t) NULL; +} + +int +is_empty(list_t l) +{ + return l == NULL; +} + +info_t +head(list_t l) +{ + assert(!is_empty(l)); + return l->info; +} + +list_t +tail(list_t l) +{ + assert(!is_empty(l)); + return l->next; +} + +list_t +remove_head(list_t l) +{ + list_t res; + assert(!is_empty(l)); + + res = l->next; + free(l); + return res; +} + +list_t +cons(info_t e, list_t l) +{ + list_t res = malloc(sizeof(*l)); + if (!res) + return NULL; + res->info = e; + res->next = l; + + return res; +} + +list_t +prepend_elem(info_t e, list_t l) +{ + return cons(e,l); +} + +list_t +append_elem(info_t e, list_t l) +{ + if (is_empty(l)) { + return cons(e,l); + } + l->next = append_elem(e, l->next); + + return l; +} + +list_t +insert_sorted(cmp_func_t cmp, info_t e, list_t l) +{ + if (is_empty(l)) + return cons(e, l); + + switch (cmp(e, l->info)) { + case -1: + case 0: + return l; + break; + case 1: + l->next = insert_sorted(cmp, e, l); + break; + default: + break; + } + + /* never reached */ + return NULL; +} + +list_t +remove_all(free_func_t free_func, list_t l) +{ + if (is_empty(l)) + return l; + list_t lnext = l->next; + + if (free_func && l->info) { + free_func(&(l->info)); + } + free(l); + + return remove_all(free_func, lnext); +} + + +info_t +is_in(cmp_func_t cmp, info_t e, list_t l) +{ + return + (is_empty(l)) + ? NULL + : (cmp(e, l->info)) == 0 ? l->info : is_in(cmp, e, l->next); +} + + +void +apply(process_func_t process_func, list_t l) +{ + list_t ptr; + void *i; + foreach(i, ptr, l) { + process_func(i); + } +} diff --git a/ubi-utils/old-tools/src/list.h b/ubi-utils/old-tools/src/list.h new file mode 100644 index 0000000..e8452a2 --- /dev/null +++ b/ubi-utils/old-tools/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 <stdint.h> + +#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/old-tools/src/mkbootenv.c b/ubi-utils/old-tools/src/mkbootenv.c new file mode 100644 index 0000000..952f651 --- /dev/null +++ b/ubi-utils/old-tools/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 <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <getopt.h> +#include <unistd.h> +#include <errno.h> +#include <mtd/ubi-header.h> + +#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=<fname> Write the output data to <output> 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 <output>] [--copyright] [--output=<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/old-tools/src/nand2bin.c b/ubi-utils/old-tools/src/nand2bin.c new file mode 100644 index 0000000..8c95b27 --- /dev/null +++ b/ubi-utils/old-tools/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. + * 1.7 Made NAND ECC layout configurable, the holes which were previously + * filled with 0x00 are untouched now and will be 0xff just like MTD + * behaves when writing the oob (haver) + */ + +#include <config.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdint.h> +#include <getopt.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include "config.h" +#include "nandecc.h" +#include "ecclayouts.h" + +#define PROGRAM_VERSION "1.7" + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) +#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 oobsize; + int bad_marker_offs_in_oob; + size_t blocksize; + int split_blocks; + size_t in_len; /* size of input file */ + int correct_ecc; + struct nand_ecclayout *nand_oob; + + /* 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, + .nand_oob = &ibm_nand_oob_64, + .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 = +" -l, --ecc-placement=<MTD,IBM> OOB placement scheme (default is IBM).\n" +" -o, --output=<output> Data output file\n" +" -O, --oob=<oob> OOB output file\n" +" -p, --pagesize=<pagesize> NAND pagesize\n" +" -b, --blocksize=<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 <output>] [-O <oob>] [-p <pagesize>]\n" +" [--output=<output>] [--oob=<oob>] [--pagesize=<pagesize>] [--help]\n" +" [--usage] input.mif\n"; + +static int verbose = 0; + +static struct option long_options[] = { + { .name = "ecc-layout", .has_arg = 1, .flag = NULL, .val = 'l' }, + { .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) +{ + unsigned int i, oob_idx = 0; + const char *ecc_layout = NULL; + + while (1) { + int key; + + key = getopt_long(argc, argv, "b:el:o:O:p:sv?", long_options, NULL); + if (key == -1) + break; + + switch (key) { + case 'b': /* --blocksize<blocksize> */ + args->blocksize = str_to_num(optarg); + break; + case 'e': /* --correct-ecc */ + args->correct_ecc = 1; + break; + case 'l': /* --ecc-layout=<...> */ + ecc_layout = optarg; + break; + case 'o': /* --output=<output.bin> */ + args->output_file = optarg; + break; + case 'O': /* --oob=<oob.bin> */ + args->oob_file = optarg; + break; + case 'p': /* --pagesize<pagesize> */ + args->pagesize = str_to_num(optarg); + break; + case 's': /* --split-blocks */ + args->split_blocks = 1; + break; + case 'v': /* --verbose */ + verbose++; + break; + case 'V': + printf("%s\n", PROGRAM_VERSION); + exit(0); + break; + case '?': /* help */ + printf("Usage: nand2bin [OPTION...] input.mif\n"); + printf("%s%s", doc, optionsstr); + printf("\nReport bugs to %s\n", + PACKAGE_BUGREPORT); + exit(0); + break; + default: + printf("%s", usage); + exit(-1); + } + } + + if (optind < argc) + args->arg1 = argv[optind++]; + + switch (args->pagesize) { + case 512: + args->oobsize = 16; + args->bad_marker_offs_in_oob = 5; + oob_idx = 0; + break; + case 2048: + args->oobsize = 64; + args->bad_marker_offs_in_oob = 0; + oob_idx = 1; + break; + default: + fprintf(stderr, "Unsupported page size: %d\n", args->pagesize); + return -EINVAL; + } + + /* Figure out correct oob layout if it differs from default */ + if (ecc_layout) { + for (i = 0; i < ARRAY_SIZE(oob_placement); i++) + if (strcmp(ecc_layout, oob_placement[i].name) == 0) + args->nand_oob = + oob_placement[i].nand_oob[oob_idx]; + } + return 0; +} + +/* + * We must only compare the relevant bytes in the OOB area. All other + * bytes can be ignored. The information we need to do this is in + * nand_oob. + */ +static int oob_cmp(struct nand_ecclayout *nand_oob, uint8_t *oob, + uint8_t *calc_oob) +{ + unsigned int i; + for (i = 0; i < nand_oob->eccbytes; i++) + if (oob[nand_oob->eccpos[i]] != calc_oob[nand_oob->eccpos[i]]) + return 1; + 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(struct args *args, uint8_t *buf, uint8_t *oobbuf) +{ + size_t i, j; + int eccpoi; + uint8_t ecc_code[3] = { 0, }; /* temp */ + + /* Calculate ECC */ + memset(oobbuf, 0xff, args->oobsize); + for (eccpoi = 0, i = 0; i < args->pagesize; i += 256, eccpoi += 3) { + nand_calculate_ecc(&buf[i], ecc_code); + for (j = 0; j < 3; j++) + oobbuf[args->nand_oob->eccpos[eccpoi + j]] = ecc_code[j]; + } + return 0; +} + +static int decompose_image(struct args *args, FILE *in_fp, + FILE *bin_fp, FILE *oob_fp) +{ + unsigned int i, eccpoi; + int read, rc, page = 0; + uint8_t *buf = malloc(args->pagesize); + uint8_t *oob = malloc(args->oobsize); + uint8_t *calc_oob = malloc(args->oobsize); + uint8_t *calc_buf = malloc(args->pagesize); + uint8_t *page_buf; + int pages_per_block = args->blocksize / args->pagesize; + int badpos = args->bad_marker_offs_in_oob; + uint8_t ecc_code[3] = { 0, }; /* temp */ + uint8_t calc_ecc_code[3] = { 0, }; /* temp */ + + if (!buf || !oob || !calc_oob || !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, args->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(args, buf, calc_oob); + memcpy(calc_buf, buf, args->pagesize); + + if (verbose && oob_cmp(args->nand_oob, oob, calc_oob) != 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, args->oobsize); + + printf("Calculated OOB Data:\n"); + hexdump(stdout, calc_oob, args->oobsize); + } + + /* Do correction on subpage base */ + for (i = 0, eccpoi = 0; i < args->pagesize; i += 256, eccpoi += 3) { + int j; + + for (j = 0; j < 3; j++) { + ecc_code[j] = oob[args->nand_oob->eccpos[eccpoi + j]]; + calc_ecc_code[j] = + calc_oob[args->nand_oob->eccpos[eccpoi + j]]; + } + rc = nand_correct_data(calc_buf + i, ecc_code, + calc_ecc_code); + 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, args->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; + int pages_per_block = args->blocksize / args->pagesize; + int block_len = pages_per_block * (args->pagesize + args->oobsize); + int blocks = args->in_len / block_len; + char bname[256] = { 0, }; + int badpos = args->bad_marker_offs_in_oob; + 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 + args->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, args->oobsize); + printf("--\n"); + hexdump(stdout, buf + 2 * args->pagesize + + args->oobsize, args->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/old-tools/src/nandcorr.c b/ubi-utils/old-tools/src/nandcorr.c new file mode 100644 index 0000000..caa07e2 --- /dev/null +++ b/ubi-utils/old-tools/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/old-tools/src/nandecc.c b/ubi-utils/old-tools/src/nandecc.c new file mode 100644 index 0000000..71660ef --- /dev/null +++ b/ubi-utils/old-tools/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/old-tools/src/nandecc.h b/ubi-utils/old-tools/src/nandecc.h new file mode 100644 index 0000000..bcf1982 --- /dev/null +++ b/ubi-utils/old-tools/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 <stdint.h> + +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/old-tools/src/pddcustomize.c b/ubi-utils/old-tools/src/pddcustomize.c new file mode 100644 index 0000000..515efd6 --- /dev/null +++ b/ubi-utils/old-tools/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 <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <getopt.h> +#include <unistd.h> +#include <limits.h> +#include <errno.h> +#include <mtd/ubi-header.h> + +#include "config.h" +#include "bootenv.h" +#include "error.h" +#include "example_ubi.h" +#include "libubi.h" +#include "ubimirror.h" + +#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=<input> Binary input file. For debug purposes.\n" +" -l, --list List card bootenv/pdd values.\n" +" -o, --output=<output> Binary output file. For debug purposes.\n" +" -s, --side=<seqnum> 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 <input>] [-o <output>] [-s <seqnum>]\n" +" [--both] [--copyright] [--input=<input>] [--list]\n" +" [--output=<output>] [--side=<seqnum>] [--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/old-tools/src/peb.c b/ubi-utils/old-tools/src/peb.c new file mode 100644 index 0000000..160a463 --- /dev/null +++ b/ubi-utils/old-tools/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 <stdlib.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <assert.h> + +#include "peb.h" + +int +peb_cmp(peb_t eb_1, peb_t eb_2) +{ + assert(eb_1); + assert(eb_2); + + return eb_1->num == eb_2->num ? 0 + : eb_1->num > eb_2->num ? 1 : -1; +} + +int +peb_new(uint32_t eb_num, uint32_t peb_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 = peb_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/old-tools/src/peb.h b/ubi-utils/old-tools/src/peb.h new file mode 100644 index 0000000..246bce8 --- /dev/null +++ b/ubi-utils/old-tools/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 <stdint.h> +#include <stdio.h> + +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/old-tools/src/pfi.c b/ubi-utils/old-tools/src/pfi.c new file mode 100644 index 0000000..fa835e2 --- /dev/null +++ b/ubi-utils/old-tools/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. + * + * <oliloh@de.ibm.com> Wed Feb 8 11:38:22 CET 2006: Initial creation. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <syslog.h> +#include <stdarg.h> +#include <errno.h> + +#include "pfi.h" + +#define PFI_MAGIC "PFI!\n" +#define PFI_DATA "DATA\n" /* The same size as PFI_MAGIC */ +#define PFI_MAGIC_LEN 5 + +static const char copyright [] __attribute__((unused)) = + "Copyright (c) International Business Machines Corp., 2006"; + +enum key_id { + /* version 1 */ + key_version, /* must be index position 0! */ + key_mode, + key_size, + key_crc, + key_label, + key_flags, + key_ubi_ids, + key_ubi_size, + key_ubi_type, + key_ubi_names, + key_ubi_alignment, + key_raw_starts, + key_raw_total_size, + num_keys, +}; + +struct pfi_header { + char defined[num_keys]; /* reserve all possible keys even if + version does not require this. */ + int mode_no; /* current mode no. -> can only increase */ + union { + char *str; + uint32_t num; + } value[num_keys]; +}; + + +#define PFI_MANDATORY 0x0001 +#define PFI_STRING 0x0002 +#define PFI_LISTVALUE 0x0004 /* comma seperated list of nums */ +#define PFI_MANDATORY_UBI 0x0008 +#define PFI_MANDATORY_RAW 0x0010 + +struct key_descriptor { + enum key_id id; + const char *name; + uint32_t flags; +}; + +static const struct key_descriptor key_desc_v1[] = { + { key_version, "version", PFI_MANDATORY }, + { key_mode, "mode", PFI_MANDATORY | PFI_STRING }, + { key_size, "size", PFI_MANDATORY }, + { key_crc, "crc", PFI_MANDATORY }, + { key_label, "label", PFI_MANDATORY | PFI_STRING }, + { key_flags, "flags", PFI_MANDATORY }, + { key_ubi_ids, "ubi_ids", PFI_MANDATORY_UBI | PFI_STRING }, + { key_ubi_size, "ubi_size", PFI_MANDATORY_UBI }, + { key_ubi_type, "ubi_type", PFI_MANDATORY_UBI | PFI_STRING }, + { key_ubi_names, "ubi_names", PFI_MANDATORY_UBI | PFI_STRING }, + { key_ubi_alignment, "ubi_alignment", PFI_MANDATORY_UBI }, + { key_raw_starts, "raw_starts", PFI_MANDATORY_RAW | PFI_STRING }, + { key_raw_total_size, "raw_total_size", PFI_MANDATORY_RAW }, +}; + +static const struct key_descriptor *key_descriptors[] = { + NULL, + key_desc_v1, /* version 1 */ +}; + +static const int key_descriptors_max[] = { + 0, /* version 0 */ + sizeof(key_desc_v1)/sizeof(struct key_descriptor), /* version 1 */ +}; + +#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/old-tools/src/pfi.h b/ubi-utils/old-tools/src/pfi.h new file mode 100644 index 0000000..8c5cc07 --- /dev/null +++ b/ubi-utils/old-tools/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 <oliloh@de.ibm.com> + * Andreas Arnez <arnez@de.ibm.com> + * Joern Engel <engeljoe@de.ibm.com> + * Frank Haverkamp <haverkam@de.ibm.com> + * + * @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 <stdio.h> /* 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 <asm/errno.h>. + */ +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 <asm/errno.h>. + * + * 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/old-tools/src/pfi2bin.c b/ubi-utils/old-tools/src/pfi2bin.c new file mode 100644 index 0000000..7f31938 --- /dev/null +++ b/ubi-utils/old-tools/src/pfi2bin.c @@ -0,0 +1,681 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Oliver Lohmann + * + * Convert a PFI file (partial flash image) into a plain binary file. + * This tool can be used to prepare the data to be burned into flash + * chips in a manufacturing step where the flashes are written before + * being soldered onto the hardware. For NAND images another step is + * required to add the right OOB data to the binary image. + * + * 1.3 Removed argp because we want to use uClibc. + * 1.4 Minor cleanups + */ + +#include <stdlib.h> +#include <stdint.h> +#include <getopt.h> +#include <stdio.h> +#include <string.h> +#include <assert.h> +#include <errno.h> + +#include <ubigen.h> +#include <mtd/ubi-header.h> + +#include "config.h" +#include "list.h" +#include "error.h" +#include "reader.h" +#include "peb.h" +#include "crc32.h" + +#define PROGRAM_VERSION "1.4" + +#define MAX_FNAME 255 +#define DEFAULT_ERASE_COUNT 0 /* Hmmm.... Perhaps */ +#define ERR_BUF_SIZE 1024 + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) + +static uint32_t crc32_table[256]; +static char err_buf[ERR_BUF_SIZE]; + +/* + * Data used to buffer raw blocks which have to be + * located at a specific point inside the generated RAW file + */ + +typedef enum action_t { + ACT_NOTHING = 0x00000000, + ACT_RAW = 0x00000001, +} action_t; + +static const char copyright [] __attribute__((unused)) = + "(c) Copyright IBM Corp 2006\n"; + +static char doc[] = "\nVersion: " PROGRAM_VERSION "\n" + "pfi2bin - a tool to convert PFI files into binary images.\n"; + +static const char *optionsstr = +" Common settings:\n" +" -c, --copyright\n" +" -v, --verbose Print more information.\n" +"\n" +" Input:\n" +" -j, --platform=pdd-file PDD information which contains the card settings.\n" +"\n" +" Output:\n" +" -o, --output=filename Outputfile, default: stdout.\n" +"\n" +" -?, --help Give this help list\n" +" --usage Give a short usage message\n" +" -V, --version Print program version\n"; + +static const char *usage = +"Usage: pfi2bin [-cv?V] [-j pdd-file] [-o filename] [--copyright]\n" +" [--verbose] [--platform=pdd-file] [--output=filename] [--help]\n" +" [--usage] [--version] pfifile\n"; + +struct option long_options[] = { + { .name = "copyright", .has_arg = 0, .flag = NULL, .val = 'c' }, + { .name = "verbose", .has_arg = 0, .flag = NULL, .val = 'v' }, + { .name = "platform", .has_arg = 1, .flag = NULL, .val = 'j' }, + { .name = "output", .has_arg = 1, .flag = NULL, .val = 'o' }, + { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' }, + { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 }, + { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, + { NULL, 0, NULL, 0} +}; + +typedef struct io { + FILE* fp_pdd; /* a FilePointer to the PDD data */ + FILE* fp_pfi; /* a FilePointer to the PFI input stream */ + FILE* fp_out; /* a FilePointer to the output stream */ +} *io_t; + +typedef struct myargs { + /* common settings */ + action_t action; + int verbose; + const char *f_in_pfi; + const char *f_in_pdd; + const char *f_out; + + /* special stuff needed to get additional arguments */ + char *arg1; + char **options; /* [STRING...] */ +} myargs; + +static int +parse_opt(int argc, char **argv, myargs *args) +{ + while (1) { + int key; + + key = getopt_long(argc, argv, "cvj:o:?V", long_options, NULL); + if (key == -1) + break; + + switch (key) { + /* common settings */ + case 'v': /* --verbose=<level> */ + args->verbose = 1; + break; + + case 'c': /* --copyright */ + fprintf(stderr, "%s\n", copyright); + exit(0); + break; + + case 'j': /* --platform */ + args->f_in_pdd = optarg; + break; + + case 'o': /* --output */ + args->f_out = optarg; + break; + + case '?': /* help */ + printf("pfi2bin [OPTION...] pfifile\n"); + printf("%s", doc); + printf("%s", optionsstr); + printf("\nReport bugs to %s\n", + PACKAGE_BUGREPORT); + exit(0); + break; + + case 'V': + printf("%s\n", PROGRAM_VERSION); + exit(0); + break; + + default: + printf("%s", usage); + exit(-1); + } + } + + if (optind < argc) + args->f_in_pfi = argv[optind++]; + + return 0; +} + + +static size_t +byte_to_blk(size_t byte, size_t blk_size) +{ + return (byte % blk_size) == 0 + ? byte / blk_size + : byte / blk_size + 1; +} + + + + +/** + * @precondition IO: File stream points to first byte of RAW data. + * @postcondition IO: File stream points to first byte of next + * or EOF. + */ +static int +memorize_raw_eb(pfi_raw_t pfi_raw, pdd_data_t pdd, list_t *raw_pebs, + io_t io) +{ + int rc = 0; + uint32_t i; + + size_t read, to_read, eb_num; + size_t bytes_left; + list_t pebs = *raw_pebs; + peb_t peb = NULL; + + long old_file_pos = ftell(io->fp_pfi); + for (i = 0; i < pfi_raw->starts_size; i++) { + bytes_left = pfi_raw->data_size; + rc = fseek(io->fp_pfi, old_file_pos, SEEK_SET); + if (rc != 0) + goto err; + + eb_num = byte_to_blk(pfi_raw->starts[i], pdd->eb_size); + while (bytes_left) { + to_read = MIN(bytes_left, pdd->eb_size); + rc = peb_new(eb_num++, pdd->eb_size, &peb); + if (rc != 0) + goto err; + read = fread(peb->data, 1, to_read, io->fp_pfi); + if (read != to_read) { + rc = -EIO; + goto err; + } + pebs = append_elem(peb, pebs); + bytes_left -= read; + } + + } + *raw_pebs = pebs; + return 0; +err: + pebs = remove_all((free_func_t)&peb_free, pebs); + return rc; +} + +static int +convert_ubi_volume(pfi_ubi_t ubi, pdd_data_t pdd, list_t raw_pebs, + struct ubi_vtbl_record *vol_tab, + size_t *ebs_written, io_t io) +{ + int rc = 0; + uint32_t i, j; + peb_t raw_peb; + peb_t cmp_peb; + ubi_info_t u; + size_t leb_total = 0; + uint8_t vol_type; + + switch (ubi->type) { + case pfi_ubi_static: + vol_type = UBI_VID_STATIC; break; + case pfi_ubi_dynamic: + vol_type = UBI_VID_DYNAMIC; break; + default: + vol_type = UBI_VID_DYNAMIC; + } + + rc = peb_new(0, 0, &cmp_peb); + if (rc != 0) + goto err; + + long old_file_pos = ftell(io->fp_pfi); + for (i = 0; i < ubi->ids_size; i++) { + rc = fseek(io->fp_pfi, old_file_pos, SEEK_SET); + if (rc != 0) + goto err; + rc = ubigen_create(&u, ubi->ids[i], vol_type, + pdd->eb_size, DEFAULT_ERASE_COUNT, + ubi->alignment, UBI_VERSION, + pdd->vid_hdr_offset, 0, ubi->data_size, + io->fp_pfi, io->fp_out); + if (rc != 0) + goto err; + + rc = ubigen_get_leb_total(u, &leb_total); + if (rc != 0) + goto err; + + j = 0; + while(j < leb_total) { + cmp_peb->num = *ebs_written; + raw_peb = is_in((cmp_func_t)peb_cmp, cmp_peb, + raw_pebs); + if (raw_peb) { + rc = peb_write(io->fp_out, raw_peb); + } + else { + rc = ubigen_write_leb(u, NO_ERROR); + j++; + } + if (rc != 0) + goto err; + (*ebs_written)++; + } + /* memorize volume table entry */ + rc = ubigen_set_lvol_rec(u, ubi->size, + ubi->names[i], + (void*) &vol_tab[ubi->ids[i]]); + if (rc != 0) + goto err; + ubigen_destroy(&u); + } + + peb_free(&cmp_peb); + return 0; + +err: + peb_free(&cmp_peb); + ubigen_destroy(&u); + return rc; +} + + +static FILE* +my_fmemopen (void *buf, size_t size, const char *opentype) +{ + FILE* f; + size_t ret; + + assert(strcmp(opentype, "r") == 0); + + f = tmpfile(); + ret = fwrite(buf, 1, size, f); + rewind(f); + + return f; +} + +/** + * @brief Builds a UBI volume table from a volume entry list. + * @return 0 On success. + * else Error. + */ +static int +write_ubi_volume_table(pdd_data_t pdd, list_t raw_pebs, + struct ubi_vtbl_record *vol_tab, size_t vol_tab_size, + size_t *ebs_written, io_t io) +{ + int rc = 0; + ubi_info_t u; + peb_t raw_peb; + peb_t cmp_peb; + size_t leb_size, leb_total, j = 0; + uint8_t *ptr = NULL; + FILE* fp_leb = NULL; + int vt_slots; + size_t vol_tab_size_limit; + + rc = peb_new(0, 0, &cmp_peb); + if (rc != 0) + goto err; + + /* @FIXME: Artem creates one volume with 2 LEBs. + * IMO 2 volumes would be more convenient. In order + * to get 2 reserved LEBs from ubigen, I have to + * introduce this stupid mechanism. Until no final + * decision of the VTAB structure is made... Good enough. + */ + rc = ubigen_create(&u, UBI_LAYOUT_VOL_ID, UBI_VID_DYNAMIC, + pdd->eb_size, DEFAULT_ERASE_COUNT, + 1, UBI_VERSION, + pdd->vid_hdr_offset, UBI_COMPAT_REJECT, + vol_tab_size, stdin, io->fp_out); + /* @FIXME stdin for fp_in is a hack */ + if (rc != 0) + goto err; + rc = ubigen_get_leb_size(u, &leb_size); + if (rc != 0) + goto err; + ubigen_destroy(&u); + + /* + * The number of supported volumes is restricted by the eraseblock size + * and by the UBI_MAX_VOLUMES constant. + */ + vt_slots = leb_size / UBI_VTBL_RECORD_SIZE; + if (vt_slots > UBI_MAX_VOLUMES) + vt_slots = UBI_MAX_VOLUMES; + vol_tab_size_limit = vt_slots * UBI_VTBL_RECORD_SIZE; + + ptr = (uint8_t*) malloc(leb_size * sizeof(uint8_t)); + if (ptr == NULL) + goto err; + + memset(ptr, 0xff, leb_size); + memcpy(ptr, vol_tab, vol_tab_size_limit); + fp_leb = my_fmemopen(ptr, leb_size, "r"); + + rc = ubigen_create(&u, UBI_LAYOUT_VOL_ID, UBI_VID_DYNAMIC, + pdd->eb_size, DEFAULT_ERASE_COUNT, + 1, UBI_VERSION, pdd->vid_hdr_offset, + UBI_COMPAT_REJECT, leb_size * UBI_LAYOUT_VOLUME_EBS, + fp_leb, io->fp_out); + if (rc != 0) + goto err; + rc = ubigen_get_leb_total(u, &leb_total); + if (rc != 0) + goto err; + + long old_file_pos = ftell(fp_leb); + while(j < leb_total) { + rc = fseek(fp_leb, old_file_pos, SEEK_SET); + if (rc != 0) + goto err; + + cmp_peb->num = *ebs_written; + raw_peb = is_in((cmp_func_t)peb_cmp, cmp_peb, + raw_pebs); + if (raw_peb) { + rc = peb_write(io->fp_out, raw_peb); + } + else { + rc = ubigen_write_leb(u, NO_ERROR); + j++; + } + + if (rc != 0) + goto err; + (*ebs_written)++; + } + +err: + free(ptr); + peb_free(&cmp_peb); + ubigen_destroy(&u); + fclose(fp_leb); + return rc; +} + +static int +write_remaining_raw_ebs(pdd_data_t pdd, list_t raw_blocks, size_t *ebs_written, + FILE* fp_out) +{ + int rc = 0; + uint32_t j, delta; + list_t ptr; + peb_t empty_eb, peb; + + /* create an empty 0xff EB (for padding) */ + rc = peb_new(0, pdd->eb_size, &empty_eb); + + foreach(peb, ptr, raw_blocks) { + if (peb->num < *ebs_written) { + continue; /* omit blocks which + are already passed */ + } + + if (peb->num < *ebs_written) { + err_msg("eb_num: %d\n", peb->num); + err_msg("Bug: This should never happen. %d %s", + __LINE__, __FILE__); + goto err; + } + + delta = peb->num - *ebs_written; + if (((delta + *ebs_written) * pdd->eb_size) > pdd->flash_size) { + err_msg("RAW block outside of flash_size."); + goto err; + } + for (j = 0; j < delta; j++) { + rc = peb_write(fp_out, empty_eb); + if (rc != 0) + goto err; + (*ebs_written)++; + } + rc = peb_write(fp_out, peb); + if (rc != 0) + goto err; + (*ebs_written)++; + } + +err: + peb_free(&empty_eb); + return rc; +} + +static int +init_vol_tab(struct ubi_vtbl_record **vol_tab, size_t *vol_tab_size) +{ + uint32_t crc; + size_t i; + struct ubi_vtbl_record* res = NULL; + + *vol_tab_size = UBI_MAX_VOLUMES * UBI_VTBL_RECORD_SIZE; + + res = (struct ubi_vtbl_record*) calloc(1, *vol_tab_size); + if (vol_tab == NULL) { + return -ENOMEM; + } + + for (i = 0; i < UBI_MAX_VOLUMES; i++) { + crc = clc_crc32(crc32_table, UBI_CRC32_INIT, + &(res[i]), UBI_VTBL_RECORD_SIZE_CRC); + res[i].crc = __cpu_to_be32(crc); + } + + *vol_tab = res; + return 0; +} + +static int +create_raw(io_t io) +{ + int rc = 0; + size_t ebs_written = 0; /* eraseblocks written already... */ + size_t vol_tab_size; + list_t ptr; + + list_t pfi_raws = mk_empty(); /* list of raw sections from a pfi */ + list_t pfi_ubis = mk_empty(); /* list of ubi sections from a pfi */ + list_t raw_pebs = mk_empty(); /* list of raw eraseblocks */ + + struct ubi_vtbl_record *vol_tab = NULL; + pdd_data_t pdd = NULL; + + rc = init_vol_tab (&vol_tab, &vol_tab_size); + if (rc != 0) { + err_msg("Cannot initialize volume table."); + goto err; + } + + rc = read_pdd_data(io->fp_pdd, &pdd, + err_buf, ERR_BUF_SIZE); + if (rc != 0) { + err_msg("Cannot read necessary pdd_data: %s rc: %d", + err_buf, rc); + goto err; + } + + rc = read_pfi_headers(&pfi_raws, &pfi_ubis, io->fp_pfi, + err_buf, ERR_BUF_SIZE); + if (rc != 0) { + err_msg("Cannot read pfi header: %s rc: %d", + err_buf, rc); + goto err; + } + + pfi_raw_t pfi_raw; + foreach(pfi_raw, ptr, pfi_raws) { + rc = memorize_raw_eb(pfi_raw, pdd, &raw_pebs, + io); + if (rc != 0) { + err_msg("Cannot create raw_block in mem. rc: %d\n", + rc); + goto err; + } + } + + pfi_ubi_t pfi_ubi; + foreach(pfi_ubi, ptr, pfi_ubis) { + rc = convert_ubi_volume(pfi_ubi, pdd, raw_pebs, + vol_tab, &ebs_written, io); + if (rc != 0) { + err_msg("Cannot convert UBI volume. rc: %d\n", rc); + goto err; + } + } + + rc = write_ubi_volume_table(pdd, raw_pebs, vol_tab, vol_tab_size, + &ebs_written, io); + if (rc != 0) { + err_msg("Cannot write UBI volume table. rc: %d\n", rc); + goto err; + } + + rc = write_remaining_raw_ebs(pdd, raw_pebs, &ebs_written, io->fp_out); + if (rc != 0) + goto err; + + if (io->fp_out != stdout) + info_msg("Physical eraseblocks written: %8d\n", ebs_written); +err: + free(vol_tab); + pfi_raws = remove_all((free_func_t)&free_pfi_raw, pfi_raws); + pfi_ubis = remove_all((free_func_t)&free_pfi_ubi, pfi_ubis); + raw_pebs = remove_all((free_func_t)&peb_free, raw_pebs); + free_pdd_data(&pdd); + return rc; +} + + +/* ------------------------------------------------------------------------- */ +static void +open_io_handle(myargs *args, io_t io) +{ + /* set PDD input */ + io->fp_pdd = fopen(args->f_in_pdd, "r"); + if (io->fp_pdd == NULL) { + err_sys("Cannot open: %s", args->f_in_pdd); + } + + /* set PFI input */ + io->fp_pfi = fopen(args->f_in_pfi, "r"); + if (io->fp_pfi == NULL) { + err_sys("Cannot open PFI input file: %s", args->f_in_pfi); + } + + /* set output prefix */ + if (strcmp(args->f_out,"") == 0) + io->fp_out = stdout; + else { + io->fp_out = fopen(args->f_out, "wb"); + if (io->fp_out == NULL) { + err_sys("Cannot open output file: %s", args->f_out); + } + } +} + +static void +close_io_handle(io_t io) +{ + if (fclose(io->fp_pdd) != 0) { + err_sys("Cannot close PDD file."); + } + if (fclose(io->fp_pfi) != 0) { + err_sys("Cannot close PFI file."); + } + if (io->fp_out != stdout) { + if (fclose(io->fp_out) != 0) { + err_sys("Cannot close output file."); + } + } + + io->fp_pdd = NULL; + io->fp_pfi = NULL; + io->fp_out = NULL; +} + +int +main(int argc, char *argv[]) +{ + int rc = 0; + + ubigen_init(); + init_crc32_table(crc32_table); + + struct io io = {NULL, NULL, NULL}; + myargs args = { + .action = ACT_RAW, + .verbose = 0, + + .f_in_pfi = "", + .f_in_pdd = "", + .f_out = "", + + /* arguments */ + .arg1 = NULL, + .options = NULL, + }; + + /* parse arguments */ + parse_opt(argc, argv, &args); + + if (strcmp(args.f_in_pfi, "") == 0) { + err_quit("No PFI input file specified!"); + } + + if (strcmp(args.f_in_pdd, "") == 0) { + err_quit("No PDD input file specified!"); + } + + open_io_handle(&args, &io); + + info_msg("[ Creating RAW..."); + rc = create_raw(&io); + if (rc != 0) { + err_msg("Creating RAW failed."); + goto err; + } + +err: + close_io_handle(&io); + if (rc != 0) { + remove(args.f_out); + } + + return rc; +} diff --git a/ubi-utils/old-tools/src/pfiflash.c b/ubi-utils/old-tools/src/pfiflash.c new file mode 100644 index 0000000..754fe33 --- /dev/null +++ b/ubi-utils/old-tools/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 <unistd.h> +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <getopt.h> +#include <unistd.h> +#include <errno.h> + +#include <pfiflash.h> +#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=<file> Write a logfile to <file>.\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=<type> Specify the pdd-update algorithm. <type> is either\n" +" 'keep', 'merge' or 'overwrite'.\n" +" -r, --raw-flash=<dev> Flash the raw data. Use the specified mtd device.\n" +" -s, --side=<seqnum> 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 <file>] [-p <type>] [-r <dev>] [-s <seqnum>]\n" +" [--copyright] [--logfile=<file>] [--verbose] [--complete]\n" +" [--pdd-update=<type>] [--raw-flash=<dev>] [--side=<seqnum>]\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/old-tools/src/pfiflash.h b/ubi-utils/old-tools/src/pfiflash.h new file mode 100644 index 0000000..039705d --- /dev/null +++ b/ubi-utils/old-tools/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 <oliloh@de.ibm.com> + * + * @brief The pfiflash library offers an interface for using the + * pfiflash * utility. + */ + +#include <stdio.h> /* 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/old-tools/src/pfiflash_error.h b/ubi-utils/old-tools/src/pfiflash_error.h new file mode 100644 index 0000000..0f27f4a --- /dev/null +++ b/ubi-utils/old-tools/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 <dowsett@de.ibm.com> + * Contact: Andreas Arnez <arnez@de.ibm.com> + */ + +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/old-tools/src/reader.c b/ubi-utils/old-tools/src/reader.c new file mode 100644 index 0000000..0ea8c6d --- /dev/null +++ b/ubi-utils/old-tools/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 <string.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> + +#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/old-tools/src/reader.h b/ubi-utils/old-tools/src/reader.h new file mode 100644 index 0000000..715e464 --- /dev/null +++ b/ubi-utils/old-tools/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 <stdint.h> +#include <stdio.h> + +#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/old-tools/src/ubigen.c b/ubi-utils/old-tools/src/ubigen.c new file mode 100644 index 0000000..9fcafab --- /dev/null +++ b/ubi-utils/old-tools/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 <stdlib.h> +#include <stdint.h> +#include <stdio.h> +#include <getopt.h> +#include <unistd.h> +#include <sys/stat.h> +#include <mtd/ubi-header.h> + +#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=<num> Set the alignment size to <num> (default 1).\n" +" Values can be specified as bytes, 'ki' or 'Mi'.\n" +" -B, --blocksize=<num> Set the eraseblock size to <num> (default 128\n" +" KiB).\n" +" Values can be specified as bytes, 'ki' or 'Mi'.\n" +" -E, --erasecount=<num> Set the erase count to <num> (default 0)\n" +" -I, --id=<num> The UBI volume id.\n" +" -O, --offset=<num> Offset from start of an erase block to the UBI\n" +" volume header.\n" +" -T, --type=<num> The UBI volume type:\n" +" 1 = dynamic, 2 = static\n" +" -X, --setver=<num> Set UBI version number to <num> (default 1)\n" +"\n" +" Input/Output:\n" +" -i, --infile=<filename> Read input from file.\n" +" -o, --outfile=<filename> Write output to file (default is stdout).\n" +"\n" +" Special options:\n" +" -U, --broken-update=<leb> Create an ubi image which simulates a broken\n" +" update.\n" +" <leb> 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 <num>] [-B <num>] [-E <num>] [-I <num>]\n" +" [-O <num>] [-T <num>] [-X <num>] [-i <filename>] [-o <filename>]\n" +" [-U <leb>] [--copyright] [--debug] [--verbose] [--alignment=<num>]\n" +" [--blocksize=<num>] [--erasecount=<num>] [--id=<num>]\n" +" [--offset=<num>] [--type=<num>] [--setver=<num>]\n" +" [--infile=<filename>] [--outfile=<filename>]\n" +" [--broken-update=<leb>] [--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/old-tools/src/ubigen.h b/ubi-utils/old-tools/src/ubigen.h new file mode 100644 index 0000000..0f43a46 --- /dev/null +++ b/ubi-utils/old-tools/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 <stdio.h> /* FILE */ +#include <stdint.h> +#include <mtd/ubi-header.h> + +#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/old-tools/src/ubimirror.c b/ubi-utils/old-tools/src/ubimirror.c new file mode 100644 index 0000000..2cc4596 --- /dev/null +++ b/ubi-utils/old-tools/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 <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <getopt.h> +#include <unistd.h> +#include <errno.h> +#include <mtd/ubi-header.h> + +#include "config.h" +#include "error.h" +#include "example_ubi.h" +#include "ubimirror.h" + +#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=<seqnum> Use the side <seqnum> 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 <seqnum>] [--copyright] [--side=<seqnum>]\n" +" [--help] [--usage] [--version] <source> <destination>\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...] " + "<source> <destination>\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/old-tools/src/ubimirror.h b/ubi-utils/old-tools/src/ubimirror.h new file mode 100644 index 0000000..d7ae2ad --- /dev/null +++ b/ubi-utils/old-tools/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 <stdint.h> + +/** + * @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/old-tools/src/ubiupdate.c b/ubi-utils/old-tools/src/ubiupdate.c new file mode 100644 index 0000000..75222d4 --- /dev/null +++ b/ubi-utils/old-tools/src/ubiupdate.c @@ -0,0 +1,318 @@ +/* + * 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 <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdint.h> +#include <getopt.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/stat.h> + +#include <libubi.h> +#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 myargs = { + .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=<volume 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 " <UBI volume node file name> [-t] [-h] [-V] [--truncate] [--help]\n" +"\t\t[--version] <image file>\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': + myargs.truncate = 1; + break; + + case 'h': + fprintf(stderr, "%s\n\n", doc); + fprintf(stderr, "%s\n\n", usage); + fprintf(stderr, "%s\n", optionsstr); + exit(0); + + case 'V': + fprintf(stderr, "%s\n", PROGRAM_VERSION); + exit(0); + + case ':': + errmsg("parameter is missing"); + return -1; + + default: + fprintf(stderr, "Use -h for help\n"); + exit(-1); + } + } + + if (optind == argc) { + errmsg("UBI device name was not specified (use -h for help)"); + return -1; + } else if (optind != argc - 2) { + errmsg("specify UBI device name and image file name as first 2 " + "parameters (use -h for help)"); + return -1; + } + + myargs.node = argv[optind]; + myargs.img = argv[optind + 1]; + + return 0; +} + +static int truncate_volume(libubi_t libubi) +{ + int err, fd; + + fd = open(myargs.node, O_RDWR); + if (fd == -1) { + errmsg("cannot open \"%s\"", myargs.node); + perror("open"); + return -1; + } + + err = ubi_update_start(libubi, fd, 0); + if (err) { + errmsg("cannot truncate volume \"%s\"", myargs.node); + perror("ubi_update_start"); + 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; + } + errmsg("cannot write %d bytes to volume \"%s\"", + len, myargs.node); + perror("write"); + return -1; + } + + if (ret == 0) { + errmsg("cannot write %d bytes to volume \"%s\"", + len, myargs.node); + return -1; + } + + 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) { + errmsg("cannot allocate %d bytes of memory", vol_info->leb_size); + return -1; + } + + err = stat(myargs.img, &st); + if (err < 0) { + errmsg("stat failed on \"%s\"", myargs.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)", + myargs.img, bytes, myargs.node, vol_info->rsvd_bytes); + goto out_free; + } + + fd = open(myargs.node, O_RDWR); + if (fd == -1) { + errmsg("cannot open UBI volume \"%s\"", myargs.node); + perror("open"); + goto out_free; + } + + ifd = open(myargs.img, O_RDONLY); + if (ifd == -1) { + errmsg("cannot open \"%s\"", myargs.img); + perror("open"); + goto out_close1; + } + + err = ubi_update_start(libubi, fd, bytes); + if (err) { + errmsg("cannot start volume \"%s\" update", myargs.node); + perror("ubi_update_start"); + 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 { + errmsg("cannot read %d bytes from \"%s\"", + tocopy, myargs.img); + perror("read"); + 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 (!myargs.img && !myargs.truncate) { + errmsg("incorrect arguments, use -h for help"); + return -1; + } + + libubi = libubi_open(); + if (libubi == NULL) { + perror("Cannot open libubi"); + goto out_libubi; + } + + err = ubi_node_type(libubi, myargs.node); + if (err == 1) { + errmsg("\"%s\" is an UBI device node, not an UBI volume node", + myargs.node); + goto out_libubi; + } else if (err < 0) { + errmsg("\"%s\" is not an UBI volume node", myargs.node); + goto out_libubi; + } + + err = ubi_get_vol_info(libubi, myargs.node, &vol_info); + if (err) { + errmsg("cannot get information about UBI volume \"%s\"", + myargs.node); + perror("ubi_get_dev_info"); + goto out_libubi; + } + + if (myargs.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/old-tools/src/unubi.c b/ubi-utils/old-tools/src/unubi.c new file mode 100644 index 0000000..5c1d324 --- /dev/null +++ b/ubi-utils/old-tools/src/unubi.c @@ -0,0 +1,1023 @@ +/* + * 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 <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdint.h> +#include <getopt.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <limits.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <mtd/ubi-header.h> + +#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=<volume-id> Extract and rebuild volume\n" +"\n" +" OPTIONS\n" +" -b, --blocksize=<block-size> Specify size of eraseblocks in image in bytes\n" +" (default 128KiB)\n" +" -d, --dir=<output-dir> Specify output directory\n" +" -D, --debug Enable debug output\n" +" -s, --headersize=<header-size> 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 <volume-id>] [-b <block-size>] [-d <output-dir>]\n" +" [-s <header-size>] [--analyze] [--info-table]\n" +" [--rebuild=<volume-id>] [--blocksize=<block-size>]\n" +" [--dir=<output-dir>] [--headersize=<header-size>] [--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=<block-size> */ + args->bsize = str_to_num(optarg); + break; + case 's': /* --header-size=<header-size> */ + args->hsize = str_to_num(optarg); + break; + case 'd': /* --dir=<output-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=<volume-id> */ + 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_VOL_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_VOL_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/old-tools/src/unubi_analyze.c b/ubi-utils/old-tools/src/unubi_analyze.c new file mode 100644 index 0000000..2ab3b87 --- /dev/null +++ b/ubi-utils/old-tools/src/unubi_analyze.c @@ -0,0 +1,462 @@ +/* + * 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 <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <limits.h> +#include <sys/types.h> +#include <sys/stat.h> + +#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/old-tools/src/unubi_analyze.h b/ubi-utils/old-tools/src/unubi_analyze.h new file mode 100644 index 0000000..8588219 --- /dev/null +++ b/ubi-utils/old-tools/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 <stdio.h> +#include <stdint.h> +#include <mtd/ubi-header.h> + +#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__ */ |