diff options
Diffstat (limited to 'ubi-utils/src/pddcustomize.c')
-rw-r--r-- | ubi-utils/src/pddcustomize.c | 509 |
1 files changed, 509 insertions, 0 deletions
diff --git a/ubi-utils/src/pddcustomize.c b/ubi-utils/src/pddcustomize.c new file mode 100644 index 0000000..515efd6 --- /dev/null +++ b/ubi-utils/src/pddcustomize.c @@ -0,0 +1,509 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Oliver Lohmann + * + * PDD (platform description data) contains a set of system specific + * boot-parameters. Some of those parameters need to be handled + * special on updates, e.g. the MAC addresses. They must also be kept + * if the system is updated and one must be able to modify them when + * the system has booted the first time. This tool is intended to do + * PDD modification. + * + * 1.3 Removed argp because we want to use uClibc. + * 1.4 Minor cleanups + * 1.5 Migrated to new libubi + */ + +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <getopt.h> +#include <unistd.h> +#include <limits.h> +#include <errno.h> +#include <mtd/ubi-header.h> + +#include "config.h" +#include "bootenv.h" +#include "error.h" +#include "example_ubi.h" +#include "libubi.h" +#include "ubimirror.h" + +#define PROGRAM_VERSION "1.5" + +#define DEFAULT_DEV_PATTERN "/dev/ubi%d" +#define DEFAULT_VOL_PATTERN "/dev/ubi%d_%d" + +typedef enum action_t { + ACT_NORMAL = 0, + ACT_LIST, + ACT_ARGP_ABORT, + ACT_ARGP_ERR, +} action_t; + +#define ABORT_ARGP do { \ + args->action = ACT_ARGP_ABORT; \ +} while (0) + +#define ERR_ARGP do { \ + args->action = ACT_ARGP_ERR; \ +} while (0) + +static char doc[] = "\nVersion: " PROGRAM_VERSION "\n" + "pddcustomize - customize bootenv and pdd values.\n"; + +static const char *optionsstr = +" -b, --both Mirror updated PDD to redundand copy.\n" +" -c, --copyright Print copyright information.\n" +" -i, --input=<input> Binary input file. For debug purposes.\n" +" -l, --list List card bootenv/pdd values.\n" +" -o, --output=<output> Binary output file. For debug purposes.\n" +" -s, --side=<seqnum> The side/seqnum to update.\n" +" -x, --host use x86 platform for debugging.\n" +" -?, --help Give this help list\n" +" --usage Give a short usage message\n" +" -V, --version Print program version\n"; + +static const char *usage = +"Usage: pddcustomize [-bclx?V] [-i <input>] [-o <output>] [-s <seqnum>]\n" +" [--both] [--copyright] [--input=<input>] [--list]\n" +" [--output=<output>] [--side=<seqnum>] [--host] [--help] [--usage]\n" +" [--version] [key=value] [...]\n"; + +struct option long_options[] = { + { .name = "both", .has_arg = 0, .flag = NULL, .val = 'b' }, + { .name = "copyright", .has_arg = 0, .flag = NULL, .val = 'c' }, + { .name = "input", .has_arg = 1, .flag = NULL, .val = 'i' }, + { .name = "list", .has_arg = 0, .flag = NULL, .val = 'l' }, + { .name = "output", .has_arg = 1, .flag = NULL, .val = 'o' }, + { .name = "side", .has_arg = 1, .flag = NULL, .val = 's' }, + { .name = "host", .has_arg = 0, .flag = NULL, .val = 'x' }, + { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' }, + { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 }, + { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, + { NULL, 0, NULL, 0} +}; + +static const char copyright [] __attribute__((unused)) = + "Copyright IBM Corp 2006"; + +typedef struct myargs { + action_t action; + const char* file_in; + const char* file_out; + int both; + int side; + int x86; /* X86 host, use files for testing */ + bootenv_t env_in; + + char *arg1; + char **options; /* [STRING...] */ +} myargs; + +static int +get_update_side(const char* str) +{ + uint32_t i = strtoul(str, NULL, 0); + + if ((i != 0) && (i != 1)) { + return -1; + } + + return i; +} + +static int +extract_pair(bootenv_t env, const char* str) +{ + int rc = 0; + char* key; + char* val; + + key = strdup(str); + if (key == NULL) + return -ENOMEM; + + val = strstr(key, "="); + if (val == NULL) { + err_msg("Wrong argument: %s\n" + "Expecting key=value pair.\n", str); + rc = -1; + goto err; + } + + *val = '\0'; /* split strings */ + val++; + rc = bootenv_set(env, key, val); + +err: + free(key); + return rc; +} + +static int +parse_opt(int argc, char **argv, myargs *args) +{ + int rc = 0; + + while (1) { + int key; + + key = getopt_long(argc, argv, "clbxs:i:o:?V", + long_options, NULL); + if (key == -1) + break; + + switch (key) { + case 'c': + err_msg("%s\n", copyright); + ABORT_ARGP; + break; + case 'l': + args->action = ACT_LIST; + break; + case 'b': + args->both = 1; + break; + case 'x': + args->x86 = 1; + break; + case 's': + args->side = get_update_side(optarg); + if (args->side < 0) { + err_msg("Unsupported seqnum: %d.\n" + "Supported seqnums are " + "'0' and '1'\n", + args->side, optarg); + ERR_ARGP; + } + break; + case 'i': + args->file_in = optarg; + break; + case 'o': + args->file_out = optarg; + break; + case '?': /* help */ + err_msg("Usage: pddcustomize [OPTION...] " + "[key=value] [...]"); + err_msg("%s", doc); + err_msg("%s", optionsstr); + err_msg("\nReport bugs to %s", + PACKAGE_BUGREPORT); + exit(0); + break; + case 'V': + err_msg("%s", PROGRAM_VERSION); + exit(0); + break; + default: + err_msg("%s", usage); + exit(-1); + } + } + + if (optind < argc) { + rc = extract_pair(args->env_in, argv[optind++]); + if (rc != 0) + ERR_ARGP; + } + + return 0; +} + +static int +list_bootenv(bootenv_t env) +{ + int rc = 0; + rc = bootenv_write_txt(stdout, env); + if (rc != 0) { + err_msg("Cannot list bootenv/pdd. rc: %d\n", rc); + goto err; + } +err: + return rc; +} + +static int +process_key_value(bootenv_t env_in, bootenv_t env) +{ + int rc = 0; + size_t size, i; + const char* tmp; + const char** key_vec = NULL; + + rc = bootenv_get_key_vector(env_in, &size, 0, &key_vec); + if (rc != 0) + goto err; + + for (i = 0; i < size; i++) { + rc = bootenv_get(env_in, key_vec[i], &tmp); + if (rc != 0) { + err_msg("Cannot read value to input key: %s. rc: %d\n", + key_vec[i], rc); + goto err; + } + rc = bootenv_set(env, key_vec[i], tmp); + if (rc != 0) { + err_msg("Cannot set value key: %s. rc: %d\n", + key_vec[i], rc); + goto err; + } + } + +err: + if (key_vec != NULL) + free(key_vec); + return rc; +} + +static int +read_bootenv(const char* file, bootenv_t env) +{ + int rc = 0; + FILE* fp_in = NULL; + + fp_in = fopen(file, "rb"); + if (fp_in == NULL) { + err_msg("Cannot open file: %s\n", file); + return -EIO; + } + + rc = bootenv_read(fp_in, env, BOOTENV_MAXSIZE); + if (rc != 0) { + err_msg("Cannot read bootenv from file %s. rc: %d\n", + file, rc); + goto err; + } + +err: + fclose(fp_in); + return rc; +} + +/* + * Read bootenv from ubi volume + */ +static int +ubi_read_bootenv(uint32_t devno, uint32_t id, bootenv_t env) +{ + libubi_t ulib; + int rc = 0; + char path[PATH_MAX]; + FILE* fp_in = NULL; + + ulib = libubi_open(); + if (ulib == NULL) { + err_msg("Cannot allocate ubi structure\n"); + return -1; + } + + snprintf(path, PATH_MAX, DEFAULT_VOL_PATTERN, devno, id); + + fp_in = fopen(path, "r"); + if (fp_in == NULL) { + err_msg("Cannot open volume:%d number:%d\n", devno, id); + goto err; + } + + rc = bootenv_read(fp_in, env, BOOTENV_MAXSIZE); + if (rc != 0) { + err_msg("Cannot read volume:%d number:%d\n", devno, id); + goto err; + } + +err: + if (fp_in) + fclose(fp_in); + libubi_close(ulib); + return rc; +} + +static int +write_bootenv(const char* file, bootenv_t env) +{ + int rc = 0; + FILE* fp_out; + + fp_out = fopen(file, "wb"); + if (fp_out == NULL) { + err_msg("Cannot open file: %s\n", file); + return -EIO; + } + + rc = bootenv_write(fp_out, env); + if (rc != 0) { + err_msg("Cannot write bootenv to file %s. rc: %d\n", file, rc); + goto err; + } + +err: + fclose(fp_out); + return rc; +} + +/* + * Read bootenv from ubi volume + */ +static int +ubi_write_bootenv(uint32_t devno, uint32_t id, bootenv_t env) +{ + libubi_t ulib; + int rc = 0; + char path[PATH_MAX]; + FILE* fp_out = NULL; + size_t nbytes ; + + rc = bootenv_size(env, &nbytes); + if (rc) { + err_msg("Cannot determine size of bootenv structure\n"); + return rc; + } + ulib = libubi_open(); + if (ulib == NULL) { + err_msg("Cannot allocate ubi structure\n"); + return rc; + } + + snprintf(path, PATH_MAX, DEFAULT_VOL_PATTERN, devno, id); + + fp_out = fopen(path, "r+"); + if (fp_out == NULL) { + err_msg("Cannot fopen volume:%d number:%d\n", devno, id); + goto err; + } + + rc = bootenv_write(fp_out, env); + if (rc != 0) { + err_msg("Cannot write bootenv to volume %d number:%d\n", + devno, id); + goto err; + } + +err: + if( fp_out ) + fclose(fp_out); + libubi_close(ulib); + return rc; +} + +static int +do_mirror(int volno) +{ + char errbuf[1024]; + uint32_t ids[2]; + int rc; + int src_volno_idx = 0; + + ids[0] = EXAMPLE_BOOTENV_VOL_ID_1; + ids[1] = EXAMPLE_BOOTENV_VOL_ID_2; + + if (volno == EXAMPLE_BOOTENV_VOL_ID_2) + src_volno_idx = 1; + + rc = ubimirror(EXAMPLE_UBI_DEVICE, src_volno_idx, ids, 2, errbuf, + sizeof errbuf); + if( rc ) + err_msg(errbuf); + return rc; +} + +int +main(int argc, char **argv) { + int rc = 0; + bootenv_t env = NULL; + uint32_t boot_volno; + myargs args = { + .action = ACT_NORMAL, + .file_in = NULL, + .file_out = NULL, + .side = -1, + .x86 = 0, + .both = 0, + .env_in = NULL, + + .arg1 = NULL, + .options = NULL, + }; + + rc = bootenv_create(&env); + if (rc != 0) { + err_msg("Cannot create bootenv handle. rc: %d", rc); + goto err; + } + + rc = bootenv_create(&(args.env_in)); + if (rc != 0) { + err_msg("Cannot create bootenv handle. rc: %d", rc); + goto err; + } + + parse_opt(argc, argv, &args); + if (args.action == ACT_ARGP_ERR) { + rc = -1; + goto err; + } + if (args.action == ACT_ARGP_ABORT) { + rc = 0; + goto out; + } + + if ((args.side == 0) || (args.side == -1)) + boot_volno = EXAMPLE_BOOTENV_VOL_ID_1; + else + boot_volno = EXAMPLE_BOOTENV_VOL_ID_2; + + if( args.x86 ) + rc = read_bootenv(args.file_in, env); + else + rc = ubi_read_bootenv(EXAMPLE_UBI_DEVICE, boot_volno, env); + if (rc != 0) { + goto err; + } + + if (args.action == ACT_LIST) { + rc = list_bootenv(env); + if (rc != 0) { + goto err; + } + goto out; + } + + rc = process_key_value(args.env_in, env); + if (rc != 0) { + goto err; + } + + if( args.x86 ) + rc = write_bootenv(args.file_in, env); + else + rc = ubi_write_bootenv(EXAMPLE_UBI_DEVICE, boot_volno, env); + if (rc != 0) { + goto err; + } + if( args.both ) /* No side specified, update both */ + rc = do_mirror(boot_volno); + + out: + err: + bootenv_destroy(&env); + bootenv_destroy(&(args.env_in)); + return rc; +} |