/* * 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 #include #include #include #include #include #include #include #include #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: "", flags: 0, doc: "Binary input file. For debug purposes.", group: 1 }, { name: "output", key: 'o', arg: "", 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: "", 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; }