/* * 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 #include "u_boot.h" #include "mtd_erase_all.h" #define XMK_STR(x) #x #define MK_STR(x) XMK_STR(x) #define DUMMY_MAC "00:00:00:00:00:00" #define EMPTY_CRC 0xf9137807 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 (CONFIG_ENV_SIZE - ENV_HEADER_SIZE) #define DO_WRITE_ENV 1 #define SKIP_WRITE_ENV 0 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; } if(env == NULL) return 0; 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 do_write) { 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 %p", var, 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("Calculated crc (to be stored): 0x%08X", env->crc); env->flags = 0; if(do_write == DO_WRITE_ENV) { 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, 2018 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 ] | clearenv }\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; uint32_t crc1_calc, crc2_calc; 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 stored crc: 0x%08X", env1->crc); dbg("env1 flags: %d", env1->flags); crc1_calc = crc32(0, (uint8_t *) env1->data, sizeof(env1->data)); if (crc1_calc == env1->crc) { crc1_ok = 1; } else { dbg("env1 stored crc 0x%x, calculated crc 0x%x",env1->crc,crc1_calc); if ((env1->crc != 0xffffffff) || (crc1_calc != EMPTY_CRC)) error("crc does not match on env1"); else dbg("uninitialized env1"); crc1_ok = 0; } err = read_uboot_env(MTD_ENV2, env2); if (err < 0) { error("read_uboot_env failed"); return 1; } dbg("env2 stored crc: 0x%08X", env2->crc); dbg("env2 flags: %d", env2->flags); crc2_calc = crc32(0, (uint8_t *) env2->data, sizeof(env2->data)); if (crc2_calc == env2->crc) { crc2_ok = 1; } else { dbg("env2 stored crc 0x%x, calculated crc 0x%x",env2->crc,crc2_calc); if ((env2->crc != 0xffffffff) || (crc2_calc != EMPTY_CRC) ) error("crc does not match on env2"); else dbg("uninitialized env2"); crc2_ok = 0; } if (!crc1_ok && !crc2_ok && (env1->crc == 0xffffffff) && (env2->crc == 0xffffffff) && (crc1_calc == EMPTY_CRC) && (crc2_calc == EMPTY_CRC) ) { int mp_fd = open(MAC_PATH, O_RDONLY); int len,retval; char *p; char *genarg[2]; char number[128]; fputs("WARNING: Flash is in initial state, so use defaults\n",stderr); env = env1; env->flags = 0; memcpy(env->data, DEFAULT_ENV, sizeof(DEFAULT_ENV)); /* MTHS has no ethernet */ /* Now need to find the MAC address */ if (mp_fd != -1) { len = sizeof DUMMY_MAC - 1; /* remove null from count */ p = malloc(len+1); retval = read(mp_fd,p,len); dbg("Mac read of %d returned %d",len,retval); if(retval != len) { if(retval == -1) { perror("Failed to read: " MAC_PATH); exit(1); } if (retval != len) { fprintf(stderr,"%s: Only read %d characters of %d for the MAC address\n", MAC_PATH,retval,len); fprintf(stderr,"%s: Read %*.*s\n",MAC_PATH,retval,retval,p); exit(1); } } p[len] = 0; genarg[0] = "ethaddr"; genarg[1] = p; if (cmd_setenv(env,2,genarg,SKIP_WRITE_ENV) == false) exit(1); free(p); } genarg[1] = number; #ifdef CONFIG_BAUDRATE genarg[0] = "baudrate"; sprintf(number,"%lld",(long long)(CONFIG_BAUDRATE)); if (cmd_setenv(env,2,genarg,SKIP_WRITE_ENV) == false) exit(1); #endif #ifdef CONFIG_BOOTDELAY genarg[0] = "bootdelay"; sprintf(number,"%lld",(long long)(CONFIG_BOOTDELAY)); if (cmd_setenv(env,2,genarg,SKIP_WRITE_ENV) == false) exit(1); #endif #ifdef CONFIG_LOADADDR genarg[0] = "loadaddr"; sprintf(number,"0x%llx",(long long)(CONFIG_LOADADDR)); if (cmd_setenv(env,2,genarg,SKIP_WRITE_ENV) == false) exit(1); #endif #ifdef CONFIG_HOSTNAME genarg[0] = "hostname"; genarg[1] = MK_STR(CONFIG_HOSTNAME); if (cmd_setenv(env,2,genarg,SKIP_WRITE_ENV) == false) exit(1); #endif #ifdef CONFIG_IPADDR genarg[0] = "ipaddr"; genarg[1] = MK_STR(CONFIG_IPADDR); if (cmd_setenv(env,2,genarg,SKIP_WRITE_ENV) == false) exit(1); #endif #ifdef CONFIG_SERVERIP genarg[0] = "serverip"; genarg[1] = MK_STR(CONFIG_SERVERIP); if (cmd_setenv(env,2,genarg,SKIP_WRITE_ENV) == false) exit(1); #endif #ifdef CONFIG_NETMASK genarg[0] = "netmask"; genarg[1] = MK_STR(CONFIG_NETMASK); if (cmd_setenv(env,2,genarg,SKIP_WRITE_ENV) == false) exit(1); #endif #ifdef CONFIG_EXTRA_ENV_SETTINGS p = env->data; while((p = next_var(p)) && *p); len = (p - env->data); if (len + sizeof CONFIG_EXTRA_ENV_SETTINGS < ENV_DATA_SIZE) memcpy(env->data + len,CONFIG_EXTRA_ENV_SETTINGS,sizeof CONFIG_EXTRA_ENV_SETTINGS); else fprintf(stderr,"Default memory settings overflow. Current size %d, CONFIG_EXTRA_ENV_SETTINGS size %d, Maximum %d\n", len,sizeof CONFIG_EXTRA_ENV_SETTINGS,ENV_DATA_SIZE); #endif #ifdef CONFIG_BOOTARGS genarg[0] = "bootargs"; genarg[1] = CONFIG_BOOTARGS; if (cmd_setenv(env,2,genarg,SKIP_WRITE_ENV) == false) exit(1); #endif #ifdef CONFIG_BOOTCOMMAND genarg[0] = "bootcmd"; genarg[1] = CONFIG_BOOTCOMMAND; if (cmd_setenv(env,2,genarg,SKIP_WRITE_ENV) == false) exit(1); #endif } else if (!crc1_ok && !crc2_ok) { error("both environments are bad: not loading DEFAULT_ENV"); exit(1); } 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, DO_WRITE_ENV); } else if (!tokcmp(cmd, "clearenv")) { (void)write_uboot_env(MTD_ENV1, NULL); (void)write_uboot_env(MTD_ENV2, NULL); } else { usage(stderr); exit(1); } return !err; }