/* * (C) Copyright 2000 * Wolfgang Denk, DENX Software Engineering, wd@denx.de. * * See file CREDITS for list of people who contributed to this * project. * * 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 "fw_env.h" typedef unsigned char uchar; #define CMD_GETENV "fw_printenv" #define CMD_SETENV "fw_setenv" typedef struct envdev_s { uchar devname[16]; /* Device name */ ulong devoff; /* Device offset */ ulong env_size; /* environment size */ ulong erase_size; /* device erase size */ } envdev_t; static envdev_t envdevices[2]; static int curdev; #define DEVNAME(i) envdevices[(i)].devname #define DEVOFFSET(i) envdevices[(i)].devoff #define ENVSIZE(i) envdevices[(i)].env_size #define DEVESIZE(i) envdevices[(i)].erase_size #define CFG_ENV_SIZE ENVSIZE(curdev) #define ENV_SIZE getenvsize() typedef struct environment_s { ulong crc; /* CRC32 over data bytes */ uchar flags; /* active or obsolete */ uchar *data; } env_t; static env_t environment; static int HaveRedundEnv = 0; static uchar active_flag = 1; static uchar obsolete_flag = 0; #define XMK_STR(x) #x #define MK_STR(x) XMK_STR(x) static uchar default_environment[] = { #if defined(CONFIG_BOOTARGS) "bootargs=" CONFIG_BOOTARGS "\0" #endif #if defined(CONFIG_BOOTCOMMAND) "bootcmd=" CONFIG_BOOTCOMMAND "\0" #endif #if defined(CONFIG_RAMBOOTCOMMAND) "ramboot=" CONFIG_RAMBOOTCOMMAND "\0" #endif #if defined(CONFIG_NFSBOOTCOMMAND) "nfsboot=" CONFIG_NFSBOOTCOMMAND "\0" #endif #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) "bootdelay=" MK_STR(CONFIG_BOOTDELAY) "\0" #endif #if defined(CONFIG_BAUDRATE) && (CONFIG_BAUDRATE >= 0) "baudrate=" MK_STR(CONFIG_BAUDRATE) "\0" #endif #ifdef CONFIG_LOADS_ECHO "loads_echo=" MK_STR(CONFIG_LOADS_ECHO) "\0" #endif #ifdef CONFIG_ETHADDR "ethaddr=" MK_STR(CONFIG_ETHADDR) "\0" #endif #ifdef CONFIG_ETH1ADDR "eth1addr=" MK_STR(CONFIG_ETH1ADDR) "\0" #endif #ifdef CONFIG_ETH2ADDR "eth2addr=" MK_STR(CONFIG_ETH2ADDR) "\0" #endif #ifdef CONFIG_ETHPRIME "ethprime=" CONFIG_ETHPRIME "\0" #endif #ifdef CONFIG_IPADDR "ipaddr=" MK_STR(CONFIG_IPADDR) "\0" #endif #ifdef CONFIG_SERVERIP "serverip=" MK_STR(CONFIG_SERVERIP) "\0" #endif #ifdef CFG_AUTOLOAD "autoload=" CFG_AUTOLOAD "\0" #endif #ifdef CONFIG_ROOTPATH "rootpath=" MK_STR(CONFIG_ROOTPATH) "\0" #endif #ifdef CONFIG_GATEWAYIP "gatewayip=" MK_STR(CONFIG_GATEWAYIP) "\0" #endif #ifdef CONFIG_NETMASK "netmask=" MK_STR(CONFIG_NETMASK) "\0" #endif #ifdef CONFIG_HOSTNAME "hostname=" MK_STR(CONFIG_HOSTNAME) "\0" #endif #ifdef CONFIG_BOOTFILE "bootfile=" MK_STR(CONFIG_BOOTFILE) "\0" #endif #ifdef CONFIG_LOADADDR "loadaddr=" MK_STR(CONFIG_LOADADDR) "\0" #endif #ifdef CONFIG_PREBOOT "preboot=" CONFIG_PREBOOT "\0" #endif #ifdef CONFIG_CLOCKS_IN_MHZ "clocks_in_mhz=" "1" "\0" #endif #if defined(CONFIG_PCI_BOOTDELAY) && (CONFIG_PCI_BOOTDELAY > 0) "pcidelay=" MK_STR(CONFIG_PCI_BOOTDELAY) "\0" #endif #ifdef CONFIG_EXTRA_ENV_SETTINGS CONFIG_EXTRA_ENV_SETTINGS #endif "\0" /* Termimate env_t data with 2 NULs */ }; static int flash_io (int mode); static uchar *envmatch(uchar *s1, uchar *s2); static int env_init(void); static int parse_config(void); #if defined(CONFIG_FILE) static int get_config(char *); #endif static inline ulong getenvsize(void) { ulong rc = CFG_ENV_SIZE - sizeof(long); if (HaveRedundEnv) rc -= sizeof(char); return rc; } /* * Search the environment for a variable. * Return the value, if found, or NULL, if not found. */ unsigned char *fw_getenv (unsigned char *name) { uchar *env, *nxt; if (env_init()) return (NULL); for (env=environment.data; *env; env=nxt+1) { uchar *val; for (nxt=env; *nxt; ++nxt) { if (nxt >= &environment.data[ENV_SIZE]) { fprintf (stderr, "## Error: " "environment not terminated\n"); return (NULL); } } val=envmatch(name, env); if (!val) continue; return (val); } return (NULL); } /* * Print the current definition of one, or more, or all * environment variables */ void fw_printenv(int argc, char *argv[]) { uchar *env, *nxt; int i, n_flag; if (env_init()) return; if (argc == 1) { /* Print all env variables */ for (env=environment.data; *env; env=nxt+1) { for (nxt=env; *nxt; ++nxt) { if (nxt >= &environment.data[ENV_SIZE]) { fprintf (stderr, "## Error: " "environment not terminated\n"); return; } } printf("%s\n", env); } return; } if (strcmp(argv[1], "-n") == 0) { n_flag = 1; ++argv; --argc; if (argc != 2) { fprintf (stderr, "## Error: " "`-n' option requires exactly one argument\n"); return; } } else { n_flag = 0; } for (i=1; i= &environment.data[ENV_SIZE]) { fprintf (stderr, "## Error: " "environment not terminated\n"); return; } } val=envmatch(name, env); if (val) { if (!n_flag) { fputs (name, stdout); putc ('=', stdout); } puts (val); break; } } if (!val) fprintf (stderr, "## Error: \"%s\" not defined\n", name); } } /* * Deletes or sets environment variables. Returns errno style error codes: * 0 - OK * EINVAL - need at least 1 argument * EROFS - certain variables ("ethaddr", "serial#") cannot be * modified or deleted * */ int fw_setenv (int argc, char *argv[]) { int i, len; uchar *env, *nxt; uchar *oldval = NULL; uchar *name; if (argc < 2) { return (EINVAL); } if (env_init()) return (errno); name = argv[1]; /* * search if variable with this name already exists */ for (env=environment.data; *env; env=nxt+1) { for (nxt=env; *nxt; ++nxt) { if (nxt >= &environment.data[ENV_SIZE]) { fprintf (stderr, "## Error: " "environment not terminated\n"); return (EINVAL); } } if ((oldval=envmatch(name, env)) != NULL) break; } /* * Delete any existing definition */ if (oldval) { /* * Ethernet Address and serial# can be set only once */ if ((strcmp (name, "ethaddr") == 0) || (strcmp (name, "serial#") == 0) ) { fprintf (stderr, "Can't overwrite \"%s\"\n", name); return (EROFS); } if (*++nxt == '\0') { *env = '\0'; } else { for (;;) { *env = *nxt++; if ((*env == '\0') && (*nxt == '\0')) break; ++env; } } *++env = '\0'; } /* Delete only ? */ if (argc < 3) goto WRITE_FLASH; /* * Append new definition at the end */ for (env=environment.data; *env || *(env+1); ++env) ; if (env > environment.data) ++env; /* * Overflow when: * "name" + "=" + "val" +"\0\0" > CFG_ENV_SIZE - (env-environment) */ len = strlen(name) + 2; /* add '=' for first arg, ' ' for all others */ for (i=2; i (&environment.data[ENV_SIZE]-env)) { fprintf (stderr, "Error: environment overflow, \"%s\" deleted\n", name); return (-1); } while ((*env = *name++) != '\0') env++; for (i=2; i