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