/* * U-Boot utility program for Linux * * Copyright (C) 2010 by Multi-Tech Systems * * Author: James Maki * * 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 #include #include #include #include #include #include #include #include #include #include #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; }