summaryrefslogtreecommitdiff
path: root/ubi-utils/src/pddcustomize.c
diff options
context:
space:
mode:
Diffstat (limited to 'ubi-utils/src/pddcustomize.c')
-rw-r--r--ubi-utils/src/pddcustomize.c500
1 files changed, 500 insertions, 0 deletions
diff --git a/ubi-utils/src/pddcustomize.c b/ubi-utils/src/pddcustomize.c
new file mode 100644
index 0000000..a86e942
--- /dev/null
+++ b/ubi-utils/src/pddcustomize.c
@@ -0,0 +1,500 @@
+/*
+ * 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 <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.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 "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=<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)
+{
+ 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;
+}