| /* |
| * Copyright (c) International Business Machines Corp., 2008 |
| * |
| * 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 |
| * 1.6 Fixed broken volume update |
| */ |
| |
| #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-media.h> |
| |
| #include "config.h" |
| #include "bootenv.h" |
| #include "error.h" |
| #include "example_ubi.h" |
| #include "libubi.h" |
| #include "ubimirror.h" |
| |
| #define PROGRAM_VERSION "1.6" |
| |
| #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); |
| rc = -EBADF; |
| goto err; |
| } |
| |
| rc = ubi_update_start(ulib, fileno(fp_out), nbytes); |
| if (rc != 0) { |
| err_msg("Cannot start update for %s\n", path); |
| 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; |
| } |