summaryrefslogtreecommitdiff
path: root/src/u_boot.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/u_boot.c')
-rw-r--r--src/u_boot.c363
1 files changed, 363 insertions, 0 deletions
diff --git a/src/u_boot.c b/src/u_boot.c
new file mode 100644
index 0000000..e240475
--- /dev/null
+++ b/src/u_boot.c
@@ -0,0 +1,363 @@
+/*
+ * U-Boot utility program for Linux
+ *
+ * Copyright (C) 2010 by Multi-Tech Systems
+ *
+ * Author: James Maki <jmaki@multitech.com>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <zlib.h>
+
+#include "u_boot.h"
+
+static int tokcmp(const char *cmd, const char *pattern) {
+ int len = strlen(cmd);
+ if (len > strlen(pattern)) {
+ return -1;
+ }
+
+ return memcmp(pattern, cmd, len);
+}
+
+#define ENV_HEADER_SIZE (sizeof(uint32_t) + sizeof(uint8_t))
+#define ENV_DATA_SIZE (MTD_SIZE - ENV_HEADER_SIZE)
+
+struct environment {
+ uint32_t crc;
+ uint8_t flags;
+ char data[ENV_DATA_SIZE];
+};
+
+static int write_uboot_env(const char *device, struct environment *env)
+{
+ int err;
+ int fd;
+
+ err = mtd_erase_all(device, 0);
+ if (!err) {
+ error("mtd_erase_all %s failed", device);
+ return -1;
+ }
+
+ fd = open(device, O_WRONLY);
+ if (fd < 0) {
+ error("open %s failed: %d", device, errno);
+ return -1;
+ }
+
+ err = write(fd, env, sizeof(*env));
+ if (err != sizeof(*env)) {
+ error("write to %s failed: %d", device, errno);
+ close(fd);
+ return -1;
+ }
+
+ close(fd);
+
+ return err;
+}
+
+static int read_uboot_env(const char *device, struct environment *env)
+{
+ int fd;
+ int err;
+
+ memset(env, 0, sizeof(*env));
+
+ fd = open(device, O_RDONLY);
+ if (fd < 0) {
+ error("open %s failed: %d", device, errno);
+ return -1;
+ }
+
+ err = read(fd, env, sizeof(*env));
+ if (err != sizeof(*env)) {
+ error("read from %s failed: %d", device, errno);
+ close(fd);
+ return -1;
+ }
+
+ close(fd);
+
+ return err;
+}
+
+static char *next_var(const char *var)
+{
+ return (char *) (var + strlen(var) + 1);
+}
+
+static int cmd_printenv(struct environment *env, int argc, char **argv)
+{
+ char *var;
+ char *name = NULL;
+ size_t name_len = 0;
+
+ if (argc) {
+ name = *argv;
+ name_len = strlen(name);
+ }
+
+ var = env->data;
+ while (*var) {
+ if (!name) {
+ printf("%s\n", var);
+ } else if (!strncmp(var, name, name_len) && var[name_len] == '=') {
+ printf("%s\n", var);
+ return true;
+ }
+
+ var = next_var(var);
+ }
+
+ if (name) {
+ fprintf(stderr, "environment variable not found\n");
+ return false;
+ }
+
+ return true;
+}
+
+static int cmd_setenv(struct environment *env, int argc, char **argv)
+{
+ int tmp;
+ char *var;
+ char *cp;
+ char *end = env->data + sizeof(env->data);
+ char *name;
+ size_t name_len;
+ size_t value_len;
+
+ if (!argc) {
+ fprintf(stderr, "name expected\n");
+ return false;
+ }
+ name = *argv;
+ argc--; argv++;
+
+ name_len = strlen(name);
+
+ var = env->data;
+ while (*var) {
+ if (!strncmp(var, name, name_len) && var[name_len] == '=') {
+ dbg("found variable (%s) at %lld", var, (long long int) var);
+
+ cp = next_var(var);
+ while (*cp) {
+ while (*cp) {
+ *var++ = *cp++;
+ }
+ *var++ = *cp++;
+ }
+
+ break;
+ }
+
+ var = next_var(var);
+ }
+
+ value_len = 0;
+ for (tmp = 0; tmp < argc; tmp++) {
+ value_len += strlen(argv[tmp]);
+ if (tmp + 1 < argc) {
+ value_len += 1;
+ }
+ }
+
+ if (value_len) {
+ if (var + strlen(name) + strlen("=") + value_len + 1 >= end) {
+ error("end of environment reached");
+ return false;
+ }
+ *var = 0;
+ strcat(var, name);
+ strcat(var, "=");
+ for (tmp = 0; tmp < argc; tmp++) {
+ strcat(var, argv[tmp]);
+ if (tmp + 1 < argc) {
+ strcat(var, " ");
+ }
+ }
+ var += strlen(var) + 1;
+ }
+
+ *var = 0;
+ if (var == env->data) {
+ *(var + 1) = 0;
+ }
+
+ env->crc = crc32(0, (uint8_t *) env->data, sizeof(env->data));
+ dbg("crc: 0x%08X", env->crc);
+ env->flags = 0;
+
+ tmp = write_uboot_env(MTD_ENV1, env);
+ if (tmp < 0) {
+ dbg("write_uboot_env %s failed with %d", MTD_ENV1, tmp);
+ }
+ tmp = write_uboot_env(MTD_ENV2, env);
+ if (tmp < 0) {
+ dbg("write_uboot_env %s failed with %d", MTD_ENV1, tmp);
+ }
+
+ return true;
+}
+
+static void print_version(const char *name) {
+ printf("%s (" PACKAGE ") " VERSION " (" __DATE__ " " __TIME__ ")\n", name);
+ printf("Copyright (C) 2010 by Multi-Tech Systems\n");
+ printf(
+"This program is free software; you may redistribute it under the terms of\n"
+"the GNU General Public License version 2 or (at your option) any later version.\n"
+"This program has absolutely no warranty.\n");
+}
+
+static void usage(FILE *out) {
+ fprintf(out, "Usage: u-boot { printenv [ name ] | setenv name [ value ] }\n");
+ fprintf(out, "\n");
+}
+
+int main(int argc, char *argv[]) {
+ int err;
+ char *cmd;
+ char *option;
+ struct environment *env = NULL;
+
+ struct environment *env1;
+ struct environment *env2;
+ uint32_t crc1_ok;
+ uint32_t crc2_ok;
+
+ if (argc <= 1) {
+ usage(stderr);
+ exit(1);
+ }
+ argc--; argv++;
+
+ while (argc) {
+ option = *argv;
+ if (*option != '-') {
+ break;
+ } else if (!strcmp(option, "--")) {
+ break;
+ } else if (!strcmp(option, "--version")) {
+ print_version("u-boot");
+ exit(0);
+ } else if (!strcmp(option, "--help")) {
+ usage(stdout);
+ exit(0);
+ } else {
+ usage(stderr);
+ exit(1);
+ }
+
+ argc--; argv++;
+ }
+
+ if (!argc) {
+ usage(stderr);
+ exit(1);
+ }
+
+ env1 = malloc(sizeof(struct environment));
+ if (!env1) {
+ error("out of memory");
+ return 1;
+ }
+
+ env2 = malloc(sizeof(struct environment));
+ if (!env2) {
+ error("out of memory");
+ return 1;
+ }
+
+ err = read_uboot_env(MTD_ENV1, env1);
+ if (err < 0) {
+ error("read_uboot_env failed");
+ return 1;
+ }
+ dbg("env1 crc: 0x%08X", env1->crc);
+ dbg("env1 flags: %d", env1->flags);
+ if (crc32(0, (uint8_t *) env1->data, sizeof(env1->data)) == env1->crc) {
+ crc1_ok = 1;
+ } else {
+ error("crc does not match on primary env");
+ crc1_ok = 0;
+ }
+
+ err = read_uboot_env(MTD_ENV2, env2);
+ if (err < 0) {
+ error("read_uboot_env failed");
+ return 1;
+ }
+ dbg("env2 crc: 0x%08X", env2->crc);
+ dbg("env2 flags: %d", env2->flags);
+ if (crc32(0, (uint8_t *) env2->data, sizeof(env2->data)) == env2->crc) {
+ crc2_ok = 1;
+ } else {
+ error("crc does not match on redundant env");
+ crc2_ok = 0;
+ }
+
+ if (!crc1_ok && !crc2_ok) {
+ error("both environments are bad: loading DEFAULT_ENV");
+ env = env1;
+ env->flags = 0;
+ memcpy(env->data, DEFAULT_ENV, sizeof(DEFAULT_ENV));
+ } else if (crc1_ok && !crc2_ok) {
+ env = env1;
+ } else if (!crc1_ok && crc2_ok) {
+ env = env2;
+ } else {
+ if (env1->flags == 255 && env2->flags == 0) {
+ env = env2;
+ } else if (env2->flags == 255 && env1->flags == 0) {
+ env = env1;
+ } else if (env1->flags > env2->flags) {
+ env = env1;
+ } else if (env2->flags > env1->flags) {
+ env = env2;
+ } else {
+ env = env1;
+ }
+ }
+
+ cmd = *argv;
+ argc--; argv++;
+ if (!tokcmp(cmd, "printenv")) {
+ err = cmd_printenv(env, argc, argv);
+ } else if (!tokcmp(cmd, "setenv")) {
+ err = cmd_setenv(env, argc, argv);
+ } else {
+ usage(stderr);
+ exit(1);
+ }
+
+ return !err;
+}
+