/* * 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 */ #include #include #include #include #include #include #include #include #include "config.h" #include "bootenv.h" #include "error.h" #include "example_ubi.h" #include "libubiold.h" #include "ubimirror.h" #define PROGRAM_VERSION "1.4" 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\tBuilt on " BUILD_CPU" "BUILD_OS" at "__DATE__" "__TIME__"\n" "\n" "pddcustomize - customize bootenv and pdd values.\n"; static const char *optionsstr = " -b, --both Mirror updated PDD to redundand copy.\n" " -c, --copyright Print copyright information.\n" " -i, --input= Binary input file. For debug purposes.\n" " -l, --list List card bootenv/pdd values.\n" " -o, --output= Binary output file. For debug purposes.\n" " -s, --side= The side/seqnum to update.\n" " -x, --host use x86 platform for debugging.\n" " -?, --help Give this help list\n" " --usage Give a short usage message\n" " -V, --version Print program version\n"; static const char *usage = "Usage: pddcustomize [-bclx?V] [-i ] [-o ] [-s ]\n" " [--both] [--copyright] [--input=] [--list]\n" " [--output=] [--side=] [--host] [--help] [--usage]\n" " [--version] [key=value] [...]\n"; struct option long_options[] = { { .name = "both", .has_arg = 0, .flag = NULL, .val = 'b' }, { .name = "copyright", .has_arg = 0, .flag = NULL, .val = 'c' }, { .name = "input", .has_arg = 1, .flag = NULL, .val = 'i' }, { .name = "list", .has_arg = 0, .flag = NULL, .val = 'l' }, { .name = "output", .has_arg = 1, .flag = NULL, .val = 'o' }, { .name = "side", .has_arg = 1, .flag = NULL, .val = 's' }, { .name = "host", .has_arg = 0, .flag = NULL, .val = 'x' }, { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' }, { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 }, { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, { NULL, 0, NULL, 0} }; static const char copyright [] __attribute__((unused)) = "Copyright IBM Corp 2006"; typedef struct myargs { action_t action; const char* file_in; const char* file_out; int both; int side; int x86; /* X86 host, use files for testing */ bootenv_t env_in; char *arg1; char **options; /* [STRING...] */ } myargs; static int get_update_side(const char* str) { uint32_t i = strtoul(str, NULL, 0); if ((i != 0) && (i != 1)) { return -1; } return i; } static int extract_pair(bootenv_t env, const char* str) { int rc = 0; char* key; char* val; key = strdup(str); if (key == NULL) return -ENOMEM; val = strstr(key, "="); if (val == NULL) { err_msg("Wrong argument: %s\n" "Expecting key=value pair.\n", str); rc = -1; goto err; } *val = '\0'; /* split strings */ val++; rc = bootenv_set(env, key, val); err: free(key); return rc; } static int parse_opt(int argc, char **argv, myargs *args) { int rc = 0; while (1) { int key; key = getopt_long(argc, argv, "clbxs:i:o:?V", long_options, NULL); if (key == -1) break; switch (key) { case 'c': err_msg("%s\n", copyright); ABORT_ARGP; break; case 'l': args->action = ACT_LIST; break; case 'b': args->both = 1; break; case 'x': args->x86 = 1; break; case 's': args->side = get_update_side(optarg); if (args->side < 0) { err_msg("Unsupported seqnum: %d.\n" "Supported seqnums are " "'0' and '1'\n", args->side, optarg); ERR_ARGP; } break; case 'i': args->file_in = optarg; break; case 'o': args->file_out = optarg; break; case '?': /* help */ err_msg("Usage: pddcustomize [OPTION...] " "[key=value] [...]"); err_msg("%s", doc); err_msg("%s", optionsstr); err_msg("\nReport bugs to %s", PACKAGE_BUGREPORT); exit(0); break; case 'V': err_msg("%s", PROGRAM_VERSION); exit(0); break; default: err_msg("%s", usage); exit(-1); } } if (optind < argc) { rc = extract_pair(args->env_in, argv[optind++]); if (rc != 0) ERR_ARGP; } return 0; } static int list_bootenv(bootenv_t env) { int rc = 0; rc = bootenv_write_txt(stdout, env); if (rc != 0) { err_msg("Cannot list bootenv/pdd. rc: %d\n", rc); goto err; } err: return rc; } static int process_key_value(bootenv_t env_in, bootenv_t env) { int rc = 0; size_t size, i; const char* tmp; const char** key_vec = NULL; rc = bootenv_get_key_vector(env_in, &size, 0, &key_vec); if (rc != 0) goto err; for (i = 0; i < size; i++) { rc = bootenv_get(env_in, key_vec[i], &tmp); if (rc != 0) { err_msg("Cannot read value to input key: %s. rc: %d\n", key_vec[i], rc); goto err; } rc = bootenv_set(env, key_vec[i], tmp); if (rc != 0) { err_msg("Cannot set value key: %s. rc: %d\n", key_vec[i], rc); goto err; } } err: if (key_vec != NULL) free(key_vec); return rc; } static int read_bootenv(const char* file, bootenv_t env) { int rc = 0; FILE* fp_in = NULL; fp_in = fopen(file, "rb"); if (fp_in == NULL) { err_msg("Cannot open file: %s\n", file); return -EIO; } rc = bootenv_read(fp_in, env, BOOTENV_MAXSIZE); if (rc != 0) { err_msg("Cannot read bootenv from file %s. rc: %d\n", file, rc); goto err; } err: fclose(fp_in); return rc; } /* * Read bootenv from ubi volume */ static int ubi_read_bootenv(uint32_t devno, uint32_t id, bootenv_t env) { 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; } 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; }