[U-Boot] [PATCH 0/5] New environment code

The following patch series adds some utilities (qsort and hash table based database functions) and uses these to reimplement the code used for the internal handling of environment variables.
Motivation:
* Old environment code used a pessimizing implementation: - variable lookup used linear search => slow - changed/added variables were added at the end, i. e. most frequently used variables had the slowest access times => slow - each setenv() would calculate the CRC32 checksum over the whole environment block => slow * "redundant" envrionment was locked down to two copies * No easy way to implement features like "reset to factory defaults", or to select one out of several pre-defined (previously saved) sets of environment settings ("profiles") * No easy way to import or export environment settings
======================================================================
API Changes:
- Variable names starting with '#' are no longer allowed
I didn't find any such variable names being used; it is highly recommended to follow standard conventions and start variable names with an alphanumeric character
- "printenv" will now print a backslash at the end of all but the last lines of a multi-line variable value.
Multi-line variables have never been formally defined, allthough there is no reason not to use them. Now we define rules how to deal with them, allowing for import and export.
- Function forceenv() and the related code in saveenv() was removed. At the moment this is causing build problems for the only user of this code (schmoogie - which has no entry in MAINTAINERS); may be fixed later by implementing the "env set -f" feature.
Inconsistencies:
- "printenv" will '\'-escape the '\n' in multi-line variables, while "printenv var" will not do that.
======================================================================
Advantages:
- "printenv" output much better readable (sorted) - faster! - extendable (additional variable properties can be added) - new, powerful features like "factory reset" or easy switching between several different environment settings ("profiles")
Disadvantages:
- Image size grows by typically 5...7 KiB (might shrink a bit again on systems with redundant environment with a following patch series)
======================================================================
Implemented:
- env command with subcommands:
- env print [arg ...]
same as "printenv": print environment
- env set [-f] name [arg ...]
same as "setenv": set (and delete) environment variables
["-f" - force setting even for read-only variables - not implemented yet.]
- end delete [-f] name
not implemented yet
["-f" - force delete even for read-only variables]
- env save
same as "saveenv": save environment
- env export [-t | -b | -c] addr [size]
export internal representation (hash table) in formats usable for persistent storage or processing:
-t: export as text format; if size is given, data will be padded with '\0' bytes; if not, one terminating '\0' will be added (which is included in the "filesize" setting so you can for exmple copy this to flash and keep the termination). -b: export as binary format (name=value pairs separated by '\0', list end marked by double "\0\0") -c: export as checksum protected environment format as used for example by "saveenv" command addr: memory address where environment gets stored size: size of output buffer
With "-c" and size is NOT given, then the export command will format the data as currently used for the persistent storage, i. e. it will use CONFIG_ENV_SECT_SIZE as output block size and prepend a valid CRC32 checksum and, in case of resundant environment, a "current" redundancy flag. If size is given, this value will be used instead of CONFIG_ENV_SECT_SIZE; again, CRC32 checksum and redundancy flag will be inserted.
With "-b" and "-t", always only the real data (including a terminating '\0' byte) will be written; here the optional size argument will be used to make sure not to overflow the user provided buffer; the command will abort if the size is not sufficient. Any remainign space will be '\0' padded.
On successful return, the variable "filesize" will be set. Note that filesize includes the trailing/terminating '\0' byte(s).
Usage szenario: create a text snapshot/backup of the current settings:
=> env export -t 100000 => era ${backup_addr} +${filesize} => cp.b 100000 ${backup_addr} ${filesize}
Re-import this snapshot, deleting all other settings:
=> env import -d -t ${backup_addr}
- env import [-d] [-t | -b | -c] addr [size]
import external format (text or binary) into hash table, optionally deleting existing values:
-d: delete existing environment before importing; otherwise overwrite / append to existion definitions -t: assume text format; either "size" must be given or the text data must be '\0' terminated -b: assume binary format ('\0' separated, "\0\0" terminated) -c: assume checksum protected environment format addr: memory address to read from size: length of input data; if missing, proper '\0' termination is mandatory
- env default -f
reset default environment: drop all environment settings and load default environment
- env ask name [message] [size]
same as "askenv": ask for environment variable
- env edit name
same as "editenv": edit environment variable
- env run
same as "run": run commands in an environment variable
======================================================================
TODO:
- drop default env as implemented now; provide a text file based initialization instead (eventually using several text files to incrementally build it from common blocks) and a tool to convert it into a binary blob / object file.
- It would be nice if we could add wildcard support for environment variables; this is needed for variable name auto-completion, but it would also be nice to be able to say "printenv ip*" or "printenv *addr*"
- Some boards don't link any more due to the grown code size: AR405, CANBT, DU405, PMC440, canyonlands, sc3, sequoia, socrates.
=> cc: Matthias Fuchs matthias.fuchs@esd-electronics.com, Stefan Roese sr@denx.de, Heiko Schocher hs@denx.de
- Dropping forceenv() causes build problems on schmoogie
=> cc: Sergey Kubushyn ksi@koi8.net
- Build tested on PPC and ARM only; runtime tested with NOR and NAND flash only => needs testing!!
Wolfgang Denk (5): Add basic errno support. Add qsort - add support for sorting data arrays Add hash table support as base for new environment code Remove support for CONFIG_HAS_UID and "forceenv" command New implementation for internal handling of environment variables.
README | 8 + board/zeus/zeus.c | 25 +- common/cmd_nvedit.c | 668 ++++++++++++++++++++++---------- common/command.c | 3 +- common/dlmalloc.c | 25 +- common/env_common.c | 125 +++--- common/env_dataflash.c | 114 ++++-- common/env_eeprom.c | 85 +++-- common/env_flash.c | 245 +++++++------ common/env_mgdisk.c | 33 +- common/env_nand.c | 188 +++++---- common/env_nowhere.c | 11 +- common/env_nvram.c | 46 ++- common/env_onenand.c | 65 ++-- common/env_sf.c | 132 ++++--- common/exports.c | 4 - include/_exports.h | 1 - include/common.h | 7 +- include/configs/MVSMR.h | 1 - include/configs/davinci_schmoogie.h | 1 - include/environment.h | 5 +- include/errno.h | 9 + include/exports.h | 3 - include/search.h | 106 +++++ lib/Makefile | 3 + lib/errno.c | 1 + lib/hashtable.c | 735 +++++++++++++++++++++++++++++++++++ lib/qsort.c | 69 ++++ 28 files changed, 1998 insertions(+), 720 deletions(-) create mode 100644 include/errno.h create mode 100644 include/search.h create mode 100644 lib/errno.c create mode 100644 lib/hashtable.c create mode 100644 lib/qsort.c

Needed for hash table support; probably useful in a lot of other places as well.
Signed-off-by: Wolfgang Denk wd@denx.de --- include/errno.h | 9 +++++++++ lib/Makefile | 1 + lib/errno.c | 1 + 3 files changed, 11 insertions(+), 0 deletions(-) create mode 100644 include/errno.h create mode 100644 lib/errno.c
diff --git a/include/errno.h b/include/errno.h new file mode 100644 index 0000000..e24a33b --- /dev/null +++ b/include/errno.h @@ -0,0 +1,9 @@ +#ifndef _ERRNO_H + +#include <asm-generic/errno.h> + +extern int errno; + +#define __set_errno(val) do { errno = val; } while (0) + +#endif /* _ERRNO_H */ diff --git a/lib/Makefile b/lib/Makefile index c45f07c..c26536d 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -37,6 +37,7 @@ COBJS-y += crc32.o COBJS-y += ctype.o COBJS-y += display_options.o COBJS-y += div64.o +COBJS-y += errno.o COBJS-$(CONFIG_GZIP) += gunzip.o COBJS-$(CONFIG_LMB) += lmb.o COBJS-y += ldiv.o diff --git a/lib/errno.c b/lib/errno.c new file mode 100644 index 0000000..8330a8f --- /dev/null +++ b/lib/errno.c @@ -0,0 +1 @@ +int errno = 0;

Dear Mike Frysinger,
In message 201007171717.27154.vapier@gentoo.org you wrote:
+++ b/lib/errno.c @@ -0,0 +1 @@ +int errno = 0;
drop the "= 0" so that errno ends up in the bss ?
This does not make any difference; the tools are clever enough to figure that out:
-> nm lib/errno.o 00000000 S errno
S = "The symbol is in an uninitialized data section for small objects."
Best regards,
Wolfgang Denk

On 07/17/2010 05:17 PM, Mike Frysinger wrote:
On Saturday, July 17, 2010 15:45:44 Wolfgang Denk wrote:
--- /dev/null +++ b/lib/errno.c @@ -0,0 +1 @@ +int errno = 0;
drop the "= 0" so that errno ends up in the bss ? -mike
Is this going to be a problem during early startup (pre-relocation) vs. normal running? Pre-relocation, we only have a temporary stack and RAM is not set up initially.
I have not looked at when RAM gets initialized vs. when env. variables get used. It is probably OK. Wolfgang?
Best regards, gvb

Dear Jerry Van Baren,
In message 4C42F8DE.5040009@gmail.com you wrote:
drop the "= 0" so that errno ends up in the bss ? -mike
Is this going to be a problem during early startup (pre-relocation) vs. normal running? Pre-relocation, we only have a temporary stack and RAM is not set up initially.
I have not looked at when RAM gets initialized vs. when env. variables get used. It is probably OK. Wolfgang?
AFAICT the relevant code runs all only after relocation to RAM, i. e. the use of errno (whether in BSS or not) should be no problem.
But you are right - using it before relocation will not work as is. We could make it a global data entry, if needed.
Best regards,
Wolfgang Denk

Dear Wolfgang Denk,
In message 1279395948-25864-2-git-send-email-wd@denx.de you wrote:
Needed for hash table support; probably useful in a lot of other places as well.
Signed-off-by: Wolfgang Denk wd@denx.de
include/errno.h | 9 +++++++++ lib/Makefile | 1 + lib/errno.c | 1 + 3 files changed, 11 insertions(+), 0 deletions(-) create mode 100644 include/errno.h create mode 100644 lib/errno.c
Applied to "next" branch.
Best regards,
Wolfgang Denk

Code adapted from uClibc-0.9.30.3
Signed-off-by: Wolfgang Denk wd@denx.de --- include/common.h | 4 +++ lib/Makefile | 1 + lib/qsort.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+), 0 deletions(-) create mode 100644 lib/qsort.c
diff --git a/include/common.h b/include/common.h index eddec22..e4b4ec0 100644 --- a/include/common.h +++ b/include/common.h @@ -631,6 +631,10 @@ static inline IPaddr_t getenv_IPaddr (char *var) return (string_to_ip(getenv(var))); }
+/* lib/qsort.c */ +void qsort(void *base, size_t nmemb, size_t size, + int(*compar)(const void *, const void *)); + /* lib/time.c */ void udelay (unsigned long);
diff --git a/lib/Makefile b/lib/Makefile index c26536d..2d969a3 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -43,6 +43,7 @@ COBJS-$(CONFIG_LMB) += lmb.o COBJS-y += ldiv.o COBJS-$(CONFIG_MD5) += md5.o COBJS-y += net_utils.o +COBJS-y += qsort.o COBJS-$(CONFIG_SHA1) += sha1.o COBJS-$(CONFIG_SHA256) += sha256.o COBJS-y += string.o diff --git a/lib/qsort.c b/lib/qsort.c new file mode 100644 index 0000000..bb47319 --- /dev/null +++ b/lib/qsort.c @@ -0,0 +1,69 @@ +/* + * Code adapted from uClibc-0.9.30.3 + * + * It is therefore covered by the GNU LESSER GENERAL PUBLIC LICENSE + * Version 2.1, February 1999 + * + * Wolfgang Denk wd@denx.de + */ + +/* This code is derived from a public domain shell sort routine by + * Ray Gardner and found in Bob Stout's snippets collection. The + * original code is included below in an #if 0/#endif block. + * + * I modified it to avoid the possibility of overflow in the wgap + * calculation, as well as to reduce the generated code size with + * bcc and gcc. */ + +#include <linux/types.h> +#if 0 +#include <assert.h> +#else +#define assert(arg) +#endif + +void qsort(void *base, + size_t nel, + size_t width, + int (*comp)(const void *, const void *)) +{ + size_t wgap, i, j, k; + char tmp; + + if ((nel > 1) && (width > 0)) { + assert(nel <= ((size_t)(-1)) / width); /* check for overflow */ + wgap = 0; + do { + wgap = 3 * wgap + 1; + } while (wgap < (nel-1)/3); + /* From the above, we know that either wgap == 1 < nel or */ + /* ((wgap-1)/3 < (int) ((nel-1)/3) <= (nel-1)/3 ==> wgap < nel. */ + wgap *= width; /* So this can not overflow if wnel doesn't. */ + nel *= width; /* Convert nel to 'wnel' */ + do { + i = wgap; + do { + j = i; + do { + register char *a; + register char *b; + + j -= wgap; + a = j + ((char *)base); + b = a + wgap; + if ((*comp)(a, b) <= 0) { + break; + } + k = width; + do { + tmp = *a; + *a++ = *b; + *b++ = tmp; + } while (--k); + } while (j >= wgap); + i += width; + } while (i < nel); + wgap = (wgap - width)/3; + } while (wgap); + } +}

Dear Wolfgang Denk,
In message 1279395948-25864-3-git-send-email-wd@denx.de you wrote:
Code adapted from uClibc-0.9.30.3
Signed-off-by: Wolfgang Denk wd@denx.de
include/common.h | 4 +++ lib/Makefile | 1 + lib/qsort.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+), 0 deletions(-) create mode 100644 lib/qsort.c
Applied to "next" branch.
Best regards,
Wolfgang Denk

This implementation is based on code from uClibc-0.9.30.3 but was modified and extended for use within U-Boot.
Major modifications and extensions:
* hsearch() [modified / extended]: - While the standard version does not make any assumptions about the type of the stored data objects at all, this implementation works with NUL terminated strings only. - Instead of storing just pointers to the original objects, we create local copies so the caller does not need to care about the data any more. - The standard implementation does not provide a way to update an existing entry. This version will create a new entry or update an existing one when both "action == ENTER" and "item.data != NULL". - hsearch_r(): Instead of returning 1 on success, we return the index into the internal hash table, which is also guaranteed to be positive. This allows us direct access to the found hash table slot for example for functions like hdelete(). * hdelete() [added]: - The standard implementation of hsearch(3) does not provide any way to delete any entries from the hash table. We extend the code to do that. * hexport() [added]: - Export the data stored in the hash table in linearized form: Entries are exported as "name=value" strings, separated by an arbitrary (non-NUL, of course) separator character. This allows to use this function both when formatting the U-Boot environment for external storage (using '\0' as separator), but also when using it for the "printenv" command to print all variables, simply by using as '\n" as separator. This can also be used for new features like exporting the environment data as text file, including the option for later re-import. - The entries in the result list will be sorted by ascending key values. * himport() [added]: - Import linearized data into hash table. This is the inverse function to hexport(): it takes a linear list of "name=value" pairs and creates hash table entries from it. - Entries without "value", i. e. consisting of only "name" or "name=", will cause this entry to be deleted from the hash table. - The "flag" argument can be used to control the behaviour: when the H_NOCLEAR bit is set, then an existing hash table will kept, i. e. new data will be added to an existing hash table; otherwise, old data will be discarded and a new hash table will be created. - The separator character for the "name=value" pairs can be selected, so we both support importing from externally stored environment data (separated by NUL characters) and from plain text files (entries separated by newline characters). - To allow for nicely formatted text input, leading white space (sequences of SPACE and TAB chars) is ignored, and entries starting (after removal of any leading white space) with a '#' character are considered comments and ignored. - NOTE: this means that a variable name cannot start with a '#' character. - When using a non-NUL separator character, backslash is used as escape character in the value part, allowing for example fo multi-line values. - In theory, arbitrary separator characters can be used, but only '\0' and '\n' have really been tested.
Signed-off-by: Wolfgang Denk wd@denx.de --- include/search.h | 106 ++++++++ lib/Makefile | 1 + lib/hashtable.c | 721 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 828 insertions(+), 0 deletions(-) create mode 100644 include/search.h create mode 100644 lib/hashtable.c
diff --git a/include/search.h b/include/search.h new file mode 100644 index 0000000..fccc757 --- /dev/null +++ b/include/search.h @@ -0,0 +1,106 @@ +/* + * Declarations for System V style searching functions. + * Copyright (C) 1995-1999, 2000 Free Software Foundation, Inc. + * This file is part of the GNU C Library. + * + * The GNU C Library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * The GNU C Library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the GNU C Library; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA. + */ + +/* + * Based on code from uClibc-0.9.30.3 + * Extensions for use within U-Boot + * Copyright (C) 2010 Wolfgang Denk wd@denx.de + */ + +#ifndef _SEARCH_H +#define _SEARCH_H 1 + +#include <stddef.h> + +#define __set_errno(val) do { errno = val; } while (0) + +/* + * Prototype structure for a linked-list data structure. + * This is the type used by the `insque' and `remque' functions. + */ + +/* For use with hsearch(3). */ +typedef int (*__compar_fn_t) (__const void *, __const void *); +typedef __compar_fn_t comparison_fn_t; + +/* Action which shall be performed in the call the hsearch. */ +typedef enum { + FIND, + ENTER +} ACTION; + +typedef struct entry { + char *key; + char *data; +} ENTRY; + +/* Opaque type for internal use. */ +struct _ENTRY; + +/* + * Family of hash table handling functions. The functions also + * have reentrant counterparts ending with _r. The non-reentrant + * functions all work on a signle internal hashing table. + */ + +/* Data type for reentrant functions. */ +struct hsearch_data { + struct _ENTRY *table; + unsigned int size; + unsigned int filled; +}; + +/* Create a new hashing table which will at most contain NEL elements. */ +extern int hcreate(size_t __nel); +extern int hcreate_r(size_t __nel, struct hsearch_data *__htab); + +/* Destroy current internal hashing table. */ +extern void hdestroy(void); +extern void hdestroy_r(struct hsearch_data *__htab); + +/* + * Search for entry matching ITEM.key in internal hash table. If + * ACTION is `FIND' return found entry or signal error by returning + * NULL. If ACTION is `ENTER' replace existing data (if any) with + * ITEM.data. + * */ +extern ENTRY *hsearch(ENTRY __item, ACTION __action); +extern int hsearch_r(ENTRY __item, ACTION __action, ENTRY ** __retval, + struct hsearch_data *__htab); + +/* Search and delete entry matching ITEM.key in internal hash table. */ +extern int hdelete(const char *__key); +extern int hdelete_r(const char *__key, struct hsearch_data *__htab); + +extern ssize_t hexport(const char __sep, char **__resp, size_t __size); +extern ssize_t hexport_r(struct hsearch_data *__htab, + const char __sep, char **__resp, size_t __size); + +extern int himport(const char *__env, size_t __size, const char __sep, + int __flag); +extern int himport_r(struct hsearch_data *__htab, + const char *__env, size_t __size, const char __sep, + int __flag); + +/* Flags for himport() / himport_r() */ +#define H_NOCLEAR 1 /* do not clear hash table before importing */ + +#endif /* search.h */ diff --git a/lib/Makefile b/lib/Makefile index 2d969a3..a8de3e1 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -39,6 +39,7 @@ COBJS-y += display_options.o COBJS-y += div64.o COBJS-y += errno.o COBJS-$(CONFIG_GZIP) += gunzip.o +COBJS-y += hashtable.o COBJS-$(CONFIG_LMB) += lmb.o COBJS-y += ldiv.o COBJS-$(CONFIG_MD5) += md5.o diff --git a/lib/hashtable.c b/lib/hashtable.c new file mode 100644 index 0000000..2f3b5c8 --- /dev/null +++ b/lib/hashtable.c @@ -0,0 +1,721 @@ +/* + * This implementation is based on code from uClibc-0.9.30.3 but was + * modified and extended for use within U-Boot. + * + * Copyright (C) 2010 Wolfgang Denk wd@denx.de + * + * Original license header: + * + * Copyright (C) 1993, 1995, 1996, 1997, 2002 Free Software Foundation, Inc. + * This file is part of the GNU C Library. + * Contributed by Ulrich Drepper drepper@gnu.ai.mit.edu, 1993. + * + * The GNU C Library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * The GNU C Library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the GNU C Library; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA. + */ + +#include <errno.h> +#include <malloc.h> + +#ifdef USE_HOSTCC /* HOST build */ +# include <string.h> +# include <assert.h> + +# ifndef debug +# ifdef DEBUG +# define debug(fmt,args...) printf(fmt ,##args) +# else +# define debug(fmt,args...) +# endif +# endif +#else /* U-Boot build */ +# include <common.h> +# include <linux/string.h> +#endif + +#include "search.h" + +/* + * [Aho,Sethi,Ullman] Compilers: Principles, Techniques and Tools, 1986 + * [Knuth] The Art of Computer Programming, part 3 (6.4) + */ + +/* + * The non-reentrant version use a global space for storing the hash table. + */ +static struct hsearch_data htab; + +/* + * The reentrant version has no static variables to maintain the state. + * Instead the interface of all functions is extended to take an argument + * which describes the current status. + */ +typedef struct _ENTRY { + unsigned int used; + ENTRY entry; +} _ENTRY; + + +/* + * hcreate() + */ + +/* + * For the used double hash method the table size has to be a prime. To + * correct the user given table size we need a prime test. This trivial + * algorithm is adequate because + * a) the code is (most probably) called a few times per program run and + * b) the number is small because the table must fit in the core + * */ +static int isprime(unsigned int number) +{ + /* no even number will be passed */ + unsigned int div = 3; + + while (div * div < number && number % div != 0) + div += 2; + + return number % div != 0; +} + +int hcreate(size_t nel) +{ + return hcreate_r(nel, &htab); +} + +/* + * Before using the hash table we must allocate memory for it. + * Test for an existing table are done. We allocate one element + * more as the found prime number says. This is done for more effective + * indexing as explained in the comment for the hsearch function. + * The contents of the table is zeroed, especially the field used + * becomes zero. + */ +int hcreate_r(size_t nel, struct hsearch_data *htab) +{ + /* Test for correct arguments. */ + if (htab == NULL) { + __set_errno(EINVAL); + return 0; + } + + /* There is still another table active. Return with error. */ + if (htab->table != NULL) + return 0; + + /* Change nel to the first prime number not smaller as nel. */ + nel |= 1; /* make odd */ + while (!isprime(nel)) + nel += 2; + + htab->size = nel; + htab->filled = 0; + + /* allocate memory and zero out */ + htab->table = (_ENTRY *) calloc(htab->size + 1, sizeof(_ENTRY)); + if (htab->table == NULL) + return 0; + + /* everything went alright */ + return 1; +} + + +/* + * hdestroy() + */ +void hdestroy(void) +{ + hdestroy_r(&htab); +} + +/* + * After using the hash table it has to be destroyed. The used memory can + * be freed and the local static variable can be marked as not used. + */ +void hdestroy_r(struct hsearch_data *htab) +{ + int i; + + /* Test for correct arguments. */ + if (htab == NULL) { + __set_errno(EINVAL); + return; + } + + /* free used memory */ + for (i = 1; i <= htab->size; ++i) { + if (htab->table[i].used) { + ENTRY *ep = &htab->table[i].entry; + + free(ep->key); + free(ep->data); + } + } + free(htab->table); + + /* the sign for an existing table is an value != NULL in htable */ + htab->table = NULL; +} + +/* + * hsearch() + */ + +/* + * This is the search function. It uses double hashing with open addressing. + * The argument item.key has to be a pointer to an zero terminated, most + * probably strings of chars. The function for generating a number of the + * strings is simple but fast. It can be replaced by a more complex function + * like ajw (see [Aho,Sethi,Ullman]) if the needs are shown. + * + * We use an trick to speed up the lookup. The table is created by hcreate + * with one more element available. This enables us to use the index zero + * special. This index will never be used because we store the first hash + * index in the field used where zero means not used. Every other value + * means used. The used field can be used as a first fast comparison for + * equality of the stored and the parameter value. This helps to prevent + * unnecessary expensive calls of strcmp. + * + * This implementation differs from the standard library version of + * this function in a number of ways: + * + * - While the standard version does not make any assumptions about + * the type of the stored data objects at all, this implementation + * works with NUL terminated strings only. + * - Instead of storing just pointers to the original objects, we + * create local copies so the caller does not need to care about the + * data any more. + * - The standard implementation does not provide a way to update an + * existing entry. This version will create a new entry or update an + * existing one when both "action == ENTER" and "item.data != NULL". + * - Instead of returning 1 on success, we return the index into the + * internal hash table, which is also guaranteed to be positive. + * This allows us direct access to the found hash table slot for + * example for functions like hdelete(). + */ + +ENTRY *hsearch(ENTRY item, ACTION action) +{ + ENTRY *result; + + (void) hsearch_r(item, action, &result, &htab); + + return result; +} + +int hsearch_r(ENTRY item, ACTION action, ENTRY ** retval, + struct hsearch_data *htab) +{ + unsigned int hval; + unsigned int count; + unsigned int len = strlen(item.key); + unsigned int idx; + + /* Compute an value for the given string. Perhaps use a better method. */ + hval = len; + count = len; + while (count-- > 0) { + hval <<= 4; + hval += item.key[count]; + } + + /* + * First hash function: + * simply take the modul but prevent zero. + */ + hval %= htab->size; + if (hval == 0) + ++hval; + + /* The first index tried. */ + idx = hval; + + if (htab->table[idx].used) { + /* + * Further action might be required according to the + * action value. + */ + unsigned hval2; + + if (htab->table[idx].used == hval + && strcmp(item.key, htab->table[idx].entry.key) == 0) { + /* Overwrite existing value? */ + if ((action == ENTER) && (item.data != NULL)) { + free(htab->table[idx].entry.data); + htab->table[idx].entry.data = + strdup(item.data); + if (!htab->table[idx].entry.data) { + __set_errno(ENOMEM); + *retval = NULL; + return 0; + } + } + /* return found entry */ + *retval = &htab->table[idx].entry; + return idx; + } + + /* + * Second hash function: + * as suggested in [Knuth] + */ + hval2 = 1 + hval % (htab->size - 2); + + do { + /* + * Because SIZE is prime this guarantees to + * step through all available indices. + */ + if (idx <= hval2) + idx = htab->size + idx - hval2; + else + idx -= hval2; + + /* + * If we visited all entries leave the loop + * unsuccessfully. + */ + if (idx == hval) + break; + + /* If entry is found use it. */ + if ((htab->table[idx].used == hval) + && strcmp(item.key, htab->table[idx].entry.key) == 0) { + /* Overwrite existing value? */ + if ((action == ENTER) && (item.data != NULL)) { + free(htab->table[idx].entry.data); + htab->table[idx].entry.data = + strdup(item.data); + if (!htab->table[idx].entry.data) { + __set_errno(ENOMEM); + *retval = NULL; + return 0; + } + } + /* return found entry */ + *retval = &htab->table[idx].entry; + return idx; + } + } + while (htab->table[idx].used); + } + + /* An empty bucket has been found. */ + if (action == ENTER) { + /* + * If table is full and another entry should be + * entered return with error. + */ + if (htab->filled == htab->size) { + __set_errno(ENOMEM); + *retval = NULL; + return 0; + } + + /* + * Create new entry; + * create copies of item.key and item.data + */ + htab->table[idx].used = hval; + htab->table[idx].entry.key = strdup(item.key); + htab->table[idx].entry.data = strdup(item.data); + if (!htab->table[idx].entry.key || + !htab->table[idx].entry.data) { + __set_errno(ENOMEM); + *retval = NULL; + return 0; + } + + ++htab->filled; + + /* return new entry */ + *retval = &htab->table[idx].entry; + return 1; + } + + __set_errno(ESRCH); + *retval = NULL; + return 0; +} + + +/* + * hdelete() + */ + +/* + * The standard implementation of hsearch(3) does not provide any way + * to delete any entries from the hash table. We extend the code to + * do that. + */ + +int hdelete(const char *key) +{ + return hdelete_r(key, &htab); +} + +int hdelete_r(const char *key, struct hsearch_data *htab) +{ + ENTRY e, *ep; + int idx; + + debug("hdelete: DELETE key "%s"\n", key); + + e.key = (char *)key; + + if ((idx = hsearch_r(e, FIND, &ep, htab)) == 0) { + __set_errno(ESRCH); + return 0; /* not found */ + } + + /* free used ENTRY */ + debug("hdelete: DELETING key "%s"\n", key); + + free(ep->key); + free(ep->data); + htab->table[idx].used = 0; + + --htab->filled; + + return 1; +} + +/* + * hexport() + */ + +/* + * Export the data stored in the hash table in linearized form. + * + * Entries are exported as "name=value" strings, separated by an + * arbitrary (non-NUL, of course) separator character. This allows to + * use this function both when formatting the U-Boot environment for + * external storage (using '\0' as separator), but also when using it + * for the "printenv" command to print all variables, simply by using + * as '\n" as separator. This can also be used for new features like + * exporting the environment data as text file, including the option + * for later re-import. + * + * The entries in the result list will be sorted by ascending key + * values. + * + * If the separator character is different from NUL, then any + * separator characters and backslash characters in the values will + * be escaped by a preceeding backslash in output. This is needed for + * example to enable multi-line values, especially when the output + * shall later be parsed (for example, for re-import). + * + * There are several options how the result buffer is handled: + * + * *resp size + * ----------- + * NULL 0 A string of sufficient length will be allocated. + * NULL >0 A string of the size given will be + * allocated. An error will be returned if the size is + * not sufficient. Any unused bytes in the string will + * be '\0'-padded. + * !NULL 0 The user-supplied buffer will be used. No length + * checking will be performed, i. e. it is assumed that + * the buffer size will always be big enough. DANGEROUS. + * !NULL >0 The user-supplied buffer will be used. An error will + * be returned if the size is not sufficient. Any unused + * bytes in the string will be '\0'-padded. + */ + +ssize_t hexport(const char sep, char **resp, size_t size) +{ + return hexport_r(&htab, sep, resp, size); +} + +static int cmpkey(const void *p1, const void *p2) +{ + ENTRY *e1 = *(ENTRY **) p1; + ENTRY *e2 = *(ENTRY **) p2; + + return (strcmp(e1->key, e2->key)); +} + +ssize_t hexport_r(struct hsearch_data *htab, const char sep, + char **resp, size_t size) +{ + ENTRY *list[htab->size]; + char *res, *p; + size_t totlen; + int i, n; + + /* Test for correct arguments. */ + if ((resp == NULL) || (htab == NULL)) { + __set_errno(EINVAL); + return (-1); + } + + debug("EXPORT table = %p, htab.size = %d, htab.filled = %d, size = %d\n", + htab, htab->size, htab->filled, size); + /* + * Pass 1: + * search used entries, + * save addresses and compute total length + */ + for (i = 1, n = 0, totlen = 0; i <= htab->size; ++i) { + + if (htab->table[i].used) { + ENTRY *ep = &htab->table[i].entry; + + list[n++] = ep; + + totlen += strlen(ep->key) + 2; + + if (sep == '\0') { + totlen += strlen(ep->data); + } else { /* check if escapes are needed */ + char *s = ep->data; + + while (*s) { + ++totlen; + /* add room for needed escape chars */ + if ((*s == sep) || (*s == '\')) + ++totlen; + ++s; + } + } + totlen += 2; /* for '=' and 'sep' char */ + } + } + +#ifdef DEBUG + /* Pass 1a: print unsorted list */ + printf("Unsorted: n=%d\n", n); + for (i = 0; i < n; ++i) { + printf("\t%3d: %p ==> %-10s => %s\n", + i, list[i], list[i]->key, list[i]->data); + } +#endif + + /* Sort list by keys */ + qsort(list, n, sizeof(ENTRY *), cmpkey); + + /* Check if the user supplied buffer size is sufficient */ + if (size) { + if (size < totlen + 1) { /* provided buffer too small */ + debug("### buffer too small: %d, but need %d\n", + size, totlen + 1); + __set_errno(ENOMEM); + return (-1); + } + } else { + size = totlen + 1; + } + + /* Check if the user provided a buffer */ + if (*resp) { + /* yes; clear it */ + res = *resp; + memset(res, '\0', size); + } else { + /* no, allocate and clear one */ + *resp = res = calloc(1, size); + if (res == NULL) { + __set_errno(ENOMEM); + return (-1); + } + } + /* + * Pass 2: + * export sorted list of result data + */ + for (i = 0, p = res; i < n; ++i) { + char *s; + + s = list[i]->key; + while (*s) + *p++ = *s++; + *p++ = '='; + + s = list[i]->data; + + while (*s) { + if ((*s == sep) || (*s == '\')) + *p++ = '\'; /* escape */ + *p++ = *s++; + } + *p++ = sep; + } + *p = '\0'; /* terminate result */ + + return size; +} + + +/* + * himport() + */ + +/* + * Import linearized data into hash table. + * + * This is the inverse function to hexport(): it takes a linear list + * of "name=value" pairs and creates hash table entries from it. + * + * Entries without "value", i. e. consisting of only "name" or + * "name=", will cause this entry to be deleted from the hash table. + * + * The "flag" argument can be used to control the behaviour: when the + * H_NOCLEAR bit is set, then an existing hash table will kept, i. e. + * new data will be added to an existing hash table; otherwise, old + * data will be discarded and a new hash table will be created. + * + * The separator character for the "name=value" pairs can be selected, + * so we both support importing from externally stored environment + * data (separated by NUL characters) and from plain text files + * (entries separated by newline characters). + * + * To allow for nicely formatted text input, leading white space + * (sequences of SPACE and TAB chars) is ignored, and entries starting + * (after removal of any leading white space) with a '#' character are + * considered comments and ignored. + * + * [NOTE: this means that a variable name cannot start with a '#' + * character.] + * + * When using a non-NUL separator character, backslash is used as + * escape character in the value part, allowing for example for + * multi-line values. + * + * In theory, arbitrary separator characters can be used, but only + * '\0' and '\n' have really been tested. + */ + +int himport(const char *env, size_t size, const char sep, int flag) +{ + return himport_r(&htab, env, size, sep, flag); +} + +int himport_r(struct hsearch_data *htab, + const char *env, size_t size, const char sep, int flag) +{ + char *data, *sp, *dp, *name, *value; + + /* Test for correct arguments. */ + if (htab == NULL) { + __set_errno(EINVAL); + return 0; + } + + /* we allocate new space to make sure we can write to the array */ + if ((data = malloc(size)) == NULL) { + debug("himport_r: can't malloc %d bytes\n", size); + __set_errno(ENOMEM); + return 0; + } + memcpy(data, env, size); + dp = data; + + if ((flag & H_NOCLEAR) == 0) { + /* Destroy old hash table if one exists */ + debug("Destroy Hash Table: %p table = %p\n", htab, + htab->table); + if (htab->table) + hdestroy_r(htab); + } + + /* + * Create new hash table (if needed). The computation of the hash + * table size is based on heuristics: in a sample of some 70+ + * existing systems we found an average size of 39+ bytes per entry + * in the environment (for the whole key=value pair). Assuming a + * size of 7 per entry (= safety factor of >5) should provide enough + * safety margin for any existing environment definitons and still + * allow for more than enough dynamic additions. Note that the + * "size" argument is supposed to give the maximum enviroment size + * (CONFIG_ENV_SIZE). + */ + + if (!htab->table) { + int nent = size / 7; + + debug("Create Hash Table: N=%d\n", nent); + + if (hcreate_r(nent, htab) == 0) { + free(data); + return 0; + } + } + + /* Parse environment; allow for '\0' and 'sep' as separators */ + do { + ENTRY e, *rv; + + /* skip leading white space */ + while ((*dp == ' ') || (*dp == '\t')) + ++dp; + + /* skip comment lines */ + if (*dp == '#') { + while (*dp && (*dp != sep)) + ++dp; + ++dp; + continue; + } + + /* parse name */ + for (name = dp; *dp != '=' && *dp && *dp != sep; ++dp) + ; + + /* deal with "name" and "name=" entries (delete var) */ + if (*dp == '\0' || *(dp + 1) == '\0' || + *dp == sep || *(dp + 1) == sep) { + if (*dp == '=') + *dp++ = '\0'; + *dp++ = '\0'; /* terminate name */ + + debug("DELETE CANDIDATE: "%s"\n", name); + + if (hdelete_r(name, htab) == 0) + debug("DELETE ERROR ##############################\n"); + + continue; + } + *dp++ = '\0'; /* terminate name */ + + /* parse value; deal with escapes */ + for (value = sp = dp; *dp && (*dp != sep); ++dp) { + if ((*dp == '\') && *(dp + 1)) + ++dp; + *sp++ = *dp; + } + *sp++ = '\0'; /* terminate value */ + ++dp; + + /* enter into hash table */ + e.key = name; + e.data = value; + + hsearch_r(e, ENTER, &rv, htab); + if (rv == NULL) { + printf("himport_r: can't insert "%s=%s" into hash table\n", name, value); + return 0; + } + + debug("INSERT: %p ==> name="%s" value="%s"\n", rv, name, + value); + debug(" table = %p, size = %d, filled = %d\n", htab, + htab->size, htab->filled); + } while ((dp < data + size) && *dp); /* size check needed for text */ + /* without '\0' termination */ + free(data); + + return 1; /* everything OK */ +}

Dear Wolfgang Denk,
In message 1279395948-25864-4-git-send-email-wd@denx.de you wrote:
This implementation is based on code from uClibc-0.9.30.3 but was modified and extended for use within U-Boot.
Major modifications and extensions:
- hsearch() [modified / extended]:
- While the standard version does not make any assumptions about the type of the stored data objects at all, this implementation works with NUL terminated strings only.
- Instead of storing just pointers to the original objects, we create local copies so the caller does not need to care about the data any more.
- The standard implementation does not provide a way to update an existing entry. This version will create a new entry or update an existing one when both "action == ENTER" and "item.data != NULL".
- hsearch_r(): Instead of returning 1 on success, we return the index into the internal hash table, which is also guaranteed to be positive. This allows us direct access to the found hash table slot for example for functions like hdelete().
- hdelete() [added]:
- The standard implementation of hsearch(3) does not provide any way to delete any entries from the hash table. We extend the code to do that.
- hexport() [added]:
- Export the data stored in the hash table in linearized form: Entries are exported as "name=value" strings, separated by an arbitrary (non-NUL, of course) separator character. This allows to use this function both when formatting the U-Boot environment for external storage (using '\0' as separator), but also when using it for the "printenv" command to print all variables, simply by using as '\n" as separator. This can also be used for new features like exporting the environment data as text file, including the option for later re-import.
- The entries in the result list will be sorted by ascending key values.
- himport() [added]:
- Import linearized data into hash table. This is the inverse function to hexport(): it takes a linear list of "name=value" pairs and creates hash table entries from it.
- Entries without "value", i. e. consisting of only "name" or "name=", will cause this entry to be deleted from the hash table.
- The "flag" argument can be used to control the behaviour: when the H_NOCLEAR bit is set, then an existing hash table will kept, i. e. new data will be added to an existing hash table; otherwise, old data will be discarded and a new hash table will be created.
- The separator character for the "name=value" pairs can be selected, so we both support importing from externally stored environment data (separated by NUL characters) and from plain text files (entries separated by newline characters).
- To allow for nicely formatted text input, leading white space (sequences of SPACE and TAB chars) is ignored, and entries starting (after removal of any leading white space) with a '#' character are considered comments and ignored.
- NOTE: this means that a variable name cannot start with a '#' character.
- When using a non-NUL separator character, backslash is used as escape character in the value part, allowing for example fo multi-line values.
- In theory, arbitrary separator characters can be used, but only '\0' and '\n' have really been tested.
Signed-off-by: Wolfgang Denk wd@denx.de
include/search.h | 106 ++++++++ lib/Makefile | 1 + lib/hashtable.c | 721 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 828 insertions(+), 0 deletions(-) create mode 100644 include/search.h create mode 100644 lib/hashtable.c
Applied to "next" branch.
Best regards,
Wolfgang Denk

On Saturday, July 17, 2010 15:45:46 Wolfgang Denk wrote:
This implementation is based on code from uClibc-0.9.30.3 but was modified and extended for use within U-Boot.
unless i'm missing something, the non-reentrant versions operate on a single shared hash table. so while this works today because there is only one consumer (the env code), wont this cause problems as soon as someone else tries to use the non-reentrant hash table code ? as such, wouldnt it make sense to punt all of the non-reentrant versions and thus force everyone to maintain their own "struct hsearch_data" instance ? -mike

Dear Mike Frysinger,
In message 201012080444.30675.vapier@gentoo.org you wrote:
unless i'm missing something, the non-reentrant versions operate on a single shared hash table. so while this works today because there is only one
Correct.
consumer (the env code), wont this cause problems as soon as someone else tries to use the non-reentrant hash table code ? as such, wouldnt it make sense to punt all of the non-reentrant versions and thus force everyone to maintain their own "struct hsearch_data" instance ?
This could be done, but I don't see an immediate need.
Best regards,
Wolfgang Denk

On Wednesday, December 08, 2010 05:02:38 Wolfgang Denk wrote:
Mike Frysinger wrote:
unless i'm missing something, the non-reentrant versions operate on a single shared hash table. so while this works today because there is only one
Correct.
consumer (the env code), wont this cause problems as soon as someone else tries to use the non-reentrant hash table code ? as such, wouldnt it make sense to punt all of the non-reentrant versions and thus force everyone to maintain their own "struct hsearch_data" instance ?
This could be done, but I don't see an immediate need.
off the top of my head: - it isnt obvious to people today that this issue exists, so anyone attempting to use the fun new hashtable code could conceivably use it without running into a problem (as long as their keys dont happen to collide with env names). so people could possibly waste quite a bit of their time trying to track down bugs that only crop up when certain env vars are set. - i imagine a bit of space is being wasted here. - i re-implemented env-autocomplete, but i need access to the hashtable structure where everything is stored. -mike

This (undocumented) concept was only in use for the MVSMR and davinci_schmoogie Sergey Kubushyn ksi@koi8.net boards. Drop it for now. If really needed, it should be reimplemented later in the context of the new environment command set.
Signed-off-by: Wolfgang Denk wd@denx.de Cc: Andre Schwarz andre.schwarz@matrix-vision.de Cc: Sergey Kubushyn ksi@koi8.net --- common/cmd_nvedit.c | 13 ------------- common/exports.c | 3 --- include/_exports.h | 1 - include/common.h | 3 --- include/configs/MVSMR.h | 1 - include/configs/davinci_schmoogie.h | 1 - include/exports.h | 3 --- 7 files changed, 0 insertions(+), 25 deletions(-)
diff --git a/common/cmd_nvedit.c b/common/cmd_nvedit.c index 13325bc..8c86f15 100644 --- a/common/cmd_nvedit.c +++ b/common/cmd_nvedit.c @@ -247,12 +247,7 @@ int _do_setenv (int flag, int argc, char * const argv[]) * ver is readonly. */ if ( -#ifdef CONFIG_HAS_UID - /* Allow serial# forced overwrite with 0xdeaf4add flag */ - ((strcmp (name, "serial#") == 0) && (flag != 0xdeaf4add)) || -#else (strcmp (name, "serial#") == 0) || -#endif ((strcmp (name, "ethaddr") == 0) #if defined(CONFIG_OVERWRITE_ETHADDR_ONCE) && defined(CONFIG_ETHADDR) && (strcmp ((char *)env_get_addr(oldval),MK_STR(CONFIG_ETHADDR)) != 0) @@ -397,14 +392,6 @@ int setenv (char *varname, char *varvalue) return _do_setenv (0, 3, argv); }
-#ifdef CONFIG_HAS_UID -void forceenv (char *varname, char *varvalue) -{ - char * const argv[4] = { "forceenv", varname, varvalue, NULL }; - _do_setenv (0xdeaf4add, 3, argv); -} -#endif - int do_setenv (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { if (argc < 2) { diff --git a/common/exports.c b/common/exports.c index cefe6f6..bde52a6 100644 --- a/common/exports.c +++ b/common/exports.c @@ -35,9 +35,6 @@ unsigned long get_version(void) # define spi_release_bus dummy # define spi_xfer dummy #endif -#ifndef CONFIG_HAS_UID -# define forceenv dummy -#endif
void jumptable_init(void) { diff --git a/include/_exports.h b/include/_exports.h index f3df568..d89b65b 100644 --- a/include/_exports.h +++ b/include/_exports.h @@ -18,7 +18,6 @@ EXPORT_FUNC(vprintf) EXPORT_FUNC(do_reset) EXPORT_FUNC(getenv) EXPORT_FUNC(setenv) -EXPORT_FUNC(forceenv) EXPORT_FUNC(simple_strtoul) EXPORT_FUNC(simple_strtol) EXPORT_FUNC(strcmp) diff --git a/include/common.h b/include/common.h index e4b4ec0..5eef9c7 100644 --- a/include/common.h +++ b/include/common.h @@ -262,9 +262,6 @@ int saveenv (void); int inline setenv (char *, char *); #else int setenv (char *, char *); -#ifdef CONFIG_HAS_UID -void forceenv (char *, char *); -#endif #endif /* CONFIG_PPC */ #ifdef CONFIG_ARM # include <asm/mach-types.h> diff --git a/include/configs/MVSMR.h b/include/configs/MVSMR.h index 6492068..000c4c6 100644 --- a/include/configs/MVSMR.h +++ b/include/configs/MVSMR.h @@ -185,7 +185,6 @@ */ #define CONFIG_ENV_IS_IN_FLASH #undef CONFIG_SYS_FLASH_PROTECTION -#define CONFIG_HAS_UID #define CONFIG_OVERWRITE_ETHADDR_ONCE
#define CONFIG_ENV_OFFSET 0x8000 diff --git a/include/configs/davinci_schmoogie.h b/include/configs/davinci_schmoogie.h index 875dda4..04cdc21 100644 --- a/include/configs/davinci_schmoogie.h +++ b/include/configs/davinci_schmoogie.h @@ -99,7 +99,6 @@ /*=====================*/ #define CONFIG_RTC_DS1307 /* RTC chip on SCHMOOGIE */ #define CONFIG_SYS_I2C_RTC_ADDR 0x6f /* RTC chip I2C address */ -#define CONFIG_HAS_UID #define CONFIG_UID_DS28CM00 /* Unique ID on SCHMOOGIE */ #define CONFIG_SYS_UID_ADDR 0x50 /* UID chip I2C address */ /*==============================*/ diff --git a/include/exports.h b/include/exports.h index 1d79a31..7404a7c 100644 --- a/include/exports.h +++ b/include/exports.h @@ -26,9 +26,6 @@ int setenv (char *varname, char *varvalue); long simple_strtol(const char *cp,char **endp,unsigned int base); int strcmp(const char * cs,const char * ct); int ustrtoul(const char *cp, char **endp, unsigned int base); -#ifdef CONFIG_HAS_UID -void forceenv (char *varname, char *varvalue); -#endif #if defined(CONFIG_CMD_I2C) int i2c_write (uchar, uint, int , uchar* , int); int i2c_read (uchar, uint, int , uchar* , int);

On Sat, 17 Jul 2010, Wolfgang Denk wrote:
Ack by: Sergey Kubushyn ksi@koi8.net ---
This (undocumented) concept was only in use for the MVSMR and davinci_schmoogie Sergey Kubushyn ksi@koi8.net boards. Drop it for now. If really needed, it should be reimplemented later in the context of the new environment command set.
Signed-off-by: Wolfgang Denk wd@denx.de Cc: Andre Schwarz andre.schwarz@matrix-vision.de Cc: Sergey Kubushyn ksi@koi8.net
common/cmd_nvedit.c | 13 ------------- common/exports.c | 3 --- include/_exports.h | 1 - include/common.h | 3 --- include/configs/MVSMR.h | 1 - include/configs/davinci_schmoogie.h | 1 - include/exports.h | 3 --- 7 files changed, 0 insertions(+), 25 deletions(-)
diff --git a/common/cmd_nvedit.c b/common/cmd_nvedit.c index 13325bc..8c86f15 100644 --- a/common/cmd_nvedit.c +++ b/common/cmd_nvedit.c @@ -247,12 +247,7 @@ int _do_setenv (int flag, int argc, char * const argv[]) * ver is readonly. */ if ( -#ifdef CONFIG_HAS_UID
/* Allow serial# forced overwrite with 0xdeaf4add flag */
((strcmp (name, "serial#") == 0) && (flag != 0xdeaf4add)) ||
-#else (strcmp (name, "serial#") == 0) || -#endif ((strcmp (name, "ethaddr") == 0) #if defined(CONFIG_OVERWRITE_ETHADDR_ONCE) && defined(CONFIG_ETHADDR) && (strcmp ((char *)env_get_addr(oldval),MK_STR(CONFIG_ETHADDR)) != 0) @@ -397,14 +392,6 @@ int setenv (char *varname, char *varvalue) return _do_setenv (0, 3, argv); }
-#ifdef CONFIG_HAS_UID -void forceenv (char *varname, char *varvalue) -{
- char * const argv[4] = { "forceenv", varname, varvalue, NULL };
- _do_setenv (0xdeaf4add, 3, argv);
-} -#endif
int do_setenv (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { if (argc < 2) { diff --git a/common/exports.c b/common/exports.c index cefe6f6..bde52a6 100644 --- a/common/exports.c +++ b/common/exports.c @@ -35,9 +35,6 @@ unsigned long get_version(void) # define spi_release_bus dummy # define spi_xfer dummy #endif -#ifndef CONFIG_HAS_UID -# define forceenv dummy -#endif
void jumptable_init(void) { diff --git a/include/_exports.h b/include/_exports.h index f3df568..d89b65b 100644 --- a/include/_exports.h +++ b/include/_exports.h @@ -18,7 +18,6 @@ EXPORT_FUNC(vprintf) EXPORT_FUNC(do_reset) EXPORT_FUNC(getenv) EXPORT_FUNC(setenv) -EXPORT_FUNC(forceenv) EXPORT_FUNC(simple_strtoul) EXPORT_FUNC(simple_strtol) EXPORT_FUNC(strcmp) diff --git a/include/common.h b/include/common.h index e4b4ec0..5eef9c7 100644 --- a/include/common.h +++ b/include/common.h @@ -262,9 +262,6 @@ int saveenv (void); int inline setenv (char *, char *); #else int setenv (char *, char *); -#ifdef CONFIG_HAS_UID -void forceenv (char *, char *); -#endif #endif /* CONFIG_PPC */ #ifdef CONFIG_ARM # include <asm/mach-types.h> diff --git a/include/configs/MVSMR.h b/include/configs/MVSMR.h index 6492068..000c4c6 100644 --- a/include/configs/MVSMR.h +++ b/include/configs/MVSMR.h @@ -185,7 +185,6 @@ */ #define CONFIG_ENV_IS_IN_FLASH #undef CONFIG_SYS_FLASH_PROTECTION -#define CONFIG_HAS_UID #define CONFIG_OVERWRITE_ETHADDR_ONCE
#define CONFIG_ENV_OFFSET 0x8000 diff --git a/include/configs/davinci_schmoogie.h b/include/configs/davinci_schmoogie.h index 875dda4..04cdc21 100644 --- a/include/configs/davinci_schmoogie.h +++ b/include/configs/davinci_schmoogie.h @@ -99,7 +99,6 @@ /*=====================*/ #define CONFIG_RTC_DS1307 /* RTC chip on SCHMOOGIE */ #define CONFIG_SYS_I2C_RTC_ADDR 0x6f /* RTC chip I2C address */ -#define CONFIG_HAS_UID #define CONFIG_UID_DS28CM00 /* Unique ID on SCHMOOGIE */ #define CONFIG_SYS_UID_ADDR 0x50 /* UID chip I2C address */ /*==============================*/ diff --git a/include/exports.h b/include/exports.h index 1d79a31..7404a7c 100644 --- a/include/exports.h +++ b/include/exports.h @@ -26,9 +26,6 @@ int setenv (char *varname, char *varvalue); long simple_strtol(const char *cp,char **endp,unsigned int base); int strcmp(const char * cs,const char * ct); int ustrtoul(const char *cp, char **endp, unsigned int base); -#ifdef CONFIG_HAS_UID -void forceenv (char *varname, char *varvalue); -#endif #if defined(CONFIG_CMD_I2C) int i2c_write (uchar, uint, int , uchar* , int); int i2c_read (uchar, uint, int , uchar* , int); -- 1.7.1.1
--- ****************************************************************** * KSI@home KOI8 Net < > The impossible we do immediately. * * Las Vegas NV, USA < > Miracles require 24-hour notice. * ******************************************************************

Dear Wolfgang Denk,
In message 1279395948-25864-5-git-send-email-wd@denx.de you wrote:
This (undocumented) concept was only in use for the MVSMR and davinci_schmoogie Sergey Kubushyn ksi@koi8.net boards. Drop it for now. If really needed, it should be reimplemented later in the context of the new environment command set.
Signed-off-by: Wolfgang Denk wd@denx.de Cc: Andre Schwarz andre.schwarz@matrix-vision.de Cc: Sergey Kubushyn ksi@koi8.net
common/cmd_nvedit.c | 13 ------------- common/exports.c | 3 --- include/_exports.h | 1 - include/common.h | 3 --- include/configs/MVSMR.h | 1 - include/configs/davinci_schmoogie.h | 1 - include/exports.h | 3 --- 7 files changed, 0 insertions(+), 25 deletions(-)
Applied to "next" branch.
Best regards,
Wolfgang Denk

Motivation:
* Old environment code used a pessimizing implementation: - variable lookup used linear search => slow - changed/added variables were added at the end, i. e. most frequently used variables had the slowest access times => slow - each setenv() would calculate the CRC32 checksum over the whole environment block => slow * "redundant" envrionment was locked down to two copies * No easy way to implement features like "reset to factory defaults", or to select one out of several pre-defined (previously saved) sets of environment settings ("profiles") * No easy way to import or export environment settings
======================================================================
API Changes:
- Variable names starting with '#' are no longer allowed
I didn't find any such variable names being used; it is highly recommended to follow standard conventions and start variable names with an alphanumeric character
- "printenv" will now print a backslash at the end of all but the last lines of a multi-line variable value.
Multi-line variables have never been formally defined, allthough there is no reason not to use them. Now we define rules how to deal with them, allowing for import and export.
- Function forceenv() and the related code in saveenv() was removed. At the moment this is causing build problems for the only user of this code (schmoogie - which has no entry in MAINTAINERS); may be fixed later by implementing the "env set -f" feature.
Inconsistencies:
- "printenv" will '\'-escape the '\n' in multi-line variables, while "printenv var" will not do that.
======================================================================
Advantages:
- "printenv" output much better readable (sorted) - faster! - extendable (additional variable properties can be added) - new, powerful features like "factory reset" or easy switching between several different environment settings ("profiles")
Disadvantages:
- Image size grows by typically 5...7 KiB (might shrink a bit again on systems with redundant environment with a following patch series)
======================================================================
Implemented:
- env command with subcommands:
- env print [arg ...]
same as "printenv": print environment
- env set [-f] name [arg ...]
same as "setenv": set (and delete) environment variables
["-f" - force setting even for read-only variables - not implemented yet.]
- end delete [-f] name
not implemented yet
["-f" - force delete even for read-only variables]
- env save
same as "saveenv": save environment
- env export [-t | -b | -c] addr [size]
export internal representation (hash table) in formats usable for persistent storage or processing:
-t: export as text format; if size is given, data will be padded with '\0' bytes; if not, one terminating '\0' will be added (which is included in the "filesize" setting so you can for exmple copy this to flash and keep the termination). -b: export as binary format (name=value pairs separated by '\0', list end marked by double "\0\0") -c: export as checksum protected environment format as used for example by "saveenv" command addr: memory address where environment gets stored size: size of output buffer
With "-c" and size is NOT given, then the export command will format the data as currently used for the persistent storage, i. e. it will use CONFIG_ENV_SECT_SIZE as output block size and prepend a valid CRC32 checksum and, in case of resundant environment, a "current" redundancy flag. If size is given, this value will be used instead of CONFIG_ENV_SECT_SIZE; again, CRC32 checksum and redundancy flag will be inserted.
With "-b" and "-t", always only the real data (including a terminating '\0' byte) will be written; here the optional size argument will be used to make sure not to overflow the user provided buffer; the command will abort if the size is not sufficient. Any remainign space will be '\0' padded.
On successful return, the variable "filesize" will be set. Note that filesize includes the trailing/terminating '\0' byte(s).
Usage szenario: create a text snapshot/backup of the current settings:
=> env export -t 100000 => era ${backup_addr} +${filesize} => cp.b 100000 ${backup_addr} ${filesize}
Re-import this snapshot, deleting all other settings:
=> env import -d -t ${backup_addr}
- env import [-d] [-t | -b | -c] addr [size]
import external format (text or binary) into hash table, optionally deleting existing values:
-d: delete existing environment before importing; otherwise overwrite / append to existion definitions -t: assume text format; either "size" must be given or the text data must be '\0' terminated -b: assume binary format ('\0' separated, "\0\0" terminated) -c: assume checksum protected environment format addr: memory address to read from size: length of input data; if missing, proper '\0' termination is mandatory
- env default -f
reset default environment: drop all environment settings and load default environment
- env ask name [message] [size]
same as "askenv": ask for environment variable
- env edit name
same as "editenv": edit environment variable
- env run
same as "run": run commands in an environment variable
======================================================================
TODO:
- drop default env as implemented now; provide a text file based initialization instead (eventually using several text files to incrementally build it from common blocks) and a tool to convert it into a binary blob / object file.
- It would be nice if we could add wildcard support for environment variables; this is needed for variable name auto-completion, but it would also be nice to be able to say "printenv ip*" or "printenv *addr*"
- Some boards don't link any more due to the grown code size: AR405, CANBT, DU405, PMC440, canyonlands, sc3, sequoia, socrates.
=> cc: Matthias Fuchs matthias.fuchs@esd-electronics.com, Stefan Roese sr@denx.de, Heiko Schocher hs@denx.de
- Dropping forceenv() causes build problems on schmoogie
=> cc: Sergey Kubushyn ksi@koi8.net
- Build tested on PPC and ARM only; runtime tested with NOR and NAND flash only => needs testing!!
Signed-off-by: Wolfgang Denk wd@denx.de Cc: Matthias Fuchs matthias.fuchs@esd-electronics.com, Cc: Stefan Roese sr@denx.de, Cc: Heiko Schocher hs@denx.de Cc: Sergey Kubushyn ksi@koi8.net --- README | 8 + board/zeus/zeus.c | 25 +-- common/cmd_nvedit.c | 655 +++++++++++++++++++++++++++++++++--------------- common/command.c | 3 +- common/dlmalloc.c | 25 +- common/env_common.c | 125 +++++----- common/env_dataflash.c | 114 ++++++---- common/env_eeprom.c | 85 ++++--- common/env_flash.c | 245 ++++++++++--------- common/env_mgdisk.c | 33 +-- common/env_nand.c | 188 ++++++++------ common/env_nowhere.c | 11 +- common/env_nvram.c | 46 +++- common/env_onenand.c | 65 +++--- common/env_sf.c | 132 ++++++----- common/exports.c | 1 - include/environment.h | 5 +- lib/hashtable.c | 32 ++- 18 files changed, 1094 insertions(+), 704 deletions(-)
diff --git a/README b/README index a9c98f2..3a3c631 100644 --- a/README +++ b/README @@ -2354,6 +2354,14 @@ Configuration Settings: on high Ethernet traffic. Defaults to 4 if not defined.
+- CONFIG_ENV_MAX_ENTRIES + + Maximum number of entries in the hash table that is used + internally to store the environment settings. The default + setting is supposed to be generous and should work in most + cases. This setting can be used to tune behaviour; see + lib/hashtable.c for details. + The following definitions that deal with the placement and management of environment data (variable area); in general, we support the following configurations: diff --git a/board/zeus/zeus.c b/board/zeus/zeus.c index e295151..4e6878a 100644 --- a/board/zeus/zeus.c +++ b/board/zeus/zeus.c @@ -222,27 +222,8 @@ static int restore_default(void) char *buf_save; u32 crc;
- /* - * Unprotect and erase environment area - */ - flash_protect(FLAG_PROTECT_CLEAR, - CONFIG_ENV_ADDR_REDUND, - CONFIG_ENV_ADDR_REDUND + 2*CONFIG_ENV_SECT_SIZE - 1, - &flash_info[0]); + set_default_env("");
- flash_sect_erase(CONFIG_ENV_ADDR_REDUND, - CONFIG_ENV_ADDR_REDUND + 2*CONFIG_ENV_SECT_SIZE - 1); - - /* - * Now restore default environment from U-Boot image - * -> ipaddr, serverip... - */ - memset(env_ptr, 0, sizeof(env_t)); - memcpy(env_ptr->data, default_environment, ENV_SIZE); -#ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT - env_ptr->flags = 0xFF; -#endif - env_crc_update(); gd->env_valid = 1;
/* @@ -251,6 +232,10 @@ static int restore_default(void) * -> ethaddr, eth1addr, serial# */ buf = buf_save = malloc(FACTORY_RESET_ENV_SIZE); + if (buf == NULL) { + printf("ERROR: malloc() failed\n"); + return -1; + } if (eeprom_read(FACTORY_RESET_I2C_EEPROM, FACTORY_RESET_ENV_OFFS, (u8 *)buf, FACTORY_RESET_ENV_SIZE)) { puts("\nError reading EEPROM!\n"); diff --git a/common/cmd_nvedit.c b/common/cmd_nvedit.c index 8c86f15..976a30b 100644 --- a/common/cmd_nvedit.c +++ b/common/cmd_nvedit.c @@ -1,5 +1,5 @@ /* - * (C) Copyright 2000-2002 + * (C) Copyright 2000-2010 * Wolfgang Denk, DENX Software Engineering, wd@denx.de. * * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com> @@ -24,27 +24,26 @@ * MA 02111-1307 USA */
-/************************************************************************** - * +/* * Support for persistent environment data * - * The "environment" is stored as a list of '\0' terminated - * "name=value" strings. The end of the list is marked by a double - * '\0'. New entries are always added at the end. Deleting an entry - * shifts the remaining entries to the front. Replacing an entry is a - * combination of deleting the old value and adding the new one. - * - * The environment is preceeded by a 32 bit CRC over the data part. + * The "environment" is stored on external storage as a list of '\0' + * terminated "name=value" strings. The end of the list is marked by + * a double '\0'. The environment is preceeded by a 32 bit CRC over + * the data part and, in case of redundant environment, a byte of + * flags. * - ************************************************************************** + * This linearized representation will also be used before + * relocation, i. e. as long as we don't have a full C runtime + * environment. After that, we use a hash table. */
#include <common.h> #include <command.h> #include <environment.h> -#if defined(CONFIG_CMD_EDITENV) +#include <search.h> +#include <errno.h> #include <malloc.h> -#endif #include <watchdog.h> #include <serial.h> #include <linux/stddef.h> @@ -71,8 +70,10 @@ SPI_FLASH|MG_DISK|NVRAM|NOWHERE} #define XMK_STR(x) #x #define MK_STR(x) XMK_STR(x)
-/************************************************************************ -************************************************************************/ +/* + * Maximum expected input data size for import command + */ +#define MAX_ENV_SIZE (1 << 20) /* 1 MiB */
/* * Table with supported baudrates (defined in config_xyz.h) @@ -81,7 +82,7 @@ static const unsigned long baudrate_table[] = CONFIG_SYS_BAUDRATE_TABLE; #define N_BAUDRATES (sizeof(baudrate_table) / sizeof(baudrate_table[0]))
/* - * This variable is incremented on each do_setenv (), so it can + * This variable is incremented on each do_env_set(), so it can * be used via get_env_id() as an indication, if the environment * has changed or not. So it is possible to reread an environment * variable only if the environment was changed ... done so for @@ -93,61 +94,51 @@ int get_env_id (void) { return env_id; } -/************************************************************************ - * Command interface: print one or all environment variables - */
/* - * state 0: finish printing this string and return (matched!) - * state 1: no matching to be done; print everything - * state 2: continue searching for matched name + * Command interface: print one or all environment variables + * + * Returns 0 in case of error, or length of printed string */ -static int printenv(char *name, int state) +static int env_print(char *name) { - int i, j; - char c, buf[17]; - - i = 0; - buf[16] = '\0'; - - while (state && env_get_char(i) != '\0') { - if (state == 2 && envmatch((uchar *)name, i) >= 0) - state = 0; - - j = 0; - do { - buf[j++] = c = env_get_char(i++); - if (j == sizeof(buf) - 1) { - if (state <= 1) - puts(buf); - j = 0; - } - } while (c != '\0'); + char *res = NULL; + size_t len; + + if (name) { /* print a single name */ + ENTRY e, *ep; + + e.key = name; + e.data = NULL; + ep = hsearch (e, FIND); + if (ep == NULL) + return 0; + len = printf ("%s=%s\n", ep->key, ep->data); + return len; + }
- if (state <= 1) { - if (j) - puts(buf); - putc('\n'); - } + /* print whole list */ + len = hexport('\n', &res, 0);
- if (ctrlc()) - return -1; + if (len > 0) { + puts(res); + free(res); + return len; }
- if (state == 0) - i = 0; - return i; + /* should never happen */ + return 0; }
-int do_printenv (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +int do_env_print (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { int i; int rcode = 0;
if (argc == 1) { /* print all env vars */ - rcode = printenv(NULL, 1); - if (rcode < 0) + rcode = env_print(NULL); + if (!rcode) return 1; printf("\nEnvironment size: %d/%ld bytes\n", rcode, (ulong)ENV_SIZE); @@ -156,9 +147,9 @@ int do_printenv (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
/* print selected env vars */ for (i = 1; i < argc; ++i) { - char *name = argv[i]; - if (printenv(name, 2)) { - printf("## Error: "%s" not defined\n", name); + int rc = env_print(argv[i]); + if (!rc) { + printf("## Error: "%s" not defined\n", argv[i]); ++rcode; } } @@ -166,25 +157,18 @@ int do_printenv (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) return rcode; }
-/************************************************************************ +/* * Set a new environment variable, * or replace or delete an existing one. - * - * This function will ONLY work with a in-RAM copy of the environment */
-int _do_setenv (int flag, int argc, char * const argv[]) +int _do_env_set (int flag, int argc, char * const argv[]) { - int i, len, oldval; + bd_t *bd = gd->bd; + int i, len; int console = -1; - uchar *env, *nxt = NULL; - char *name; - bd_t *bd = gd->bd; - - uchar *env_data = env_get_addr(0); - - if (!env_data) /* need copy in RAM */ - return 1; + char *name, *value, *s; + ENTRY e, *ep;
name = argv[1];
@@ -197,13 +181,9 @@ int _do_setenv (int flag, int argc, char * const argv[]) /* * search if variable with this name already exists */ - oldval = -1; - for (env=env_data; *env; env=nxt+1) { - for (nxt=env; *nxt; ++nxt) - ; - if ((oldval = envmatch((uchar *)name, env-env_data)) >= 0) - break; - } + e.key = name; + e.data = NULL; + ep = hsearch (e, FIND);
/* Check for console redirection */ if (strcmp(name,"stdin") == 0) { @@ -237,31 +217,25 @@ int _do_setenv (int flag, int argc, char * const argv[]) }
/* - * Delete any existing definition + * Some variables like "ethaddr" and "serial#" can be set only + * once and cannot be deleted; also, "ver" is readonly. */ - if (oldval >= 0) { + if (ep) { /* variable exists */ #ifndef CONFIG_ENV_OVERWRITE - - /* - * Ethernet Address and serial# can be set only once, - * ver is readonly. - */ - if ( - (strcmp (name, "serial#") == 0) || + if ((strcmp (name, "serial#") == 0) || ((strcmp (name, "ethaddr") == 0) #if defined(CONFIG_OVERWRITE_ETHADDR_ONCE) && defined(CONFIG_ETHADDR) - && (strcmp ((char *)env_get_addr(oldval),MK_STR(CONFIG_ETHADDR)) != 0) + && (strcmp (ep->data,MK_STR(CONFIG_ETHADDR)) != 0) #endif /* CONFIG_OVERWRITE_ETHADDR_ONCE && CONFIG_ETHADDR */ ) ) { printf ("Can't overwrite "%s"\n", name); return 1; } #endif - /* * Switch to new baudrate if new baudrate is supported */ - if (strcmp(argv[1],"baudrate") == 0) { + if (strcmp(name,"baudrate") == 0) { int baudrate = simple_strtoul(argv[2], NULL, 10); int i; for (i=0; i<N_BAUDRATES; ++i) { @@ -288,75 +262,50 @@ int _do_setenv (int flag, int argc, char * const argv[]) break; } } - - if (*++nxt == '\0') { - if (env > env_data) { - env--; - } else { - *env = '\0'; - } - } else { - for (;;) { - *env = *nxt++; - if ((*env == '\0') && (*nxt == '\0')) - break; - ++env; - } - } - *++env = '\0'; }
/* Delete only ? */ if ((argc < 3) || argv[2] == NULL) { - env_crc_update (); - return 0; + int rc = hdelete(name); + return !rc; }
/* - * Append new definition at the end - */ - for (env=env_data; *env || *(env+1); ++env) - ; - if (env > env_data) - ++env; - /* - * Overflow when: - * "name" + "=" + "val" +"\0\0" > ENV_SIZE - (env-env_data) + * Insert / replace new value */ - len = strlen(name) + 2; - /* add '=' for first arg, ' ' for all others */ - for (i=2; i<argc; ++i) { + for (i=2,len=1; i<argc; ++i) { len += strlen(argv[i]) + 1; } - if (len > (&env_data[ENV_SIZE]-env)) { - printf ("## Error: environment overflow, "%s" deleted\n", name); + if ((value = malloc(len)) == NULL) { + printf("## Can't malloc %d bytes\n", len); return 1; } - while ((*env = *name++) != '\0') - env++; - for (i=2; i<argc; ++i) { - char *val = argv[i]; + for (i=2,s=value; i<argc; ++i) { + char *v = argv[i];
- *env = (i==2) ? '=' : ' '; - while ((*++env = *val++) != '\0') + while ((*s++ = *v++) != '\0') ; + *s++ = ' '; + } + if (s != value) + *--s = '\0'; + + e.key = name; + e.data = value; + ep = hsearch(e, ENTER); + free(value); + if (!ep) { + printf("## Error inserting "%s" variable, errno=%d\n", + name, errno); + return 1; } - - /* end is marked with double '\0' */ - *++env = '\0'; - - /* Update CRC */ - env_crc_update ();
/* * Some variables should be updated when the corresponding - * entry in the enviornment is changed + * entry in the environment is changed */
- if (strcmp(argv[1],"ethaddr") == 0) - return 0; - - if (strcmp(argv[1],"ipaddr") == 0) { + if (strcmp(name,"ipaddr") == 0) { char *s = argv[2]; /* always use only one arg */ char *e; unsigned long addr; @@ -369,13 +318,12 @@ int _do_setenv (int flag, int argc, char * const argv[]) } bd->bi_ip_addr = htonl(addr); return 0; - } - if (strcmp(argv[1],"loadaddr") == 0) { + } else if (strcmp(argv[1],"loadaddr") == 0) { load_addr = simple_strtoul(argv[2], NULL, 16); return 0; } #if defined(CONFIG_CMD_NET) - if (strcmp(argv[1],"bootfile") == 0) { + else if (strcmp(argv[1],"bootfile") == 0) { copy_filename (BootFile, argv[2], sizeof(BootFile)); return 0; } @@ -387,32 +335,31 @@ int setenv (char *varname, char *varvalue) { char * const argv[4] = { "setenv", varname, varvalue, NULL }; if ((varvalue == NULL) || (varvalue[0] == '\0')) - return _do_setenv (0, 2, argv); + return _do_env_set(0, 2, argv); else - return _do_setenv (0, 3, argv); + return _do_env_set(0, 3, argv); }
-int do_setenv (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +int do_env_set (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { if (argc < 2) { cmd_usage(cmdtp); return 1; }
- return _do_setenv (flag, argc, argv); + return _do_env_set(flag, argc, argv); }
-/************************************************************************ +/* * Prompt for environment variable */ - #if defined(CONFIG_CMD_ASKENV) -int do_askenv ( cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +int do_env_ask ( cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { extern char console_buffer[CONFIG_SYS_CBSIZE]; char message[CONFIG_SYS_CBSIZE]; int size = CONFIG_SYS_CBSIZE - 1; - int len; + int i, len, pos; char *local_args[4];
local_args[0] = argv[0]; @@ -420,40 +367,31 @@ int do_askenv ( cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) local_args[2] = NULL; local_args[3] = NULL;
- if (argc < 2) { - cmd_usage(cmdtp); - return 1; - } /* Check the syntax */ switch (argc) { case 1: cmd_usage(cmdtp); return 1;
- case 2: /* askenv envname */ - sprintf (message, "Please enter '%s':", argv[1]); + case 2: /* env_ask envname */ + sprintf(message, "Please enter '%s':", argv[1]); break;
- case 3: /* askenv envname size */ - sprintf (message, "Please enter '%s':", argv[1]); - size = simple_strtoul (argv[2], NULL, 10); + case 3: /* env_ask envname size */ + sprintf(message, "Please enter '%s':", argv[1]); + size = simple_strtoul(argv[2], NULL, 10); break;
- default: /* askenv envname message1 ... messagen size */ - { - int i; - int pos = 0; - - for (i = 2; i < argc - 1; i++) { + default: /* env_ask envname message1 ... messagen size */ + for (i=2,pos=0; i < argc - 1; i++) { if (pos) { message[pos++] = ' '; } - strcpy (message+pos, argv[i]); + strcpy(message+pos, argv[i]); pos += strlen(argv[i]); } message[pos] = '\0'; - size = simple_strtoul (argv[argc - 1], NULL, 10); - } + size = simple_strtoul(argv[argc - 1], NULL, 10); break; }
@@ -464,7 +402,7 @@ int do_askenv ( cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) return 1;
/* prompt for input */ - len = readline (message); + len = readline(message);
if (size < len) console_buffer[size] = '\0'; @@ -476,15 +414,15 @@ int do_askenv ( cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) }
/* Continue calling setenv code */ - return _do_setenv (flag, len, local_args); + return _do_env_set(flag, len, local_args); } #endif
-/************************************************************************ +/* * Interactively edit an environment variable */ #if defined(CONFIG_CMD_EDITENV) -int do_editenv(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +int do_env_edit(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { char buffer[CONFIG_SYS_CBSIZE]; char *init_val; @@ -508,34 +446,27 @@ int do_editenv(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) } #endif /* CONFIG_CMD_EDITENV */
-/************************************************************************ +/* * Look up variable from environment, * return address of storage for that variable, * or NULL if not found */ - char *getenv (char *name) { - int i, nxt; + ENTRY e, *ep;
WATCHDOG_RESET();
- for (i=0; env_get_char(i) != '\0'; i=nxt+1) { - int val; + e.key = name; + e.data = NULL; + ep = hsearch (e, FIND);
- for (nxt=i; env_get_char(nxt) != '\0'; ++nxt) { - if (nxt >= CONFIG_ENV_SIZE) { - return (NULL); - } - } - if ((val=envmatch((uchar *)name, i)) < 0) - continue; - return ((char *)env_get_addr(val)); - } - - return (NULL); + return (ep ? ep->data : NULL); }
+/* + * Look up variable from environment for restricted C runtime env. + */ int getenv_r (char *name, char *buf, unsigned len) { int i, nxt; @@ -563,7 +494,7 @@ int getenv_r (char *name, char *buf, unsigned len)
#if defined(CONFIG_CMD_SAVEENV) && !defined(CONFIG_ENV_IS_NOWHERE)
-int do_saveenv (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +int do_env_save (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { extern char * env_name_spec;
@@ -573,7 +504,7 @@ int do_saveenv (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) }
U_BOOT_CMD( - saveenv, 1, 0, do_saveenv, + saveenv, 1, 0, do_env_save, "save environment variables to persistent storage", "" ); @@ -581,7 +512,7 @@ U_BOOT_CMD( #endif
-/************************************************************************ +/* * Match a name / name=value pair * * s1 is either a simple 'name', or a 'name=value' pair. @@ -600,12 +531,343 @@ int envmatch (uchar *s1, int i2) return(-1); }
+static int do_env_default(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) +{ + if ((argc != 2) || (strcmp(argv[1], "-f") != 0)) { + cmd_usage(cmdtp); + return 1; + } + set_default_env("## Resetting to default environment\n"); + return 0; +} + +static int do_env_delete(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) +{ + printf("Not implemented yet\n"); + return 0; +} + +/* + * env export [-t | -b | -c] addr [size] + * -t: export as text format; if size is given, data will be + * padded with '\0' bytes; if not, one terminating '\0' + * will be added (which is included in the "filesize" + * setting so you can for exmple copy this to flash and + * keep the termination). + * -b: export as binary format (name=value pairs separated by + * '\0', list end marked by double "\0\0") + * -c: export as checksum protected environment format as + * used for example by "saveenv" command + * addr: memory address where environment gets stored + * size: size of output buffer + * + * With "-c" and size is NOT given, then the export command will + * format the data as currently used for the persistent storage, + * i. e. it will use CONFIG_ENV_SECT_SIZE as output block size and + * prepend a valid CRC32 checksum and, in case of resundant + * environment, a "current" redundancy flag. If size is given, this + * value will be used instead of CONFIG_ENV_SECT_SIZE; again, CRC32 + * checksum and redundancy flag will be inserted. + * + * With "-b" and "-t", always only the real data (including a + * terminating '\0' byte) will be written; here the optional size + * argument will be used to make sure not to overflow the user + * provided buffer; the command will abort if the size is not + * sufficient. Any remainign space will be '\0' padded. + * + * On successful return, the variable "filesize" will be set. + * Note that filesize includes the trailing/terminating '\0' byte(s). + * + * Usage szenario: create a text snapshot/backup of the current settings: + * + * => env export -t 100000 + * => era ${backup_addr} +${filesize} + * => cp.b 100000 ${backup_addr} ${filesize} + * + * Re-import this snapshot, deleting all other settings: + * + * => env import -d -t ${backup_addr} + */ +static int do_env_export(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + char buf[32]; + char *addr, *cmd, *res; + size_t size; + ssize_t len; + env_t *envp = (env_t *)addr; + char sep = '\n'; + int chk = 0; + int fmt = 0; + + cmd = *argv; + + while (--argc > 0 && **++argv == '-') { + char *arg = *argv; + while (*++arg) { + switch (*arg) { + case 'b': /* raw binary format */ + if (fmt++) + goto sep_err; + sep = '\0'; + break; + case 'c': /* external checksum format */ + if (fmt++) + goto sep_err; + sep = '\0'; + chk = 1; + break; + case 't': /* text format */ + if (fmt++) + goto sep_err; + sep = '\n'; + break; + default: + cmd_usage(cmdtp); + return 1; + } + } + }
-/**************************************************/ + if (argc < 1) { + cmd_usage(cmdtp); + return 1; + } + + addr = (char *)simple_strtoul(argv[0], NULL, 16); + + if (argc == 2) { + size = simple_strtoul(argv[1], NULL, 16); + memset(addr, '\0', size); + } else { + size = 0; + } + + if (sep) { /* export as text file */ + len = hexport(sep, &addr, size); + if (len < 0) { + error("Cannot export environment: errno = %d\n", + errno); + return 1; + } + sprintf(buf, "%zX", len); + setenv("filesize", buf); + + return 0; + } + + if (chk) { /* export as checksum protected block */ + res = (char *)&envp->data; + } else { /* export as raw binary data */ + res = (char *)&addr; + } + + len = hexport('\0', &res, ENV_SIZE); + if (len < 0) { + error("Cannot export environment: errno = %d\n", + errno); + return 1; + } + + if (chk) { + envp->crc = crc32(0, envp->data, ENV_SIZE); +#ifdef CONFIG_ENV_ADDR_REDUND + envp->flags = ACTIVE_FLAG; +#endif + } + sprintf(buf, "%zX", len + offsetof(env_t,data)); + setenv("filesize", buf); + + return 0; + +sep_err: + printf("## %s: only one of "-b", "-c" or "-t" allowed\n", + cmd); + return 1; +} + +/* + * env import [-d] [-t | -b | -c] addr [size] + * -d: delete existing environment before importing; + * otherwise overwrite / append to existion definitions + * -t: assume text format; either "size" must be given or the + * text data must be '\0' terminated + * -b: assume binary format ('\0' separated, "\0\0" terminated) + * -c: assume checksum protected environment format + * addr: memory address to read from + * size: length of input data; if missing, proper '\0' + * termination is mandatory + */ +static int do_env_import(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) +{ + char *cmd, *addr; + char sep = '\n'; + int chk = 0; + int fmt = 0; + int del = 0; + size_t size; + + cmd = *argv; + + while (--argc > 0 && **++argv == '-') { + char *arg = *argv; + while (*++arg) { + switch (*arg) { + case 'b': /* raw binary format */ + if (fmt++) + goto sep_err; + sep = '\0'; + break; + case 'c': /* external checksum format */ + if (fmt++) + goto sep_err; + sep = '\0'; + chk = 1; + break; + case 't': /* text format */ + if (fmt++) + goto sep_err; + sep = '\n'; + break; + case 'd': + del = 1; + break; + default: + cmd_usage(cmdtp); + return 1; + } + } + } + + if (argc < 1) { + cmd_usage(cmdtp); + return 1; + } + + if (!fmt) + printf("## Warning: defaulting to text format\n"); + + addr = (char *)simple_strtoul(argv[0], NULL, 16); + + if (argc == 2) { + size = simple_strtoul(argv[1], NULL, 16); + } else { + char *s = addr; + + size = 0; + + while (size < MAX_ENV_SIZE) { + if ((*s == sep) && (*(s+1) == '\0')) + break; + ++s; + ++size; + } + if (size == MAX_ENV_SIZE) { + printf("## Warning: Input data exceeds %d bytes" + " - truncated\n", MAX_ENV_SIZE); + } + ++size; + printf("## Info: input data size = %zd = 0x%zX\n", size, size); + } + + if (chk) { + uint32_t crc; + env_t *ep = (env_t *)addr; + + size -= offsetof(env_t, data); + memcpy(&crc, &ep->crc, sizeof(crc)); + + if (crc32(0, ep->data, size) != crc) { + puts("## Error: bad CRC, import failed\n"); + return 1; + } + addr = (char *)ep->data; + } + + if (himport(addr, size, sep, del ? 0 : H_NOCLEAR) == 0) { + error("Environment import failed: errno = %d\n", errno); + return 1; + } + + return 0; + +sep_err: + printf("## %s: only one of "-b", "-c" or "-t" allowed\n", + cmd); + return 1; +} + +#if defined(CONFIG_CMD_RUN) +extern int do_run (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]); +#endif + +/* + * New command line interface: "env" command with subcommands + */ +static cmd_tbl_t cmd_env_sub[] = { +#if defined(CONFIG_CMD_ASKENV) + U_BOOT_CMD_MKENT(ask, CONFIG_SYS_MAXARGS, 1, do_env_ask, "", ""), +#endif + U_BOOT_CMD_MKENT(default, 1, 0, do_env_default, "", ""), + U_BOOT_CMD_MKENT(delete, 2, 0, do_env_delete, "", ""), +#if defined(CONFIG_CMD_EDITENV) + U_BOOT_CMD_MKENT(edit, 2, 0, do_env_edit, "", ""), +#endif + U_BOOT_CMD_MKENT(export, 4, 0, do_env_export, "", ""), + U_BOOT_CMD_MKENT(import, 5, 0, do_env_import, "", ""), + U_BOOT_CMD_MKENT(print, CONFIG_SYS_MAXARGS, 1, do_env_print, "", ""), +#if defined(CONFIG_CMD_RUN) + U_BOOT_CMD_MKENT(run, CONFIG_SYS_MAXARGS, 1, do_run, "", ""), +#endif +#if defined(CONFIG_CMD_SAVEENV) && !defined(CONFIG_ENV_IS_NOWHERE) + U_BOOT_CMD_MKENT(save, 1, 0, do_env_save, "", ""), +#endif + U_BOOT_CMD_MKENT(set, CONFIG_SYS_MAXARGS, 0, do_env_set, "", ""), +}; + +static int do_env (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + cmd_tbl_t *cp; + + /* drop initial "env" arg */ + argc--; + argv++; + + cp = find_cmd_tbl(argv[0], cmd_env_sub, ARRAY_SIZE(cmd_env_sub)); + + if (cp) + return cp->cmd(cmdtp, flag, argc, argv); + + cmd_usage(cmdtp); + return 1; +} + +U_BOOT_CMD( + env, CONFIG_SYS_MAXARGS, 1, do_env, + "environment handling commands", +#if defined(CONFIG_CMD_ASKENV) + "ask name [message] [size] - ask for environment variable\nenv " +#endif + "default -f - reset default environment\n" +#if defined(CONFIG_CMD_EDITENV) + "env edit name - edit environment variable\n" +#endif + "env export [-t | -b | -c] addr [size] - export environmnt\n" + "env import [-d] [-t | -b | -c] addr [size] - import environmnt\n" + "env print [name ...] - print environment\n" +#if defined(CONFIG_CMD_RUN) + "env run var [...] - run commands in an environment variable\n" +#endif + "env save - save environment\n" + "env set [-f] name [arg ...]\n" +); + +/* + * Old command line interface, kept for compatibility + */
#if defined(CONFIG_CMD_EDITENV) U_BOOT_CMD( - editenv, 2, 0, do_editenv, + editenv, 2, 0, do_env_edit, "edit environment variable", "name\n" " - edit environment variable 'name'" @@ -613,7 +875,7 @@ U_BOOT_CMD( #endif
U_BOOT_CMD( - printenv, CONFIG_SYS_MAXARGS, 1, do_printenv, + printenv, CONFIG_SYS_MAXARGS, 1, do_env_print, "print environment variables", "\n - print values of all environment variables\n" "printenv name ...\n" @@ -621,7 +883,7 @@ U_BOOT_CMD( );
U_BOOT_CMD( - setenv, CONFIG_SYS_MAXARGS, 0, do_setenv, + setenv, CONFIG_SYS_MAXARGS, 0, do_env_set, "set environment variables", "name value ...\n" " - set environment variable 'name' to 'value ...'\n" @@ -632,7 +894,7 @@ U_BOOT_CMD( #if defined(CONFIG_CMD_ASKENV)
U_BOOT_CMD( - askenv, CONFIG_SYS_MAXARGS, 1, do_askenv, + askenv, CONFIG_SYS_MAXARGS, 1, do_env_ask, "get environment variables from stdin", "name [message] [size]\n" " - get environment variable 'name' from stdin (max 'size' chars)\n" @@ -647,7 +909,6 @@ U_BOOT_CMD( #endif
#if defined(CONFIG_CMD_RUN) -int do_run (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]); U_BOOT_CMD( run, CONFIG_SYS_MAXARGS, 1, do_run, "run commands in an environment variable", diff --git a/common/command.c b/common/command.c index a1fc592..b12a278 100644 --- a/common/command.c +++ b/common/command.c @@ -160,6 +160,7 @@ int cmd_usage(cmd_tbl_t *cmdtp)
int var_complete(int argc, char * const argv[], char last_char, int maxv, char *cmdv[]) { +#if 0 /* need to reimplement */ static char tmp_buf[512]; int space;
@@ -170,7 +171,7 @@ int var_complete(int argc, char * const argv[], char last_char, int maxv, char *
if (!space && argc == 2) return env_complete(argv[1], maxv, cmdv, sizeof(tmp_buf), tmp_buf); - +#endif return 0; }
diff --git a/common/dlmalloc.c b/common/dlmalloc.c index 2276532..ae5702d 100644 --- a/common/dlmalloc.c +++ b/common/dlmalloc.c @@ -222,7 +222,6 @@
- /* Preliminaries */
#ifndef __STD_C @@ -935,10 +934,10 @@ struct mallinfo mALLINFo(); #endif
/* ---------- To make a malloc.h, end cutting here ------------ */ -#else /* Moved to malloc.h */ +#endif /* 0 */ /* Moved to malloc.h */
#include <malloc.h> -#if 0 +#ifdef DEBUG #if __STD_C static void malloc_update_mallinfo (void); void malloc_stats (void); @@ -946,9 +945,7 @@ void malloc_stats (void); static void malloc_update_mallinfo (); void malloc_stats(); #endif -#endif /* 0 */ - -#endif /* 0 */ /* Moved to malloc.h */ +#endif /* DEBUG */
DECLARE_GLOBAL_DATA_PTR;
@@ -1618,9 +1615,9 @@ static struct mallinfo current_mallinfo = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
/* Tracking mmaps */
-#if 0 +#ifdef DEBUG static unsigned int n_mmaps = 0; -#endif /* 0 */ +#endif /* DEBUG */ static unsigned long mmapped_mem = 0; #if HAVE_MMAP static unsigned int max_n_mmaps = 0; @@ -3101,7 +3098,7 @@ size_t malloc_usable_size(mem) Void_t* mem;
/* Utility to update current_mallinfo for malloc_stats and mallinfo() */
-#if 0 +#ifdef DEBUG static void malloc_update_mallinfo() { int i; @@ -3139,7 +3136,7 @@ static void malloc_update_mallinfo() current_mallinfo.keepcost = chunksize(top);
} -#endif /* 0 */ +#endif /* DEBUG */
@@ -3158,7 +3155,7 @@ static void malloc_update_mallinfo()
*/
-#if 0 +#ifdef DEBUG void malloc_stats() { malloc_update_mallinfo(); @@ -3173,19 +3170,19 @@ void malloc_stats() (unsigned int)max_n_mmaps); #endif } -#endif /* 0 */ +#endif /* DEBUG */
/* mallinfo returns a copy of updated current mallinfo. */
-#if 0 +#ifdef DEBUG struct mallinfo mALLINFo() { malloc_update_mallinfo(); return current_mallinfo; } -#endif /* 0 */ +#endif /* DEBUG */
diff --git a/common/env_common.c b/common/env_common.c index 460309b..c263ee5 100644 --- a/common/env_common.c +++ b/common/env_common.c @@ -1,10 +1,10 @@ /* - * (C) Copyright 2000-2002 + * (C) Copyright 2000-2010 * Wolfgang Denk, DENX Software Engineering, wd@denx.de. * * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com> * Andreas Heppel aheppel@sysgo.de - + * * See file CREDITS for list of people who contributed to this * project. * @@ -28,17 +28,12 @@ #include <command.h> #include <environment.h> #include <linux/stddef.h> +#include <search.h> +#include <errno.h> #include <malloc.h>
DECLARE_GLOBAL_DATA_PTR;
-#undef DEBUG_ENV -#ifdef DEBUG_ENV -#define DEBUGF(fmt,args...) printf(fmt ,##args) -#else -#define DEBUGF(fmt,args...) -#endif - extern env_t *env_ptr;
extern void env_relocate_spec (void); @@ -134,33 +129,22 @@ uchar default_environment[] = { "\0" };
-void env_crc_update (void) -{ - env_ptr->crc = crc32(0, env_ptr->data, ENV_SIZE); -} - static uchar env_get_char_init (int index) { uchar c;
/* if crc was bad, use the default environment */ if (gd->env_valid) - { c = env_get_char_spec(index); - } else { + else c = default_environment[index]; - }
return (c); }
uchar env_get_char_memory (int index) { - if (gd->env_valid) { - return ( *((uchar *)(gd->env_addr + index)) ); - } else { - return ( default_environment[index] ); - } + return *env_get_addr(index); }
uchar env_get_char (int index) @@ -178,70 +162,81 @@ uchar env_get_char (int index)
uchar *env_get_addr (int index) { - if (gd->env_valid) { - return ( ((uchar *)(gd->env_addr + index)) ); - } else { - return (&default_environment[index]); - } + if (gd->env_valid) + return (uchar *)(gd->env_addr + index); + else + return &default_environment[index]; }
-void set_default_env(void) +void set_default_env(const char *s) { if (sizeof(default_environment) > ENV_SIZE) { - puts ("*** Error - default environment is too large\n\n"); + puts("*** Error - default environment is too large\n\n"); return; }
- memset(env_ptr, 0, sizeof(env_t)); - memcpy(env_ptr->data, default_environment, - sizeof(default_environment)); -#ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT - env_ptr->flags = 0xFF; -#endif - env_crc_update (); - gd->env_valid = 1; + if (s) { + if (*s == '!') { + printf("*** Warning - %s, " + "using default environment\n\n", + s+1); + } else { + puts(s); + } + } else { + puts("Using default environment\n\n"); + } + + if (himport((char *)default_environment, + sizeof(default_environment), '\0', 0) == 0) { + error("Environment import failed: errno = %d\n", errno); + } }
-void env_relocate (void) +/* + * Check if CRC is valid and (if yes) import the environment. + * Note that "buf" may or may not be aligned. + */ +int env_import(const char *buf, int check) { -#ifndef CONFIG_RELOC_FIXUP_WORKS - DEBUGF ("%s[%d] offset = 0x%lx\n", __FUNCTION__,__LINE__, - gd->reloc_off); -#endif + env_t *ep = (env_t *)buf;
-#ifdef ENV_IS_EMBEDDED - /* - * The environment buffer is embedded with the text segment, - * just relocate the environment pointer - */ -#ifndef CONFIG_RELOC_FIXUP_WORKS - env_ptr = (env_t *)((ulong)env_ptr + gd->reloc_off); -#endif - DEBUGF ("%s[%d] embedded ENV at %p\n", __FUNCTION__,__LINE__,env_ptr); -#else - /* - * We must allocate a buffer for the environment - */ - env_ptr = (env_t *)malloc (CONFIG_ENV_SIZE); - DEBUGF ("%s[%d] malloced ENV at %p\n", __FUNCTION__,__LINE__,env_ptr); -#endif + if (check) { + uint32_t crc; + + memcpy(&crc, &ep->crc, sizeof(crc));
+ if (crc32(0, ep->data, ENV_SIZE) != crc) { + set_default_env("!bad CRC"); + return 0; + } + } + + if (himport((char *)ep->data, ENV_SIZE, '\0', 0)) + return 1; + + error("Cannot import environment: errno = %d\n", errno); + + set_default_env("!import failed"); + + return 0; +} + +void env_relocate (void) +{ if (gd->env_valid == 0) { #if defined(CONFIG_ENV_IS_NOWHERE) /* Environment not changable */ - puts ("Using default environment\n\n"); + set_default_env(NULL); #else - puts ("*** Warning - bad CRC, using default environment\n\n"); show_boot_progress (-60); #endif - set_default_env(); - } - else { + set_default_env("!bad CRC"); + } else { env_relocate_spec (); } - gd->env_addr = (ulong)&(env_ptr->data); }
-#ifdef CONFIG_AUTO_COMPLETE +#if 0 /* need to reimplement - def CONFIG_AUTO_COMPLETE */ int env_complete(char *var, int maxv, char *cmdv[], int bufsz, char *buf) { int i, nxt, len, vallen, found; diff --git a/common/env_dataflash.c b/common/env_dataflash.c index 27a3bbc..270f2b3 100644 --- a/common/env_dataflash.c +++ b/common/env_dataflash.c @@ -1,4 +1,5 @@ -/* LowLevel function for DataFlash environment support +/* + * LowLevel function for DataFlash environment support * Author : Gilles Gastaldi (Atmel) * * This program is free software; you can redistribute it and/or @@ -15,13 +16,14 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA - * */ #include <common.h> #include <command.h> #include <environment.h> #include <linux/stddef.h> #include <dataflash.h> +#include <search.h> +#include <errno.h>
DECLARE_GLOBAL_DATA_PTR;
@@ -29,39 +31,59 @@ env_t *env_ptr = NULL;
char * env_name_spec = "dataflash";
-extern int read_dataflash (unsigned long addr, unsigned long size, char -*result); -extern int write_dataflash (unsigned long addr_dest, unsigned long addr_src, - unsigned long size); -extern int AT91F_DataflashInit (void); -extern uchar default_environment[]; +extern int read_dataflash(unsigned long addr, unsigned long size, + char *result); +extern int write_dataflash(unsigned long addr_dest, + unsigned long addr_src, unsigned long size); +extern int AT91F_DataflashInit(void);
+extern uchar default_environment[];
-uchar env_get_char_spec (int index) +uchar env_get_char_spec(int index) { uchar c; + read_dataflash(CONFIG_ENV_ADDR + index + offsetof(env_t,data), - 1, (char *)&c); + 1, (char *)&c); return (c); }
-void env_relocate_spec (void) +void env_relocate_spec(void) { - read_dataflash(CONFIG_ENV_ADDR, CONFIG_ENV_SIZE, (char *)env_ptr); + char buf[CONFIG_ENV_SIZE]; + + read_dataflash(CONFIG_ENV_ADDR, CONFIG_ENV_SIZE, buf); + + env_import(buf, 1); }
+#ifdef CONFIG_ENV_OFFSET_REDUND +#error No support for redundant environment on dataflash yet! +#endif + int saveenv(void) { - /* env must be copied to do not alter env structure in memory*/ - unsigned char temp[CONFIG_ENV_SIZE]; - memcpy(temp, env_ptr, CONFIG_ENV_SIZE); - return write_dataflash(CONFIG_ENV_ADDR, (unsigned long)temp, CONFIG_ENV_SIZE); + env_t env_new; + ssize_t len; + char *res; + + res = (char *)&env_new.data; + len = hexport('\0', &res, ENV_SIZE); + if (len < 0) { + error("Cannot export environment: errno = %d\n", errno); + return 1; + } + env_new.crc = crc32(0, env_new.data, ENV_SIZE); + + return write_dataflash(CONFIG_ENV_ADDR, + (unsigned long)&env_new, + CONFIG_ENV_SIZE); }
-/************************************************************************ - * Initialize Environment use +/* + * Initialize environment use * - * We are still running from ROM, so data use is limited + * We are still running from ROM, so data use is limited. * Use a (moderately small) buffer on the stack */ int env_init(void) @@ -69,30 +91,36 @@ int env_init(void) ulong crc, len, new; unsigned off; uchar buf[64]; - if (gd->env_valid == 0){ - AT91F_DataflashInit(); /* prepare for DATAFLASH read/write */ - - /* read old CRC */ - read_dataflash(CONFIG_ENV_ADDR + offsetof(env_t, crc), - sizeof(ulong), (char *)&crc); - new = 0; - len = ENV_SIZE; - off = offsetof(env_t,data); - while (len > 0) { - int n = (len > sizeof(buf)) ? sizeof(buf) : len; - read_dataflash(CONFIG_ENV_ADDR + off, n, (char *)buf); - new = crc32 (new, buf, n); - len -= n; - off += n; - } - if (crc == new) { - gd->env_addr = offsetof(env_t,data); - gd->env_valid = 1; - } else { - gd->env_addr = (ulong)&default_environment[0]; - gd->env_valid = 0; - } + + if (gd->env_valid) + return 0; + + AT91F_DataflashInit(); /* prepare for DATAFLASH read/write */ + + /* read old CRC */ + read_dataflash(CONFIG_ENV_ADDR + offsetof(env_t, crc), + sizeof(ulong), (char *)&crc); + + new = 0; + len = ENV_SIZE; + off = offsetof(env_t,data); + while (len > 0) { + int n = (len > sizeof(buf)) ? sizeof(buf) : len; + + read_dataflash(CONFIG_ENV_ADDR + off, n, (char *)buf); + + new = crc32 (new, buf, n); + len -= n; + off += n; + } + + if (crc == new) { + gd->env_addr = offsetof(env_t,data); + gd->env_valid = 1; + } else { + gd->env_addr = (ulong)&default_environment[0]; + gd->env_valid = 0; }
- return (0); + return 0; } diff --git a/common/env_eeprom.c b/common/env_eeprom.c index 8fe59f8..792b44f 100644 --- a/common/env_eeprom.c +++ b/common/env_eeprom.c @@ -1,10 +1,10 @@ /* - * (C) Copyright 2000-2002 + * (C) Copyright 2000-2010 * Wolfgang Denk, DENX Software Engineering, wd@denx.de. * * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com> * Andreas Heppel aheppel@sysgo.de - + * * See file CREDITS for list of people who contributed to this * project. * @@ -31,16 +31,19 @@ #if defined(CONFIG_I2C_ENV_EEPROM_BUS) #include <i2c.h> #endif +#include <search.h> +#include <errno.h> +#include <linux/compiler.h> /* for BUG_ON */
DECLARE_GLOBAL_DATA_PTR;
env_t *env_ptr = NULL;
-char * env_name_spec = "EEPROM"; +char *env_name_spec = "EEPROM"; int env_eeprom_bus = -1;
-static int eeprom_bus_read (unsigned dev_addr, unsigned offset, uchar *buffer, - unsigned cnt) +static int eeprom_bus_read(unsigned dev_addr, unsigned offset, + uchar *buffer, unsigned cnt) { int rcode; #if defined(CONFIG_I2C_ENV_EEPROM_BUS) @@ -51,9 +54,9 @@ static int eeprom_bus_read (unsigned dev_addr, unsigned offset, uchar *buffer, I2C_MUX_DEVICE *dev = NULL; dev = i2c_mux_ident_muxstring( (uchar *)CONFIG_I2C_ENV_EEPROM_BUS); - if (dev != NULL) { + if (dev != NULL) env_eeprom_bus = dev->busid; - } else + else printf ("error adding env eeprom bus.\n"); } if (old_bus != env_eeprom_bus) { @@ -67,6 +70,7 @@ static int eeprom_bus_read (unsigned dev_addr, unsigned offset, uchar *buffer, #endif
rcode = eeprom_read (dev_addr, offset, buffer, cnt); + #if defined(CONFIG_I2C_ENV_EEPROM_BUS) if (old_bus != env_eeprom_bus) i2c_set_bus_num(old_bus); @@ -74,8 +78,8 @@ static int eeprom_bus_read (unsigned dev_addr, unsigned offset, uchar *buffer, return rcode; }
-static int eeprom_bus_write (unsigned dev_addr, unsigned offset, uchar *buffer, - unsigned cnt) +static int eeprom_bus_write(unsigned dev_addr, unsigned offset, + uchar *buffer, unsigned cnt) { int rcode; #if defined(CONFIG_I2C_ENV_EEPROM_BUS) @@ -83,7 +87,7 @@ static int eeprom_bus_write (unsigned dev_addr, unsigned offset, uchar *buffer,
rcode = i2c_mux_ident_muxstring_f((uchar *)CONFIG_I2C_ENV_EEPROM_BUS); #endif - rcode = eeprom_write (dev_addr, offset, buffer, cnt); + rcode = eeprom_write(dev_addr, offset, buffer, cnt); #if defined(CONFIG_I2C_ENV_EEPROM_BUS) i2c_set_bus_num(old_bus); #endif @@ -95,12 +99,12 @@ uchar env_get_char_spec (int index) uchar c; unsigned int off; off = CONFIG_ENV_OFFSET; + #ifdef CONFIG_ENV_OFFSET_REDUND if (gd->env_valid == 2) off = CONFIG_ENV_OFFSET_REDUND; #endif - - eeprom_bus_read (CONFIG_SYS_DEF_EEPROM_ADDR, + eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR, off + index + offsetof(env_t,data), &c, 1);
@@ -109,40 +113,60 @@ uchar env_get_char_spec (int index)
void env_relocate_spec (void) { + char buf[CONFIG_ENV_SIZE]; unsigned int off = CONFIG_ENV_OFFSET; + #ifdef CONFIG_ENV_OFFSET_REDUND if (gd->env_valid == 2) off = CONFIG_ENV_OFFSET_REDUND; #endif - eeprom_bus_read (CONFIG_SYS_DEF_EEPROM_ADDR, + eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR, off, - (uchar*)env_ptr, + (uchar *)buf, CONFIG_ENV_SIZE); + + env_import(buf, 1); }
int saveenv(void) { + env_t env_new; + ssize_t len; + char *res; int rc; unsigned int off = CONFIG_ENV_OFFSET; #ifdef CONFIG_ENV_OFFSET_REDUND unsigned int off_red = CONFIG_ENV_OFFSET_REDUND; char flag_obsolete = OBSOLETE_FLAG; +#endif + + BUG_ON(env_ptr != NULL); + + res = (char *)&env_new.data; + len = hexport('\0', &res, ENV_SIZE); + if (len < 0) { + error("Cannot export environment: errno = %d\n", errno); + return 1; + } + env_new.crc = crc32(0, env_new.data, ENV_SIZE); + +#ifdef CONFIG_ENV_OFFSET_REDUND if (gd->env_valid == 1) { off = CONFIG_ENV_OFFSET_REDUND; off_red = CONFIG_ENV_OFFSET; }
- env_ptr->flags = ACTIVE_FLAG; + env_new.flags = ACTIVE_FLAG; #endif
- rc = eeprom_bus_write (CONFIG_SYS_DEF_EEPROM_ADDR, + rc = eeprom_bus_write(CONFIG_SYS_DEF_EEPROM_ADDR, off, - (uchar *)env_ptr, + (uchar *)&env_new, CONFIG_ENV_SIZE);
#ifdef CONFIG_ENV_OFFSET_REDUND if (rc == 0) { - eeprom_bus_write (CONFIG_SYS_DEF_EEPROM_ADDR, + eeprom_bus_write(CONFIG_SYS_DEF_EEPROM_ADDR, off_red + offsetof(env_t,flags), (uchar *)&flag_obsolete, 1); @@ -157,10 +181,10 @@ int saveenv(void) return rc; }
-/************************************************************************ +/* * Initialize Environment use * - * We are still running from ROM, so data use is limited + * We are still running from ROM, so data use is limited. * Use a (moderately small) buffer on the stack */
@@ -175,31 +199,31 @@ int env_init(void) unsigned char flags[2]; int i;
- eeprom_init (); /* prepare for EEPROM read/write */ + eeprom_init(); /* prepare for EEPROM read/write */
off_env[0] = CONFIG_ENV_OFFSET; off_env[1] = CONFIG_ENV_OFFSET_REDUND;
for (i = 0; i < 2; i++) { /* read CRC */ - eeprom_bus_read (CONFIG_SYS_DEF_EEPROM_ADDR, + eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR, off_env[i] + offsetof(env_t,crc), (uchar *)&crc[i], sizeof(ulong)); /* read FLAGS */ - eeprom_bus_read (CONFIG_SYS_DEF_EEPROM_ADDR, + eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR, off_env[i] + offsetof(env_t,flags), (uchar *)&flags[i], sizeof(uchar));
- crc_tmp= 0; + crc_tmp = 0; len = ENV_SIZE; off = off_env[i] + offsetof(env_t,data); while (len > 0) { int n = (len > sizeof(buf)) ? sizeof(buf) : len;
- eeprom_bus_read (CONFIG_SYS_DEF_EEPROM_ADDR, off, + eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR, off, buf, n);
- crc_tmp = crc32 (crc_tmp, buf, n); + crc_tmp = crc32(crc_tmp, buf, n); len -= n; off += n; } @@ -245,22 +269,23 @@ int env_init(void) unsigned off; uchar buf[64];
- eeprom_init (); /* prepare for EEPROM read/write */ + eeprom_init(); /* prepare for EEPROM read/write */
/* read old CRC */ - eeprom_bus_read (CONFIG_SYS_DEF_EEPROM_ADDR, + eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR, CONFIG_ENV_OFFSET+offsetof(env_t,crc), (uchar *)&crc, sizeof(ulong));
new = 0; len = ENV_SIZE; off = offsetof(env_t,data); + while (len > 0) { int n = (len > sizeof(buf)) ? sizeof(buf) : len;
- eeprom_bus_read (CONFIG_SYS_DEF_EEPROM_ADDR, + eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR, CONFIG_ENV_OFFSET + off, buf, n); - new = crc32 (new, buf, n); + new = crc32(new, buf, n); len -= n; off += n; } diff --git a/common/env_flash.c b/common/env_flash.c index 925c5a0..1da78b7 100644 --- a/common/env_flash.c +++ b/common/env_flash.c @@ -1,5 +1,5 @@ /* - * (C) Copyright 2000-2002 + * (C) Copyright 2000-2010 * Wolfgang Denk, DENX Software Engineering, wd@denx.de. * * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com> @@ -31,6 +31,8 @@ #include <environment.h> #include <linux/stddef.h> #include <malloc.h> +#include <search.h> +#include <errno.h>
DECLARE_GLOBAL_DATA_PTR;
@@ -51,36 +53,38 @@ char * env_name_spec = "Flash"; extern uchar environment[]; env_t *env_ptr = (env_t *)(&environment[0]);
-#ifdef CMD_SAVEENV -/* static env_t *flash_addr = (env_t *)(&environment[0]);-broken on ARM-wd-*/ static env_t *flash_addr = (env_t *)CONFIG_ENV_ADDR; -#endif
#else /* ! ENV_IS_EMBEDDED */
env_t *env_ptr = (env_t *)CONFIG_ENV_ADDR; -#ifdef CMD_SAVEENV static env_t *flash_addr = (env_t *)CONFIG_ENV_ADDR; -#endif
#endif /* ENV_IS_EMBEDDED */
+#if defined(CMD_SAVEENV) || defined(CONFIG_ENV_ADDR_REDUND) +/* CONFIG_ENV_ADDR is supposed to be on sector boundary */ +static ulong end_addr = CONFIG_ENV_ADDR + CONFIG_ENV_SECT_SIZE - 1; +#endif + #ifdef CONFIG_ENV_ADDR_REDUND static env_t *flash_addr_new = (env_t *)CONFIG_ENV_ADDR_REDUND;
-/* CONFIG_ENV_ADDR is supposed to be on sector boundary */ -static ulong end_addr = CONFIG_ENV_ADDR + CONFIG_ENV_SECT_SIZE - 1; +/* CONFIG_ENV_ADDR_REDUND is supposed to be on sector boundary */ static ulong end_addr_new = CONFIG_ENV_ADDR_REDUND + CONFIG_ENV_SECT_SIZE - 1; #endif /* CONFIG_ENV_ADDR_REDUND */
extern uchar default_environment[];
-uchar env_get_char_spec (int index) +uchar env_get_char_spec(int index) { - return ( *((uchar *)(gd->env_addr + index)) ); + return (*((uchar *)(gd->env_addr + index))); }
+#undef debug +#define debug printf + #ifdef CONFIG_ENV_ADDR_REDUND
int env_init(void) @@ -123,91 +127,97 @@ int env_init(void) gd->env_valid = 2; }
- return (0); + return 0; }
#ifdef CMD_SAVEENV int saveenv(void) { - char *saved_data = NULL; - int rc = 1; - char flag = OBSOLETE_FLAG, new_flag = ACTIVE_FLAG; + env_t env_new; + ssize_t len; + char *saved_data = NULL; + char *res; + int rc = 1; + char flag = OBSOLETE_FLAG, new_flag = ACTIVE_FLAG; #if CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE - ulong up_data = 0; + ulong up_data = 0; #endif
- debug ("Protect off %08lX ... %08lX\n", + debug("Protect off %08lX ... %08lX\n", (ulong)flash_addr, end_addr);
- if (flash_sect_protect (0, (ulong)flash_addr, end_addr)) { - goto Done; + if (flash_sect_protect(0, (ulong)flash_addr, end_addr)) { + goto done; }
- debug ("Protect off %08lX ... %08lX\n", + debug("Protect off %08lX ... %08lX\n", (ulong)flash_addr_new, end_addr_new);
- if (flash_sect_protect (0, (ulong)flash_addr_new, end_addr_new)) { - goto Done; + if (flash_sect_protect(0, (ulong)flash_addr_new, end_addr_new)) { + goto done; + } + + res = (char *)&env_new.data; + len = hexport('\0', &res, ENV_SIZE); + if (len < 0) { + error("Cannot export environment: errno = %d\n", errno); + goto done; } + env_new.crc = crc32(0, env_new.data, ENV_SIZE); + env_new.flags = new_flag;
#if CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE up_data = (end_addr_new + 1 - ((long)flash_addr_new + CONFIG_ENV_SIZE)); - debug ("Data to save 0x%x\n", up_data); + debug("Data to save 0x%lX\n", up_data); if (up_data) { if ((saved_data = malloc(up_data)) == NULL) { printf("Unable to save the rest of sector (%ld)\n", up_data); - goto Done; + goto done; } memcpy(saved_data, (void *)((long)flash_addr_new + CONFIG_ENV_SIZE), up_data); - debug ("Data (start 0x%x, len 0x%x) saved at 0x%x\n", - (long)flash_addr_new + CONFIG_ENV_SIZE, - up_data, saved_data); + debug("Data (start 0x%lX, len 0x%lX) saved at 0x%p\n", + (long)flash_addr_new + CONFIG_ENV_SIZE, + up_data, saved_data); } #endif - puts ("Erasing Flash..."); - debug (" %08lX ... %08lX ...", + puts("Erasing Flash..."); + debug(" %08lX ... %08lX ...", (ulong)flash_addr_new, end_addr_new);
- if (flash_sect_erase ((ulong)flash_addr_new, end_addr_new)) { - goto Done; + if (flash_sect_erase((ulong)flash_addr_new, end_addr_new)) { + goto done; }
- puts ("Writing to Flash... "); - debug (" %08lX ... %08lX ...", + puts("Writing to Flash... "); + debug(" %08lX ... %08lX ...", (ulong)&(flash_addr_new->data), sizeof(env_ptr->data)+(ulong)&(flash_addr_new->data)); - if ((rc = flash_write((char *)env_ptr->data, - (ulong)&(flash_addr_new->data), - sizeof(env_ptr->data))) || - (rc = flash_write((char *)&(env_ptr->crc), - (ulong)&(flash_addr_new->crc), - sizeof(env_ptr->crc))) || + if ((rc = flash_write((char *)&env_new, + (ulong)flash_addr_new, + sizeof(env_new))) || (rc = flash_write(&flag, (ulong)&(flash_addr->flags), - sizeof(flash_addr->flags))) || - (rc = flash_write(&new_flag, - (ulong)&(flash_addr_new->flags), - sizeof(flash_addr_new->flags)))) - { - flash_perror (rc); - goto Done; + sizeof(flash_addr->flags))) ) { + flash_perror(rc); + goto done; } - puts ("done\n");
#if CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE if (up_data) { /* restore the rest of sector */ - debug ("Restoring the rest of data to 0x%x len 0x%x\n", - (long)flash_addr_new + CONFIG_ENV_SIZE, up_data); + debug("Restoring the rest of data to 0x%lX len 0x%lX\n", + (long)flash_addr_new + CONFIG_ENV_SIZE, up_data); if (flash_write(saved_data, (long)flash_addr_new + CONFIG_ENV_SIZE, up_data)) { flash_perror(rc); - goto Done; + goto done; } } #endif + puts("done\n"); + { env_t * etmp = flash_addr; ulong ltmp = end_addr; @@ -220,13 +230,12 @@ int saveenv(void) }
rc = 0; -Done: - +done: if (saved_data) - free (saved_data); + free(saved_data); /* try to re-protect */ - (void) flash_sect_protect (1, (ulong)flash_addr, end_addr); - (void) flash_sect_protect (1, (ulong)flash_addr_new, end_addr_new); + (void) flash_sect_protect(1, (ulong)flash_addr, end_addr); + (void) flash_sect_protect(1, (ulong)flash_addr_new, end_addr_new);
return rc; } @@ -244,83 +253,93 @@ int env_init(void)
gd->env_addr = (ulong)&default_environment[0]; gd->env_valid = 0; - return (0); + return 0; }
#ifdef CMD_SAVEENV
int saveenv(void) { - int len, rc; - ulong end_addr; - ulong flash_sect_addr; -#if defined(CONFIG_ENV_SECT_SIZE) && (CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE) - ulong flash_offset; - uchar env_buffer[CONFIG_ENV_SECT_SIZE]; -#else - uchar *env_buffer = (uchar *)env_ptr; -#endif /* CONFIG_ENV_SECT_SIZE */ - int rcode = 0; - -#if defined(CONFIG_ENV_SECT_SIZE) && (CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE) - - flash_offset = ((ulong)flash_addr) & (CONFIG_ENV_SECT_SIZE-1); - flash_sect_addr = ((ulong)flash_addr) & ~(CONFIG_ENV_SECT_SIZE-1); - - debug ( "copy old content: " - "sect_addr: %08lX env_addr: %08lX offset: %08lX\n", - flash_sect_addr, (ulong)flash_addr, flash_offset); - - /* copy old contents to temporary buffer */ - memcpy (env_buffer, (void *)flash_sect_addr, CONFIG_ENV_SECT_SIZE); - - /* copy current environment to temporary buffer */ - memcpy ((uchar *)((unsigned long)env_buffer + flash_offset), - env_ptr, - CONFIG_ENV_SIZE); + env_t env_new; + ssize_t len; + int rc = 1; + char *res; + char *saved_data = NULL; +#if CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE + ulong up_data = 0;
- len = CONFIG_ENV_SECT_SIZE; -#else - flash_sect_addr = (ulong)flash_addr; - len = CONFIG_ENV_SIZE; + up_data = (end_addr + 1 - ((long)flash_addr + CONFIG_ENV_SIZE)); + debug("Data to save 0x%lx\n", up_data); + if (up_data) { + if ((saved_data = malloc(up_data)) == NULL) { + printf("Unable to save the rest of sector (%ld)\n", + up_data); + goto done; + } + memcpy(saved_data, + (void *)((long)flash_addr + CONFIG_ENV_SIZE), up_data); + debug("Data (start 0x%lx, len 0x%lx) saved at 0x%lx\n", + (ulong)flash_addr + CONFIG_ENV_SIZE, + up_data, + (ulong)saved_data); + } #endif /* CONFIG_ENV_SECT_SIZE */
- end_addr = flash_sect_addr + len - 1; + debug("Protect off %08lX ... %08lX\n", + (ulong)flash_addr, end_addr);
- debug ("Protect off %08lX ... %08lX\n", - (ulong)flash_sect_addr, end_addr); + if (flash_sect_protect(0, (long)flash_addr, end_addr)) + goto done;
- if (flash_sect_protect (0, flash_sect_addr, end_addr)) - return 1; + res = (char *)&env_new.data; + len = hexport('\0', &res, ENV_SIZE); + if (len < 0) { + error("Cannot export environment: errno = %d\n", errno); + goto done; + } + env_new.crc = crc32(0, env_new.data, ENV_SIZE);
- puts ("Erasing Flash..."); - if (flash_sect_erase (flash_sect_addr, end_addr)) - return 1; + puts("Erasing Flash..."); + if (flash_sect_erase((long)flash_addr, end_addr)) + goto done;
- puts ("Writing to Flash... "); - rc = flash_write((char *)env_buffer, flash_sect_addr, len); + puts("Writing to Flash... "); + rc = flash_write((char *)&env_new, (long)flash_addr, CONFIG_ENV_SIZE); if (rc != 0) { - flash_perror (rc); - rcode = 1; - } else { - puts ("done\n"); + flash_perror(rc); + goto done; } - +#if CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE + if (up_data) { /* restore the rest of sector */ + debug("Restoring the rest of data to 0x%lx len 0x%lx\n", + (ulong)flash_addr + CONFIG_ENV_SIZE, up_data); + if (flash_write(saved_data, + (long)flash_addr + CONFIG_ENV_SIZE, + up_data)) { + flash_perror(rc); + goto done; + } + } +#endif + puts("done\n"); + rc = 0; +done: + if (saved_data) + free(saved_data); /* try to re-protect */ - (void) flash_sect_protect (1, flash_sect_addr, end_addr); - return rcode; + (void) flash_sect_protect(1, (long)flash_addr, end_addr); + return rc; }
#endif /* CMD_SAVEENV */
#endif /* CONFIG_ENV_ADDR_REDUND */
-void env_relocate_spec (void) +void env_relocate_spec(void) { -#if !defined(ENV_IS_EMBEDDED) || defined(CONFIG_ENV_ADDR_REDUND) #ifdef CONFIG_ENV_ADDR_REDUND if (gd->env_addr != (ulong)&(flash_addr->data)) { - env_t * etmp = flash_addr; + env_t *etmp = flash_addr; ulong ltmp = end_addr;
flash_addr = flash_addr_new; @@ -336,11 +355,11 @@ void env_relocate_spec (void) char flag = OBSOLETE_FLAG;
gd->env_valid = 2; - flash_sect_protect (0, (ulong)flash_addr_new, end_addr_new); + flash_sect_protect(0, (ulong)flash_addr_new, end_addr_new); flash_write(&flag, (ulong)&(flash_addr_new->flags), sizeof(flash_addr_new->flags)); - flash_sect_protect (1, (ulong)flash_addr_new, end_addr_new); + flash_sect_protect(1, (ulong)flash_addr_new, end_addr_new); }
if (flash_addr->flags != ACTIVE_FLAG && @@ -348,19 +367,17 @@ void env_relocate_spec (void) char flag = ACTIVE_FLAG;
gd->env_valid = 2; - flash_sect_protect (0, (ulong)flash_addr, end_addr); + flash_sect_protect(0, (ulong)flash_addr, end_addr); flash_write(&flag, (ulong)&(flash_addr->flags), sizeof(flash_addr->flags)); - flash_sect_protect (1, (ulong)flash_addr, end_addr); + flash_sect_protect(1, (ulong)flash_addr, end_addr); }
if (gd->env_valid == 2) puts ("*** Warning - some problems detected " "reading environment; recovered successfully\n\n"); #endif /* CONFIG_ENV_ADDR_REDUND */ -#ifdef CMD_SAVEENV - memcpy (env_ptr, (void*)flash_addr, CONFIG_ENV_SIZE); -#endif -#endif /* ! ENV_IS_EMBEDDED || CONFIG_ENV_ADDR_REDUND */ + + env_import((char *)flash_addr, 1); } diff --git a/common/env_mgdisk.c b/common/env_mgdisk.c index b9de1ed..a69923b 100644 --- a/common/env_mgdisk.c +++ b/common/env_mgdisk.c @@ -30,7 +30,7 @@ /* references to names in env_common.c */ extern uchar default_environment[];
-char * env_name_spec = "MG_DISK"; +char *env_name_spec = "MG_DISK";
env_t *env_ptr = 0;
@@ -38,34 +38,27 @@ DECLARE_GLOBAL_DATA_PTR;
uchar env_get_char_spec(int index) { - return (*((uchar *) (gd->env_addr + index))); + return (*((uchar *)(gd->env_addr + index))); }
void env_relocate_spec(void) { - unsigned int err; + char buf[CONFIG_ENV_SIZE]; + unsigned int err, rc;
err = mg_disk_init(); if (err) { - puts ("*** Warning - mg_disk_init error"); - goto OUT; - } - err = mg_disk_read(CONFIG_ENV_ADDR, (u_char *)env_ptr, CONFIG_ENV_SIZE); - if (err) { - puts ("*** Warning - mg_disk_read error"); - goto OUT; + set_default_env("!mg_disk_init error"); + return; }
- if (crc32(0, env_ptr->data, ENV_SIZE) != env_ptr->crc) { - puts ("*** Warning - CRC error"); - goto OUT; + err = mg_disk_read(CONFIG_ENV_ADDR, buf, CONFIG_ENV_SIZE); + if (err) { + set_default_env("!mg_disk_read error"); + return; }
- return; - -OUT: - printf (", using default environment\n\n"); - set_default_env(); + env_import(buf, 1); }
int saveenv(void) @@ -76,7 +69,7 @@ int saveenv(void) err = mg_disk_write(CONFIG_ENV_ADDR, (u_char *)env_ptr, CONFIG_ENV_SIZE); if (err) - puts ("*** Warning - mg_disk_write error\n\n"); + puts("*** Warning - mg_disk_write error\n\n");
return err; } @@ -84,7 +77,7 @@ int saveenv(void) int env_init(void) { /* use default */ - gd->env_addr = (ulong) & default_environment[0]; + gd->env_addr = (ulong)&default_environment[0]; gd->env_valid = 1;
return 0; diff --git a/common/env_nand.c b/common/env_nand.c index a5e1038..ab91594 100644 --- a/common/env_nand.c +++ b/common/env_nand.c @@ -1,16 +1,16 @@ /* + * (C) Copyright 2000-2010 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * * (C) Copyright 2008 * Stuart Wood, Lab X Technologies stuart.wood@labxtechnologies.com * * (C) Copyright 2004 * Jian Zhang, Texas Instruments, jzhang@ti.com. - - * (C) Copyright 2000-2006 - * Wolfgang Denk, DENX Software Engineering, wd@denx.de. * * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com> * Andreas Heppel aheppel@sysgo.de - + * * See file CREDITS for list of people who contributed to this * project. * @@ -30,7 +30,7 @@ * MA 02111-1307 USA */
-/* #define DEBUG */ +#define DEBUG
#include <common.h> #include <command.h> @@ -38,7 +38,8 @@ #include <linux/stddef.h> #include <malloc.h> #include <nand.h> -#include <asm/errno.h> +#include <search.h> +#include <errno.h>
#if defined(CONFIG_CMD_SAVEENV) && defined(CONFIG_CMD_NAND) #define CMD_SAVEENV @@ -57,7 +58,7 @@ /* references to names in env_common.c */ extern uchar default_environment[];
-char * env_name_spec = "NAND"; +char *env_name_spec = "NAND";
#if defined(ENV_IS_EMBEDDED) @@ -69,12 +70,6 @@ env_t *env_ptr = (env_t *)CONFIG_NAND_ENV_DST; env_t *env_ptr = 0; #endif /* ENV_IS_EMBEDDED */
- -/* local functions */ -#if !defined(ENV_IS_EMBEDDED) -static void use_default(void); -#endif - DECLARE_GLOBAL_DATA_PTR;
uchar env_get_char_spec (int index) @@ -82,17 +77,17 @@ uchar env_get_char_spec (int index) return ( *((uchar *)(gd->env_addr + index)) ); }
- -/* this is called before nand_init() - * so we can't read Nand to validate env data. - * Mark it OK for now. env_relocate() in env_common.c - * will call our relocate function which does the real - * validation. +/* + * This is called before nand_init() so we can't read NAND to + * validate env data. + * + * Mark it OK for now. env_relocate() in env_common.c will call our + * relocate function which does the real validation. * * When using a NAND boot image (like sequoia_nand), the environment - * can be embedded or attached to the U-Boot image in NAND flash. This way - * the SPL loads not only the U-Boot image from NAND but also the - * environment. + * can be embedded or attached to the U-Boot image in NAND flash. + * This way the SPL loads not only the U-Boot image from NAND but + * also the environment. */ int env_init(void) { @@ -189,11 +184,12 @@ int writeenv(size_t offset, u_char *buf) #ifdef CONFIG_ENV_OFFSET_REDUND int saveenv(void) { - int ret = 0; + env_t env_new; + ssize_t len; + char *res; + int ret = 0; nand_erase_options_t nand_erase_options;
- env_ptr->flags++; - nand_erase_options.length = CONFIG_ENV_RANGE; nand_erase_options.quiet = 0; nand_erase_options.jffs2 = 0; @@ -201,36 +197,53 @@ int saveenv(void)
if (CONFIG_ENV_RANGE < CONFIG_ENV_SIZE) return 1; + + res = (char *)&env_new.data; + len = hexport('\0', &res, ENV_SIZE); + if (len < 0) { + error("Cannot export environment: errno = %d\n", errno); + return 1; + } + env_new.crc = crc32(0, env_new.data, ENV_SIZE); + env_new.flags = ACTIVE_FLAG; + if(gd->env_valid == 1) { - puts ("Erasing redundant Nand...\n"); + puts("Erasing redundant NAND...\n"); nand_erase_options.offset = CONFIG_ENV_OFFSET_REDUND; if (nand_erase_opts(&nand_info[0], &nand_erase_options)) return 1;
- puts ("Writing to redundant Nand... "); - ret = writeenv(CONFIG_ENV_OFFSET_REDUND, (u_char *) env_ptr); + puts("Writing to redundant NAND... "); + ret = writeenv(CONFIG_ENV_OFFSET_REDUND, + (u_char *)&env_new); } else { - puts ("Erasing Nand...\n"); + puts("Erasing NAND...\n"); nand_erase_options.offset = CONFIG_ENV_OFFSET; if (nand_erase_opts(&nand_info[0], &nand_erase_options)) return 1;
- puts ("Writing to Nand... "); - ret = writeenv(CONFIG_ENV_OFFSET, (u_char *) env_ptr); + puts("Writing to NAND... "); + ret = writeenv(CONFIG_ENV_OFFSET, + (u_char *)&env_new); } if (ret) { puts("FAILED!\n"); return 1; }
- puts ("done\n"); + puts("done\n"); + gd->env_valid = (gd->env_valid == 2 ? 1 : 2); + return ret; } #else /* ! CONFIG_ENV_OFFSET_REDUND */ int saveenv(void) { int ret = 0; + env_t env_new; + ssize_t len; + char *res; nand_erase_options_t nand_erase_options;
nand_erase_options.length = CONFIG_ENV_RANGE; @@ -241,23 +254,32 @@ int saveenv(void)
if (CONFIG_ENV_RANGE < CONFIG_ENV_SIZE) return 1; - puts ("Erasing Nand...\n"); + + res = (char *)&env_new.data; + len = hexport('\0', &res, ENV_SIZE); + if (len < 0) { + error("Cannot export environment: errno = %d\n", errno); + return 1; + } + env_new.crc = crc32(0, env_new.data, ENV_SIZE); + + puts("Erasing Nand...\n"); if (nand_erase_opts(&nand_info[0], &nand_erase_options)) return 1;
- puts ("Writing to Nand... "); - if (writeenv(CONFIG_ENV_OFFSET, (u_char *) env_ptr)) { + puts("Writing to Nand... "); + if (writeenv(CONFIG_ENV_OFFSET, (u_char *)&env_new)) { puts("FAILED!\n"); return 1; }
- puts ("done\n"); + puts("done\n"); return ret; } #endif /* CONFIG_ENV_OFFSET_REDUND */ #endif /* CMD_SAVEENV */
-int readenv (size_t offset, u_char * buf) +int readenv(size_t offset, u_char * buf) { size_t end = offset + CONFIG_ENV_RANGE; size_t amount_loaded = 0; @@ -318,47 +340,50 @@ int get_nand_env_oob(nand_info_t *nand, unsigned long *result) #endif
#ifdef CONFIG_ENV_OFFSET_REDUND -void env_relocate_spec (void) +void env_relocate_spec(void) { #if !defined(ENV_IS_EMBEDDED) int crc1_ok = 0, crc2_ok = 0; - env_t *tmp_env1, *tmp_env2; + env_t *ep, *tmp_env1, *tmp_env2;
- tmp_env1 = (env_t *) malloc(CONFIG_ENV_SIZE); - tmp_env2 = (env_t *) malloc(CONFIG_ENV_SIZE); + tmp_env1 = (env_t *)malloc(CONFIG_ENV_SIZE); + tmp_env2 = (env_t *)malloc(CONFIG_ENV_SIZE);
if ((tmp_env1 == NULL) || (tmp_env2 == NULL)) { puts("Can't allocate buffers for environment\n"); - free (tmp_env1); - free (tmp_env2); - return use_default(); + free(tmp_env1); + free(tmp_env2); + set_default_env("!malloc() failed"); + return; }
if (readenv(CONFIG_ENV_OFFSET, (u_char *) tmp_env1)) - puts("No Valid Environment Area Found\n"); + puts("No Valid Environment Area found\n"); + if (readenv(CONFIG_ENV_OFFSET_REDUND, (u_char *) tmp_env2)) - puts("No Valid Reundant Environment Area Found\n"); + puts("No Valid Redundant Environment Area found\n");
crc1_ok = (crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc); crc2_ok = (crc32(0, tmp_env2->data, ENV_SIZE) == tmp_env2->crc);
- if(!crc1_ok && !crc2_ok) { + if (!crc1_ok && !crc2_ok) { free(tmp_env1); free(tmp_env2); - return use_default(); - } else if(crc1_ok && !crc2_ok) + set_default_env("!bad CRC"); + return; + } else if (crc1_ok && !crc2_ok) { gd->env_valid = 1; - else if(!crc1_ok && crc2_ok) + } else if (!crc1_ok && crc2_ok) { gd->env_valid = 2; - else { + } else { /* both ok - check serial */ - if(tmp_env1->flags == 255 && tmp_env2->flags == 0) + if (tmp_env1->flags == 255 && tmp_env2->flags == 0) gd->env_valid = 2; - else if(tmp_env2->flags == 255 && tmp_env1->flags == 0) + else if (tmp_env2->flags == 255 && tmp_env1->flags == 0) gd->env_valid = 1; - else if(tmp_env1->flags > tmp_env2->flags) + else if (tmp_env1->flags > tmp_env2->flags) gd->env_valid = 1; - else if(tmp_env2->flags > tmp_env1->flags) + else if (tmp_env2->flags > tmp_env1->flags) gd->env_valid = 2; else /* flags are equal - almost impossible */ gd->env_valid = 1; @@ -366,51 +391,52 @@ void env_relocate_spec (void) }
free(env_ptr); - if(gd->env_valid == 1) { - env_ptr = tmp_env1; - free(tmp_env2); - } else { - env_ptr = tmp_env2; - free(tmp_env1); - } + + if (gd->env_valid == 1) + ep = tmp_env1; + else + ep = tmp_env2; + + env_import((char *)ep, 0); + + free(tmp_env1); + free(tmp_env2);
#endif /* ! ENV_IS_EMBEDDED */ } #else /* ! CONFIG_ENV_OFFSET_REDUND */ /* - * The legacy NAND code saved the environment in the first NAND device i.e., - * nand_dev_desc + 0. This is also the behaviour using the new NAND code. + * The legacy NAND code saved the environment in the first NAND + * device i.e., nand_dev_desc + 0. This is also the behaviour using + * the new NAND code. */ void env_relocate_spec (void) { #if !defined(ENV_IS_EMBEDDED) int ret; + char buf[CONFIG_ENV_SIZE];
#if defined(CONFIG_ENV_OFFSET_OOB) ret = get_nand_env_oob(&nand_info[0], &nand_env_oob_offset); - /* If unable to read environment offset from NAND OOB then fall through + /* + * If unable to read environment offset from NAND OOB then fall through * to the normal environment reading code below */ - if (!ret) + if (!ret) { printf("Found Environment offset in OOB..\n"); - else - return use_default(); + } else { + set_default_env("!no env offset in OOB"); + return; + } #endif
- ret = readenv(CONFIG_ENV_OFFSET, (u_char *) env_ptr); - if (ret) - return use_default(); + ret = readenv(CONFIG_ENV_OFFSET, (u_char *)buf); + if (ret) { + set_default_env("!readenv() failed"); + return; + }
- if (crc32(0, env_ptr->data, ENV_SIZE) != env_ptr->crc) - return use_default(); + env_import(buf, 1); #endif /* ! ENV_IS_EMBEDDED */ } #endif /* CONFIG_ENV_OFFSET_REDUND */ - -#if !defined(ENV_IS_EMBEDDED) -static void use_default() -{ - puts ("*** Warning - bad CRC or NAND, using default environment\n\n"); - set_default_env(); -} -#endif diff --git a/common/env_nowhere.c b/common/env_nowhere.c index ccc068b..75ef78d 100644 --- a/common/env_nowhere.c +++ b/common/env_nowhere.c @@ -1,5 +1,5 @@ /* - * (C) Copyright 2000-2002 + * (C) Copyright 2000-2010 * Wolfgang Denk, DENX Software Engineering, wd@denx.de. * * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com> @@ -35,22 +35,21 @@ env_t *env_ptr = NULL;
extern uchar default_environment[];
- -void env_relocate_spec (void) +void env_relocate_spec(void) { }
-uchar env_get_char_spec (int index) +uchar env_get_char_spec(int index) { return ( *((uchar *)(gd->env_addr + index)) ); }
-/************************************************************************ +/* * Initialize Environment use * * We are still running from ROM, so data use is limited */ -int env_init(void) +int env_init(void) { gd->env_addr = (ulong)&default_environment[0]; gd->env_valid = 0; diff --git a/common/env_nvram.c b/common/env_nvram.c index 7c7cf98..6e90f2b 100644 --- a/common/env_nvram.c +++ b/common/env_nvram.c @@ -1,5 +1,5 @@ /* - * (C) Copyright 2000-2002 + * (C) Copyright 2000-2010 * Wolfgang Denk, DENX Software Engineering, wd@denx.de. * * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com> @@ -44,6 +44,8 @@ #include <command.h> #include <environment.h> #include <linux/stddef.h> +#include <search.h> +#include <errno.h>
DECLARE_GLOBAL_DATA_PTR;
@@ -59,7 +61,7 @@ char * env_name_spec = "NVRAM";
extern uchar default_environment[];
-uchar env_get_char_spec (int index) +uchar env_get_char_spec(int index) { #ifdef CONFIG_SYS_NVRAM_ACCESS_ROUTINE uchar c; @@ -72,40 +74,56 @@ uchar env_get_char_spec (int index) #endif }
-void env_relocate_spec (void) +void env_relocate_spec(void) { + char buf[CONFIG_ENV_SIZE]; + #if defined(CONFIG_SYS_NVRAM_ACCESS_ROUTINE) - nvram_read(env_ptr, CONFIG_ENV_ADDR, CONFIG_ENV_SIZE); + nvram_read(buf, CONFIG_ENV_ADDR, CONFIG_ENV_SIZE); #else - memcpy (env_ptr, (void*)CONFIG_ENV_ADDR, CONFIG_ENV_SIZE); + memcpy(buf, (void*)CONFIG_ENV_ADDR, CONFIG_ENV_SIZE); #endif + env_import(buf, 1); }
-int saveenv (void) +int saveenv(void) { - int rcode = 0; + env_t env_new; + ssize_t len; + char *res; + int rcode = 0; + + res = (char *)&env_new.data; + len = hexport('\0', &res, ENV_SIZE); + if (len < 0) { + error("Cannot export environment: errno = %d\n", errno); + return 1; + } + env_new.crc = crc32(0, env_new.data, ENV_SIZE); + #ifdef CONFIG_SYS_NVRAM_ACCESS_ROUTINE - nvram_write(CONFIG_ENV_ADDR, env_ptr, CONFIG_ENV_SIZE); + nvram_write(CONFIG_ENV_ADDR, &env_new, CONFIG_ENV_SIZE); #else - if (memcpy ((char *)CONFIG_ENV_ADDR, env_ptr, CONFIG_ENV_SIZE) == NULL) - rcode = 1 ; + if (memcpy((char *)CONFIG_ENV_ADDR, &env_new, CONFIG_ENV_SIZE) == NULL) + rcode = 1; #endif return rcode; }
-/************************************************************************ +/* * Initialize Environment use * * We are still running from ROM, so data use is limited */ -int env_init (void) +int env_init(void) { #if defined(CONFIG_SYS_NVRAM_ACCESS_ROUTINE) ulong crc; uchar data[ENV_SIZE]; - nvram_read (&crc, CONFIG_ENV_ADDR, sizeof(ulong)); - nvram_read (data, CONFIG_ENV_ADDR+sizeof(ulong), ENV_SIZE); + + nvram_read(&crc, CONFIG_ENV_ADDR, sizeof(ulong)); + nvram_read(data, CONFIG_ENV_ADDR+sizeof(ulong), ENV_SIZE);
if (crc32(0, data, ENV_SIZE) == crc) { gd->env_addr = (ulong)CONFIG_ENV_ADDR + sizeof(long); diff --git a/common/env_onenand.c b/common/env_onenand.c index cf997bf..02cb535 100644 --- a/common/env_onenand.c +++ b/common/env_onenand.c @@ -1,4 +1,7 @@ /* + * (C) Copyright 2010 DENX Software Engineering + * Wolfgang Denk wd@denx.de + * * (C) Copyright 2005-2009 Samsung Electronics * Kyungmin Park kyungmin.park@samsung.com * @@ -26,6 +29,8 @@ #include <environment.h> #include <linux/stddef.h> #include <malloc.h> +#include <search.h> +#include <errno.h>
#include <linux/mtd/compat.h> #include <linux/mtd/mtd.h> @@ -44,17 +49,13 @@ char *env_name_spec = "OneNAND";
#ifdef ENV_IS_EMBEDDED extern uchar environment[]; -env_t *env_ptr = (env_t *) (&environment[0]); -#else /* ! ENV_IS_EMBEDDED */ -static unsigned char onenand_env[ONENAND_MAX_ENV_SIZE]; -env_t *env_ptr = (env_t *) onenand_env; #endif /* ENV_IS_EMBEDDED */
DECLARE_GLOBAL_DATA_PTR;
uchar env_get_char_spec(int index) { - return (*((uchar *) (gd->env_addr + index))); + return (*((uchar *)(gd->env_addr + index))); }
void env_relocate_spec(void) @@ -63,48 +64,57 @@ void env_relocate_spec(void) #ifdef CONFIG_ENV_ADDR_FLEX struct onenand_chip *this = &onenand_chip; #endif - loff_t env_addr; - int use_default = 0; + int rc; size_t retlen; +#ifdef ENV_IS_EMBEDDED + char *buf = (char *)&environment[0]; +#else + loff_t env_addr = CONFIG_ENV_ADDR; + char onenand_env[ONENAND_MAX_ENV_SIZE]; + char *buf = (char *)&onenand_env[0]; +#endif /* ENV_IS_EMBEDDED */
- env_addr = CONFIG_ENV_ADDR; -#ifdef CONFIG_ENV_ADDR_FLEX +#ifndef ENV_IS_EMBEDDED +# ifdef CONFIG_ENV_ADDR_FLEX if (FLEXONENAND(this)) env_addr = CONFIG_ENV_ADDR_FLEX; -#endif +# endif /* Check OneNAND exist */ if (mtd->writesize) /* Ignore read fail */ mtd->read(mtd, env_addr, ONENAND_MAX_ENV_SIZE, - &retlen, (u_char *) env_ptr); + &retlen, (u_char *)buf); else mtd->writesize = MAX_ONENAND_PAGESIZE; +#endif /* !ENV_IS_EMBEDDED */
- if (crc32(0, env_ptr->data, ONENAND_ENV_SIZE(mtd)) != env_ptr->crc) - use_default = 1; - - if (use_default) { - memcpy(env_ptr->data, default_environment, - ONENAND_ENV_SIZE(mtd)); - env_ptr->crc = - crc32(0, env_ptr->data, ONENAND_ENV_SIZE(mtd)); - } - - gd->env_addr = (ulong) & env_ptr->data; - gd->env_valid = 1; + rc = env_import(buf, 1); + if (rc) + gd->env_valid = 1; }
int saveenv(void) { + env_t env_new; + ssize_t len; + char *res; struct mtd_info *mtd = &onenand_mtd; #ifdef CONFIG_ENV_ADDR_FLEX struct onenand_chip *this = &onenand_chip; #endif - loff_t env_addr = CONFIG_ENV_ADDR; + loff_t env_addr = CONFIG_ENV_ADDR; + size_t retlen; struct erase_info instr = { .callback = NULL, }; - size_t retlen; + + res = (char *)&env_new.data; + len = hexport('\0', &res, ENV_SIZE); + if (len < 0) { + error("Cannot export environment: errno = %d\n", errno); + return 1; + } + env_new.crc = crc32(0, env_new.data, ENV_SIZE);
instr.len = CONFIG_ENV_SIZE; #ifdef CONFIG_ENV_ADDR_FLEX @@ -122,11 +132,8 @@ int saveenv(void) return 1; }
- /* update crc */ - env_ptr->crc = crc32(0, env_ptr->data, ONENAND_ENV_SIZE(mtd)); - if (mtd->write(mtd, env_addr, ONENAND_MAX_ENV_SIZE, &retlen, - (u_char *) env_ptr)) { + (u_char *)&env_new)) { printf("OneNAND: write failed at 0x%llx\n", instr.addr); return 2; } diff --git a/common/env_sf.c b/common/env_sf.c index 4391d61..fb0c39b 100644 --- a/common/env_sf.c +++ b/common/env_sf.c @@ -1,5 +1,5 @@ /* - * (C) Copyright 2000-2002 + * (C) Copyright 2000-2010 * Wolfgang Denk, DENX Software Engineering, wd@denx.de. * * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com> @@ -29,6 +29,8 @@ #include <environment.h> #include <malloc.h> #include <spi_flash.h> +#include <search.h> +#include <errno.h>
#ifndef CONFIG_ENV_SPI_BUS # define CONFIG_ENV_SPI_BUS 0 @@ -77,17 +79,29 @@ void swap_env(void)
int saveenv(void) { - u32 saved_size, saved_offset; - char *saved_buffer = NULL; - u32 sector = 1; - int ret; - char flag = OBSOLETE_FLAG, new_flag = ACTIVE_FLAG; + env_t env_new; + ssize_t len; + char *res; + u32 saved_size, saved_offset; + char *saved_buffer = NULL; + u32 sector = 1; + int ret; + char flag = OBSOLETE_FLAG, new_flag = ACTIVE_FLAG;
if (!env_flash) { puts("Environment SPI flash not initialized\n"); return 1; }
+ res = (char *)&env_new.data; + len = hexport('\0', &res, ENV_SIZE); + if (len < 0) { + error("Cannot export environment: errno = %d\n", errno); + return 1; + } + env_new.crc = crc32(0, env_new.data, ENV_SIZE); + env_new.flags = ACTIVE_FLAG; + /* Is the sector larger than the env (i.e. embedded) */ if (CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE) { saved_size = CONFIG_ENV_SECT_SIZE - CONFIG_ENV_SIZE; @@ -118,25 +132,25 @@ int saveenv(void) puts("Writing to SPI flash..."); ret = spi_flash_write(env_flash, env_new_offset + offsetof(env_t, data), - sizeof(env_ptr->data), env_ptr->data); + sizeof(env_new.data), env_new.data); if (ret) goto done;
ret = spi_flash_write(env_flash, env_new_offset + offsetof(env_t, crc), - sizeof(env_ptr->crc), &env_ptr->crc); + sizeof(env_new.crc), &env_new.crc); if (ret) goto done;
ret = spi_flash_write(env_flash, env_offset + offsetof(env_t, flags), - sizeof(env_ptr->flags), &flag); + sizeof(env_new.flags), &flag); if (ret) goto done;
ret = spi_flash_write(env_flash, env_new_offset + offsetof(env_t, flags), - sizeof(env_ptr->flags), &new_flag); + sizeof(env_new.flags), &new_flag); if (ret) goto done;
@@ -164,33 +178,34 @@ void env_relocate_spec(void) int crc1_ok = 0, crc2_ok = 0; env_t *tmp_env1 = NULL; env_t *tmp_env2 = NULL; + env_t ep; uchar flag1, flag2; /* current_env is set only in case both areas are valid! */ int current_env = 0;
tmp_env1 = (env_t *)malloc(CONFIG_ENV_SIZE); - if (!tmp_env1) { - puts("*** Warning: could not init environment," - " using defaults\n\n"); - goto out; - } - tmp_env2 = (env_t *)malloc(CONFIG_ENV_SIZE); - if (!tmp_env2) { - puts("*** Warning: could not init environment," - " using defaults\n\n"); - goto out; + + if (!tmp_env1 || !tmp_env2) { + free(tmp_env1); + free(tmp_env2); + set_default_env("!malloc() failed"); + return; }
env_flash = spi_flash_probe(CONFIG_ENV_SPI_BUS, CONFIG_ENV_SPI_CS, CONFIG_ENV_SPI_MAX_HZ, CONFIG_ENV_SPI_MODE); - if (!env_flash) - goto err_probe; + if (!env_flash) { + set_default_env("!spi_flash_probe() failed"); + return; + }
ret = spi_flash_read(env_flash, CONFIG_ENV_OFFSET, CONFIG_ENV_SIZE, tmp_env1); - if (ret) + if (ret) { + set_default_env("!spi_flash_read() failed"); goto err_read; + }
if (crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc) crc1_ok = 1; @@ -208,25 +223,25 @@ void env_relocate_spec(void) goto err_crc; else if (crc1_ok && !crc2_ok) { gd->env_valid = 1; - memcpy(env_ptr, tmp_env1, CONFIG_ENV_SIZE); + ep = tmp_env1; } else if (!crc1_ok && crc2_ok) { gd->env_valid = 1; - memcpy(env_ptr, tmp_env2, CONFIG_ENV_SIZE); + ep = tmp_env2; swap_env(); } else if (flag1 == ACTIVE_FLAG && flag2 == OBSOLETE_FLAG) { gd->env_valid = 1; - memcpy(env_ptr, tmp_env1, CONFIG_ENV_SIZE); + ep = tmp_env1; } else if (flag1 == OBSOLETE_FLAG && flag2 == ACTIVE_FLAG) { gd->env_valid = 1; - memcpy(env_ptr, tmp_env2, CONFIG_ENV_SIZE); + ep = tmp_env2; swap_env(); } else if (flag1 == flag2) { gd->env_valid = 2; - memcpy(env_ptr, tmp_env1, CONFIG_ENV_SIZE); + ep = tmp_env1; current_env = 1; } else if (flag1 == 0xFF) { gd->env_valid = 2; - memcpy(env_ptr, tmp_env1, CONFIG_ENV_SIZE); + ep = tmp_env1; current_env = 1; } else { /* @@ -234,35 +249,42 @@ void env_relocate_spec(void) * default path is desirable. */ gd->env_valid = 2; - memcpy(env_ptr, tmp_env2, CONFIG_ENV_SIZE); + ep = tmp_env2; swap_env(); current_env = 2; } + + rc = env_import((char *)ep, 0); + if (!rc) { + error("Cannot import environment: errno = %d\n", errno); + goto out; + } + if (current_env == 1) { if (flag2 != OBSOLETE_FLAG) { flag2 = OBSOLETE_FLAG; spi_flash_write(env_flash, env_new_offset + offsetof(env_t, flags), - sizeof(env_ptr->flags), &flag2); + sizeof(env_new.flags), &flag2); } if (flag1 != ACTIVE_FLAG) { flag1 = ACTIVE_FLAG; spi_flash_write(env_flash, env_offset + offsetof(env_t, flags), - sizeof(env_ptr->flags), &flag1); + sizeof(env_new.flags), &flag1); } } else if (current_env == 2) { if (flag1 != OBSOLETE_FLAG) { flag1 = OBSOLETE_FLAG; spi_flash_write(env_flash, env_new_offset + offsetof(env_t, flags), - sizeof(env_ptr->flags), &flag1); + sizeof(env_new.flags), &flag1); } if (flag2 != ACTIVE_FLAG) { flag2 = ACTIVE_FLAG; spi_flash_write(env_flash, env_offset + offsetof(env_t, flags), - sizeof(env_ptr->flags), &flag2); + sizeof(env_new.flags), &flag2); } } if (gd->env_valid == 2) { @@ -278,15 +300,9 @@ void env_relocate_spec(void) err_read: spi_flash_free(env_flash); env_flash = NULL; -err_probe: -err_crc: - puts("*** Warning - bad CRC, using default environment\n\n"); out: - if (tmp_env1) - free(tmp_env1); - if (tmp_env2) - free(tmp_env2); - set_default_env(); + free(tmp_env1); + free(tmp_env2); } #else int saveenv(void) @@ -348,32 +364,30 @@ int saveenv(void)
void env_relocate_spec(void) { + char buf[CONFIG_ENV_SIZE]; int ret;
env_flash = spi_flash_probe(CONFIG_ENV_SPI_BUS, CONFIG_ENV_SPI_CS, CONFIG_ENV_SPI_MAX_HZ, CONFIG_ENV_SPI_MODE); - if (!env_flash) - goto err_probe; - - ret = spi_flash_read(env_flash, CONFIG_ENV_OFFSET, CONFIG_ENV_SIZE, env_ptr); - if (ret) - goto err_read; - - if (crc32(0, env_ptr->data, ENV_SIZE) != env_ptr->crc) - goto err_crc; + if (!env_flash) { + set_default_env("!spi_flash_probe() failed"); + return; + }
- gd->env_valid = 1; + ret = spi_flash_read(env_flash, + CONFIG_ENV_OFFSET, CONFIG_ENV_SIZE, buf); + if (ret) { + set_default_env("!spi_flash_read() failed"); + goto out; + }
- return; + ret = env_import(buf, 1);
-err_read: + if (ret) + gd->env_valid = 1; +out: spi_flash_free(env_flash); env_flash = NULL; -err_probe: -err_crc: - puts("*** Warning - bad CRC, using default environment\n\n"); - - set_default_env(); } #endif
diff --git a/common/exports.c b/common/exports.c index bde52a6..3dff735 100644 --- a/common/exports.c +++ b/common/exports.c @@ -6,7 +6,6 @@ DECLARE_GLOBAL_DATA_PTR; __attribute__((unused)) static void dummy(void) { } -#endif
unsigned long get_version(void) { diff --git a/include/environment.h b/include/environment.h index fbccf6a..bedbc54 100644 --- a/include/environment.h +++ b/include/environment.h @@ -160,6 +160,9 @@ unsigned char env_get_char_memory (int index); void env_crc_update (void);
/* [re]set to the default environment */ -void set_default_env(void); +void set_default_env(const char *s); + +/* Import from binary representation into hash table */ +int env_import(const char *buf, int check);
#endif /* _ENVIRONMENT_H_ */ diff --git a/lib/hashtable.c b/lib/hashtable.c index 2f3b5c8..c573992 100644 --- a/lib/hashtable.c +++ b/lib/hashtable.c @@ -45,6 +45,10 @@ # include <linux/string.h> #endif
+#ifndef CONFIG_ENV_MAX_ENTRIES /* maximum number of entries */ +#define CONFIG_ENV_MAX_ENTRIES 512 +#endif + #include "search.h"
/* @@ -636,15 +640,23 @@ int himport_r(struct hsearch_data *htab, * table size is based on heuristics: in a sample of some 70+ * existing systems we found an average size of 39+ bytes per entry * in the environment (for the whole key=value pair). Assuming a - * size of 7 per entry (= safety factor of >5) should provide enough - * safety margin for any existing environment definitons and still + * size of 8 per entry (= safety factor of ~5) should provide enough + * safety margin for any existing environment definitions and still * allow for more than enough dynamic additions. Note that the * "size" argument is supposed to give the maximum enviroment size - * (CONFIG_ENV_SIZE). + * (CONFIG_ENV_SIZE). This heuristics will result in + * unreasonably large numbers (and thus memory footprint) for + * big flash environments (>8,000 entries for 64 KB + * envrionment size), so we clip it to a reasonable value + * (which can be overwritten in the board config file if + * needed). */
if (!htab->table) { - int nent = size / 7; + int nent = size / 8; + + if (nent > CONFIG_ENV_MAX_ENTRIES) + nent = CONFIG_ENV_MAX_ENTRIES;
debug("Create Hash Table: N=%d\n", nent);
@@ -705,17 +717,19 @@ int himport_r(struct hsearch_data *htab,
hsearch_r(e, ENTER, &rv, htab); if (rv == NULL) { - printf("himport_r: can't insert "%s=%s" into hash table\n", name, value); + printf("himport_r: can't insert "%s=%s" into hash table\n", + name, value); return 0; }
- debug("INSERT: %p ==> name="%s" value="%s"\n", rv, name, - value); - debug(" table = %p, size = %d, filled = %d\n", htab, - htab->size, htab->filled); + debug("INSERT: table %p, filled %d/%d rv %p ==> name="%s" value="%s"\n", + htab, htab->filled, htab->size, + rv, name, value); } while ((dp < data + size) && *dp); /* size check needed for text */ /* without '\0' termination */ + debug("INSERT: free(data = %p)\n", data); free(data);
+ debug("INSERT: done\n"); return 1; /* everything OK */ }

Fix an off-by-one error in the string concatenation for the setevn command which would write over the end of the allocated area, resulting eventually in a crash due to corruption of the malloc metadata.
Signed-off-by: Wolfgang Denk wd@denx.de --- Posted as incremental patch here to provide quick fix to testers (also pushed to "hashtable" branch in u-boot-testing repo. Will be squashed into new env code patch when resubmitting.
common/cmd_nvedit.c | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/common/cmd_nvedit.c b/common/cmd_nvedit.c index 976a30b..010d003 100644 --- a/common/cmd_nvedit.c +++ b/common/cmd_nvedit.c @@ -273,7 +273,7 @@ int _do_env_set (int flag, int argc, char * const argv[]) /* * Insert / replace new value */ - for (i=2,len=1; i<argc; ++i) { + for (i=2,len=0; i<argc; ++i) { len += strlen(argv[i]) + 1; } if ((value = malloc(len)) == NULL) { @@ -285,7 +285,7 @@ int _do_env_set (int flag, int argc, char * const argv[])
while ((*s++ = *v++) != '\0') ; - *s++ = ' '; + *(s-1) = ' '; } if (s != value) *--s = '\0';

On Sat, 17 Jul 2010 21:45:48 +0200 Wolfgang Denk wd@denx.de wrote:
- It would be nice if we could add wildcard support for environment variables; this is needed for variable name auto-completion, but it would also be nice to be able to say "printenv ip*" or "printenv *addr*"
you were right - a grepenv/findenv/'env search' substring implementation on top of this looks to be at least as expensive as a full export operation :/
int var_complete(int argc, char * const argv[], char last_char, int maxv, char *cmdv[]) { +#if 0 /* need to reimplement */
ouch - this is u-boot's most useful feature :)
It would be good to know boot time overhead the initial import function makes, esp. in terms of number of boot-time accesses to the environment...
Kim

Dear Kim Phillips,
In message 20100719193825.120ebf29.kim.phillips@freescale.com you wrote:
- It would be nice if we could add wildcard support for environment variables; this is needed for variable name auto-completion, but it would also be nice to be able to say "printenv ip*" or "printenv *addr*"
you were right - a grepenv/findenv/'env search' substring implementation on top of this looks to be at least as expensive as a full export operation :/
Expensive in terms of what? Code size? Probably not. It all boils down to running strstr() over all entries...
int var_complete(int argc, char * const argv[], char last_char, int maxv, char *cmdv[]) { +#if 0 /* need to reimplement */
ouch - this is u-boot's most useful feature :)
He. We should probably run a poll for UMUF :-)
It would be good to know boot time overhead the initial import function makes, esp. in terms of number of boot-time accesses to the environment...
I don't see any significant changes on the systems I tested...
Example TQM5200:
MPC5200 at 400 MHz, 16 KiB environment size, ~2 KiB used:
relevant env settings:
nfsargs=setenv bootargs root=/dev/nfs rw nfsroot=${serverip}:${rootpath} addip=setenv bootargs ${bootargs} ip=${ipaddr}:${serverip}:${gatewayip}:${netmask}:${hostname}:${netdev}:off panic=1 addcons=setenv bootargs ${bootargs} console=${console},${baudrate} net_nfs=tftp ${kernel_addr_r} ${bootfile}; tftp ${fdt_addr_r} ${fdt_file}; run nfsargs addip addcons; bootm ${kernel_addr_r} - ${fdt_addr_r} bootcmd=run net_nfs
old: U-Boot 2010.03-00167-g6e5fb4e (Apr 27 2010 - 15:37:15) new: U-Boot 2010.06-00208-g7151bce-dirty (Jul 18 2010 - 00:36:48)
old new 0.000 0.000 U-Boot 2010.03-00167-g6e5fb4e (Apr 27 2010 - 15:37:15) 0.000 0.000 0.000 0.000 CPU: MPC5200B v2.2, Core v1.4 at 396 MHz 0.000 0.000 Bus 132 MHz, IPB 132 MHz, PCI 66 MHz 0.040 0.040 Board: TQM5200S (TQ-Components GmbH) 0.040 0.040 on a STK52xx carrier board 0.040 0.040 I2C: 85 kHz, ready 0.040 0.040 DRAM: 64 MB 0.080 0.080 FLASH: 32 MB 0.120 0.130 In: serial 0.120 0.130 Out: serial 0.120 0.130 Err: serial 0.160 0.130 Net: FEC ETHERNET 0.420 0.400 POST i2c PASSED 0.440 0.440 POST cpu PASSED 0.920 0.920 IDE: Bus 0: OK 0.920 0.920 Device 0: Model: HITACHI_DK23DA-20 Firm: 00J2A0A1 Ser#: 12Y0MN 0.920 0.920 Type: Hard Disk 0.920 0.920 Capacity: 19077.1 MB = 18.6 GB (39070080 x 512) 2.840 2.871 Device 1: not available 2.840 2.871 SRAM: 512 kB 4.840 4.860 PS/2: No device found 5.840 5.880 Kbd: reset failed, no ACK 5.880 5.880 5.880 5.880 Type run flash_nfs to mount root filesystem over NFS 5.880 5.880 10.880 10.880 Hit any key to stop autoboot: 0 12.520 12.480 Using FEC ETHERNET device ... 13.480 13.361 Bytes transferred = 7275 (1c6b hex) 13.640 13.400 ## Booting kernel from Legacy Image at 00400000 ... 13.640 13.400 Image Name: Linux-2.6.32-rc5-01449-g2c33dca ... 13.960 13.720 ## Flattened Device Tree blob at 00600000 13.960 13.720 Booting using the fdt blob at 0x600000 14.960 13.720 Uncompressing Kernel Image ... OK 15.040 14.760 [ 0.000000] Using mpc5200-simple-platform machine description 15.040 14.760 [ 0.000000] Linux version 2.6.32-rc5-01449-g2c33dca (wd@pollux.denx.de) (gcc version 4.2.2) #1 Mon Nov 2 09:31:00 CET 2009 ...
As you can see, there are stages where the new code is a bit slower (20...40 milliseconds, which is close to the resolution of the measurement), but in this specific test we win some 280 milliseconds; if you exclude the 5 seconds boot delay that's some 2.7%. I don't think it's worth mentioning, but at least it's not worse than the old code.
I haven't tested many boards, especially not for timing. If you have additional input it will be welcome.
Best regards,
Wolfgang Denk

On Tue, 20 Jul 2010 11:40:23 +0200 Wolfgang Denk wd@denx.de wrote:
Dear Kim Phillips,
In message 20100719193825.120ebf29.kim.phillips@freescale.com you wrote:
- It would be nice if we could add wildcard support for environment variables; this is needed for variable name auto-completion, but it would also be nice to be able to say "printenv ip*" or "printenv *addr*"
you were right - a grepenv/findenv/'env search' substring implementation on top of this looks to be at least as expensive as a full export operation :/
Expensive in terms of what? Code size? Probably not. It all boils down to running strstr() over all entries...
time I thought, but ok, I'll look into it when I have time.
As you can see, there are stages where the new code is a bit slower (20...40 milliseconds, which is close to the resolution of the measurement), but in this specific test we win some 280 milliseconds; if you exclude the 5 seconds boot delay that's some 2.7%. I don't think it's worth mentioning, but at least it's not worse than the old code.
excellent, thank you.
I haven't tested many boards, especially not for timing. If you have additional input it will be welcome.
I would, however u-boot-testing.git/hashtable currently fails to boot on an 8572:
U-Boot 2010.06-02552-g9895ea5 (Jul 19 2010 - 18:39:19)
CPU0: 8572E, Version: 1.1, (0x80e80011) Core: E500, Version: 3.0, (0x80210030) Clock Configuration: CPU0:1499.985 MHz, CPU1:1499.985 MHz, CCB:599.994 MHz, DDR:399.996 MHz (799.992 MT/s data rate) (Asynchronous), LBC:37.500 MHz L1: D-cache 32 kB enabled I-cache 32 kB enabled Board: MPC8572DS Sys ID: 0x14, Sys Ver: 0x30, FPGA Ver: 0x10, vBank: 0 I2C: ready DRAM: Initializing....
I presume it's related to the issue brought up in this thread?:
http://lists.denx.de/pipermail/u-boot/2010-July/074151.html
Cheers,
Kim

Dear Kim Phillips,
In message 20100720133648.cbb95a9f.kim.phillips@freescale.com you wrote:
I would, however u-boot-testing.git/hashtable currently fails to boot on an 8572:
Hm... one of the boards I don't have access to; can you please help debug the problem?
U-Boot 2010.06-02552-g9895ea5 (Jul 19 2010 - 18:39:19)
CPU0: 8572E, Version: 1.1, (0x80e80011) Core: E500, Version: 3.0, (0x80210030) Clock Configuration: CPU0:1499.985 MHz, CPU1:1499.985 MHz, CCB:599.994 MHz, DDR:399.996 MHz (799.992 MT/s data rate) (Asynchronous), LBC:37.500 MHz L1: D-cache 32 kB enabled I-cache 32 kB enabled Board: MPC8572DS Sys ID: 0x14, Sys Ver: 0x30, FPGA Ver: 0x10, vBank: 0 I2C: ready DRAM: Initializing....
I presume it's related to the issue brought up in this thread?:
Hm... I don't think so, but as it's failing there must be some bug somewhere... My thinking is that all the relevant environment handling that actually uses errno is related to the hash table handling, and all this runs definitely only after relocation to RAM, so this should not be the problem.
What we see here is in the pre-relocation phase, where we are still parsing the original, in-flash copy of the stringified environment. What is strange is that this seems to have worked when accessing "baudrate" (either because the environment was found, or because the default environment was used), but is stopping here.
Can you attach a debugger and check?
Best regards,
Wolfgang Denk

Dear Kim,
In message 20100720190112.3F435153A7F@gemini.denx.de I wrote:
What we see here is in the pre-relocation phase, where we are still parsing the original, in-flash copy of the stringified environment. What is strange is that this seems to have worked when accessing "baudrate" (either because the environment was found, or because the default environment was used), but is stopping here.
I think the problem is that before relocation we have to use getenv_r(), which is done for example to read the baudrate, see arch/powerpc/lib/board.c: init_baudrate()
However, some boards also use getenv() before relocation. I just found for example that the Sequoia board does this (before relocation):
"board/amcc/sequoia/sequoia.c":
330 int checkboard(void) 331 { 332 char *s = getenv("serial#"); 333 u8 rev; 334 u32 clock = get_async_pci_freq(); 335 336 #ifdef CONFIG_440EPX 337 printf("Board: Sequoia - AMCC PPC440EPx Evaluation Board"); 338 #else 339 printf("Board: Rainier - AMCC PPC440GRx Evaluation Board"); 340 #endif ...
and as one can expect the board crashes before printing the board information.
I'm considering a way to eliminate getenv_r() (at the cost of providing some static buffer in the global data area?).
[Comments? Ideas?]
However, I cannot see any gentenv() use before relocation for your board, so the problem there is still unclear. I think I need your help to debug this...
Best regards,
Wolfgang Denk

On Tue, 20 Jul 2010 22:09:08 +0200 Wolfgang Denk wd@denx.de wrote:
However, I cannot see any gentenv() use before relocation for your board, so the problem there is still unclear. I think I need your help to debug this...
looks like arch/powerpc/cpu/mpc8xxx/ddr/options.c gets memory interleaving configuration from the environment, using getenv().
Kim

Dear Kim,
In message 20100720160808.b7ecf34c.kim.phillips@freescale.com you wrote:
However, I cannot see any gentenv() use before relocation for your board, so the problem there is still unclear. I think I need your help to debug this...
looks like arch/powerpc/cpu/mpc8xxx/ddr/options.c gets memory interleaving configuration from the environment, using getenv().
Ah! Can you please try the following patch - it's not intended to be a fix, just to verify my hypothesis. Thanks in advance.
diff --git a/arch/powerpc/cpu/mpc8xxx/ddr/options.c b/arch/powerpc/cpu/mpc8xxx/ddr/options.c index 46731c8..0b9cb1d 100644 --- a/arch/powerpc/cpu/mpc8xxx/ddr/options.c +++ b/arch/powerpc/cpu/mpc8xxx/ddr/options.c @@ -24,6 +24,7 @@ unsigned int populate_memctl_options(int all_DIMMs_registered, { unsigned int i; const char *p; + char tmp[64];
/* Chip select options. */
@@ -221,7 +222,8 @@ unsigned int populate_memctl_options(int all_DIMMs_registered, * should be a subset of the requested configuration. */ #if (CONFIG_NUM_DDR_CONTROLLERS > 1) - if ((p = getenv("memctl_intlv_ctl")) != NULL) { + i = getenv_r("memctl_intlv_ctl", tmp, sizeof (tmp)); + if (i > 0) { if (pdimm[0].n_ranks == 0) { printf("There is no rank on CS0. Because only rank on " "CS0 and ranks chip-select interleaved with CS0" @@ -230,37 +232,38 @@ unsigned int populate_memctl_options(int all_DIMMs_registered, popts->memctl_interleaving = 0; } else { popts->memctl_interleaving = 1; - if (strcmp(p, "cacheline") == 0) + if (strcmp(tmp, "cacheline") == 0) popts->memctl_interleaving_mode = FSL_DDR_CACHE_LINE_INTERLEAVING; - else if (strcmp(p, "page") == 0) + else if (strcmp(tmp, "page") == 0) popts->memctl_interleaving_mode = FSL_DDR_PAGE_INTERLEAVING; - else if (strcmp(p, "bank") == 0) + else if (strcmp(tmp, "bank") == 0) popts->memctl_interleaving_mode = FSL_DDR_BANK_INTERLEAVING; - else if (strcmp(p, "superbank") == 0) + else if (strcmp(tmp, "superbank") == 0) popts->memctl_interleaving_mode = FSL_DDR_SUPERBANK_INTERLEAVING; else popts->memctl_interleaving_mode = - simple_strtoul(p, NULL, 0); + simple_strtoul(tmp, NULL, 0); } } #endif
- if( ((p = getenv("ba_intlv_ctl")) != NULL) && + i = getenv_r("ba_intlv_ctl", tmp, sizeof (tmp)); + if (i > 0) && (CONFIG_CHIP_SELECTS_PER_CTRL > 1)) { (CONFIG_CHIP_SELECTS_PER_CTRL > 1)) { - if (strcmp(p, "cs0_cs1") == 0) + if (strcmp(tmp, "cs0_cs1") == 0) popts->ba_intlv_ctl = FSL_DDR_CS0_CS1; - else if (strcmp(p, "cs2_cs3") == 0) + else if (strcmp(tmp, "cs2_cs3") == 0) popts->ba_intlv_ctl = FSL_DDR_CS2_CS3; - else if (strcmp(p, "cs0_cs1_and_cs2_cs3") == 0) + else if (strcmp(tmp, "cs0_cs1_and_cs2_cs3") == 0) popts->ba_intlv_ctl = FSL_DDR_CS0_CS1_AND_CS2_CS3; - else if (strcmp(p, "cs0_cs1_cs2_cs3") == 0) + else if (strcmp(tmp, "cs0_cs1_cs2_cs3") == 0) popts->ba_intlv_ctl = FSL_DDR_CS0_CS1_CS2_CS3; else - popts->ba_intlv_ctl = simple_strtoul(p, NULL, 0); + popts->ba_intlv_ctl = simple_strtoul(tmp, NULL, 0);
switch (popts->ba_intlv_ctl & FSL_DDR_CS0_CS1_CS2_CS3) { case FSL_DDR_CS0_CS1_CS2_CS3:
Best regards,
Wolfgang Denk

On Tue, 20 Jul 2010 23:43:14 +0200 Wolfgang Denk wd@denx.de wrote:
Dear Kim,
In message 20100720160808.b7ecf34c.kim.phillips@freescale.com you wrote:
However, I cannot see any gentenv() use before relocation for your board, so the problem there is still unclear. I think I need your help to debug this...
looks like arch/powerpc/cpu/mpc8xxx/ddr/options.c gets memory interleaving configuration from the environment, using getenv().
Ah! Can you please try the following patch - it's not intended to be a fix, just to verify my hypothesis. Thanks in advance.
indeed, your patch plus the following makes the 8572 boot properly again.
diff --git a/arch/powerpc/cpu/mpc8xxx/ddr/options.c b/arch/powerpc/cpu/mpc8xxx/ddr/options.c index 0b9cb1d..692669f 100644 --- a/arch/powerpc/cpu/mpc8xxx/ddr/options.c +++ b/arch/powerpc/cpu/mpc8xxx/ddr/options.c @@ -252,8 +252,7 @@ unsigned int populate_memctl_options(int all_DIMMs_registered, #endif
i = getenv_r("ba_intlv_ctl", tmp, sizeof (tmp)); - if (i > 0) && (CONFIG_CHIP_SELECTS_PER_CTRL > 1)) { - (CONFIG_CHIP_SELECTS_PER_CTRL > 1)) { + if ((i > 0) && (CONFIG_CHIP_SELECTS_PER_CTRL > 1)) { if (strcmp(tmp, "cs0_cs1") == 0) popts->ba_intlv_ctl = FSL_DDR_CS0_CS1; else if (strcmp(tmp, "cs2_cs3") == 0)
Kim

Dear Kim,
In message 20100720170026.fee7f593.kim.phillips@freescale.com you wrote:
Ah! Can you please try the following patch - it's not intended to be a fix, just to verify my hypothesis. Thanks in advance.
indeed, your patch plus the following makes the 8572 boot properly again.
Thanks for testing.
Could you please also test the code from the current (rebased) version in the "hashtable" branch of the git://git.denx.de/u-boot-testing.git repo?
I have adresses the problem of early environment accesses, and at least on the boards I've tested so far it appears to be working.
Best regards,
Wolfgang Denk

On Sun, 25 Jul 2010 23:45:02 +0200 Wolfgang Denk wd@denx.de wrote:
Could you please also test the code from the current (rebased) version in the "hashtable" branch of the git://git.denx.de/u-boot-testing.git repo?
I have adresses the problem of early environment accesses, and at least on the boards I've tested so far it appears to be working.
U-Boot 2010.06-02590-g38045a3 ("4xx: Make sequoia board build with new environment code") runs beautifully on the 8572.
Thanks,
Kim

Dear Kim Phillips,
In message 20100720133648.cbb95a9f.kim.phillips@freescale.com you wrote:
I would, however u-boot-testing.git/hashtable currently fails to boot on an 8572:
...
I presume it's related to the issue brought up in this thread?:
I smell a wumpus. Don't spend time on debugging this, let me track down an idea first, please.
Best regards,
Wolfgang Denk

Hi Wolfgang,
I could think of some situations where the new env command is helpful. But more during development than for production systems.
Switching between environment profiles would be cool. And a "env default -f" behavior that keeps MAC addresses and serial# is also on my wishlist.
I did some testing on our PMC440 with environment in EEPROM. Please see some comments below.
On Saturday 17 July 2010 21:45, Wolfgang Denk wrote:
diff --git a/common/cmd_nvedit.c b/common/cmd_nvedit.c index 8c86f15..976a30b 100644 --- a/common/cmd_nvedit.c +++ b/common/cmd_nvedit.c
...
+/*
- env export [-t | -b | -c] addr [size]
- -t: export as text format; if size is given, data will be
padded with '\0' bytes; if not, one terminating '\0'
will be added (which is included in the "filesize"
setting so you can for exmple copy this to flash and
keep the termination).
- -b: export as binary format (name=value pairs separated by
'\0', list end marked by double "\0\0")
- -c: export as checksum protected environment format as
used for example by "saveenv" command
- addr: memory address where environment gets stored
- size: size of output buffer
...
+static int do_env_export(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{
- char buf[32];
- char *addr, *cmd, *res;
- size_t size;
- ssize_t len;
- env_t *envp = (env_t *)addr;
addr is uninitialized. declaration is enough here.
- char sep = '\n';
- int chk = 0;
- int fmt = 0;
- cmd = *argv;
- while (--argc > 0 && **++argv == '-') {
I'd like some more braces for readability.
char *arg = *argv;
while (*++arg) {
switch (*arg) {
case 'b': /* raw binary format */
if (fmt++)
goto sep_err;
sep = '\0';
break;
case 'c': /* external checksum format */
if (fmt++)
goto sep_err;
sep = '\0';
chk = 1;
break;
case 't': /* text format */
if (fmt++)
goto sep_err;
sep = '\n';
break;
default:
cmd_usage(cmdtp);
return 1;
}
}
- }
-/**************************************************/
- if (argc < 1) {
cmd_usage(cmdtp);
return 1;
- }
- addr = (char *)simple_strtoul(argv[0], NULL, 16);
- if (argc == 2) {
size = simple_strtoul(argv[1], NULL, 16);
memset(addr, '\0', size);
- } else {
size = 0;
- }
- if (sep) { /* export as text file */
len = hexport(sep, &addr, size);
if (len < 0) {
error("Cannot export environment: errno = %d\n",
errno);
return 1;
}
sprintf(buf, "%zX", len);
setenv("filesize", buf);
return 0;
- }
- if (chk) { /* export as checksum protected block */
Add: envp = (env_t *)addr;
res = (char *)&envp->data;
- } else { /* export as raw binary data */
res = (char *)&addr;
Should'n this be res = addr;
- }
- len = hexport('\0', &res, ENV_SIZE);
- if (len < 0) {
error("Cannot export environment: errno = %d\n",
errno);
return 1;
- }
- if (chk) {
envp->crc = crc32(0, envp->data, ENV_SIZE);
+#ifdef CONFIG_ENV_ADDR_REDUND
envp->flags = ACTIVE_FLAG;
+#endif
- }
- sprintf(buf, "%zX", len + offsetof(env_t,data));
- setenv("filesize", buf);
- return 0;
+sep_err:
- printf("## %s: only one of "-b", "-c" or "-t" allowed\n",
cmd);
- return 1;
+}
...
Fixes for non-building board (AR405, CANBT, PMC440) will come up shortly.
Matthias

Dear Matthias Fuchs,
In message 201007261652.39368.matthias.fuchs@esd-electronics.com you wrote:
I could think of some situations where the new env command is helpful. But more during development than for production systems.
It depends. "Reset to factory defaults" is a not so uncommon request. And acceleration of scripts is not so uncommon either.
Switching between environment profiles would be cool. And a "env default -f" behavior that keeps MAC addresses and serial# is also on my wishlist.
Actually neither MAC addresses nor serial# are part of the default environment.
I did some testing on our PMC440 with environment in EEPROM. Please see some comments below.
Thanks.
+static int do_env_export(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{
- char buf[32];
- char *addr, *cmd, *res;
- size_t size;
- ssize_t len;
- env_t *envp = (env_t *)addr;
addr is uninitialized. declaration is enough here.
Will check this.
...
- if (chk) { /* export as checksum protected block */
Add: envp = (env_t *)addr;
res = (char *)&envp->data;
- } else { /* export as raw binary data */
res = (char *)&addr;
Should'n this be res = addr;
No. We need the address of the pointer variable, so the function can store the result pointer there.
...
Fixes for non-building board (AR405, CANBT, PMC440) will come up shortly.
Thanks a lot.

Hi Wolfgang,
On Wednesday 28 July 2010 23:17, Wolfgang Denk wrote:
Dear Matthias Fuchs,
In message 201007261652.39368.matthias.fuchs@esd-electronics.com you wrote:
I could think of some situations where the new env command is helpful. But more during development than for production systems.
It depends. "Reset to factory defaults" is a not so uncommon request. And acceleration of scripts is not so uncommon either.
Switching between environment profiles would be cool. And a "env default -f" behavior that keeps MAC addresses and serial# is also on my wishlist.
Actually neither MAC addresses nor serial# are part of the default environment.
Right. But, I'd like to have that described feature in any case. Let's call it different, but the functionality would be helpful. What about a further option like '-p' for keep _p_rotected.
I did some testing on our PMC440 with environment in EEPROM. Please see some comments below.
Thanks.
+static int do_env_export(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{
- char buf[32];
- char *addr, *cmd, *res;
- size_t size;
- ssize_t len;
- env_t *envp = (env_t *)addr;
addr is uninitialized. declaration is enough here.
Will check this.
...
- if (chk) { /* export as checksum protected block */
Add: envp = (env_t *)addr;
res = (char *)&envp->data;
- } else { /* export as raw binary data */
res = (char *)&addr;
Should'n this be res = addr;
No. We need the address of the pointer variable, so the function can store the result pointer there.
Nak. You added &-operator when calling: hexport('\0', &res, ENV_SIZE); That's one to much. You can also remove the &-operator from this: res = (char *)&envp->data;
So the code will look like this:
if (chk) { /* export as checksum protected block */ envp = (env_t *)addr; res = (char *)envp->data; } else { /* export as raw binary data */ res = addr; }
len = hexport('\0', &res, ENV_SIZE); ...
Matthias

Dear Matthias Fuchs,
In message 201007291116.15503.matthias.fuchs@esd-electronics.com you wrote:
Actually neither MAC addresses nor serial# are part of the default environment.
Right. But, I'd like to have that described feature in any case. Let's call it different, but the functionality would be helpful. What about a further option like '-p' for keep _p_rotected.
I see this a s part of a sub-sequent patch (series), which adds flags to variables (like read-only, don't-save, etc.).
No. We need the address of the pointer variable, so the function can store the result pointer there.
Nak. You added &-operator when calling: hexport('\0', &res, ENV_SIZE); That's one to much. You can also remove the &-operator from this: res = (char *)&envp->data;
So the code will look like this:
Thanks, will re-check this when I have a little more time.
Best regards,
Wolfgang Denk

Dear Wolfgang Denk,
In message 1279395948-25864-6-git-send-email-wd@denx.de you wrote:
Motivation:
- Old environment code used a pessimizing implementation:
- variable lookup used linear search => slow
- changed/added variables were added at the end, i. e. most frequently used variables had the slowest access times => slow
- each setenv() would calculate the CRC32 checksum over the whole environment block => slow
- "redundant" envrionment was locked down to two copies
- No easy way to implement features like "reset to factory defaults", or to select one out of several pre-defined (previously saved) sets of environment settings ("profiles")
- No easy way to import or export environment settings
======================================================================
API Changes:
Variable names starting with '#' are no longer allowed
I didn't find any such variable names being used; it is highly recommended to follow standard conventions and start variable names with an alphanumeric character
"printenv" will now print a backslash at the end of all but the last lines of a multi-line variable value.
Multi-line variables have never been formally defined, allthough there is no reason not to use them. Now we define rules how to deal with them, allowing for import and export.
Function forceenv() and the related code in saveenv() was removed. At the moment this is causing build problems for the only user of this code (schmoogie - which has no entry in MAINTAINERS); may be fixed later by implementing the "env set -f" feature.
Inconsistencies:
- "printenv" will '\'-escape the '\n' in multi-line variables, while "printenv var" will not do that.
======================================================================
Advantages:
- "printenv" output much better readable (sorted)
- faster!
- extendable (additional variable properties can be added)
- new, powerful features like "factory reset" or easy switching between several different environment settings ("profiles")
Disadvantages:
- Image size grows by typically 5...7 KiB (might shrink a bit again on systems with redundant environment with a following patch series)
======================================================================
Implemented:
env command with subcommands:
env print [arg ...]
same as "printenv": print environment
env set [-f] name [arg ...]
same as "setenv": set (and delete) environment variables
["-f" - force setting even for read-only variables - not implemented yet.]
end delete [-f] name
not implemented yet
["-f" - force delete even for read-only variables]
env save
same as "saveenv": save environment
env export [-t | -b | -c] addr [size]
export internal representation (hash table) in formats usable for persistent storage or processing:
-t: export as text format; if size is given, data will be padded with '\0' bytes; if not, one terminating '\0' will be added (which is included in the "filesize" setting so you can for exmple copy this to flash and keep the termination). -b: export as binary format (name=value pairs separated by '\0', list end marked by double "\0\0") -c: export as checksum protected environment format as used for example by "saveenv" command addr: memory address where environment gets stored size: size of output buffer
With "-c" and size is NOT given, then the export command will format the data as currently used for the persistent storage, i. e. it will use CONFIG_ENV_SECT_SIZE as output block size and prepend a valid CRC32 checksum and, in case of resundant environment, a "current" redundancy flag. If size is given, this value will be used instead of CONFIG_ENV_SECT_SIZE; again, CRC32 checksum and redundancy flag will be inserted.
With "-b" and "-t", always only the real data (including a terminating '\0' byte) will be written; here the optional size argument will be used to make sure not to overflow the user provided buffer; the command will abort if the size is not sufficient. Any remainign space will be '\0' padded.
On successful return, the variable "filesize" will be set. Note that filesize includes the trailing/terminating '\0' byte(s). Usage szenario: create a text snapshot/backup of the current
settings:
=> env export -t 100000 => era ${backup_addr} +${filesize} => cp.b 100000 ${backup_addr} ${filesize}
Re-import this snapshot, deleting all other settings:
=> env import -d -t ${backup_addr}
env import [-d] [-t | -b | -c] addr [size]
import external format (text or binary) into hash table, optionally deleting existing values:
-d: delete existing environment before importing; otherwise overwrite / append to existion definitions -t: assume text format; either "size" must be given or the text data must be '\0' terminated -b: assume binary format ('\0' separated, "\0\0" terminated) -c: assume checksum protected environment format addr: memory address to read from size: length of input data; if missing, proper '\0' termination is mandatory
env default -f
reset default environment: drop all environment settings and load default environment
env ask name [message] [size]
same as "askenv": ask for environment variable
env edit name
same as "editenv": edit environment variable
env run
same as "run": run commands in an environment variable
======================================================================
TODO:
drop default env as implemented now; provide a text file based initialization instead (eventually using several text files to incrementally build it from common blocks) and a tool to convert it into a binary blob / object file.
It would be nice if we could add wildcard support for environment variables; this is needed for variable name auto-completion, but it would also be nice to be able to say "printenv ip*" or "printenv *addr*"
Some boards don't link any more due to the grown code size: AR405, CANBT, DU405, PMC440, canyonlands, sc3, sequoia, socrates.
=> cc: Matthias Fuchs matthias.fuchs@esd-electronics.com, Stefan Roese sr@denx.de, Heiko Schocher hs@denx.de
Dropping forceenv() causes build problems on schmoogie
=> cc: Sergey Kubushyn ksi@koi8.net
Build tested on PPC and ARM only; runtime tested with NOR and NAND flash only => needs testing!!
Signed-off-by: Wolfgang Denk wd@denx.de Cc: Matthias Fuchs matthias.fuchs@esd-electronics.com, Cc: Stefan Roese sr@denx.de, Cc: Heiko Schocher hs@denx.de Cc: Sergey Kubushyn ksi@koi8.net
README | 8 + board/zeus/zeus.c | 25 +-- common/cmd_nvedit.c | 655 +++++++++++++++++++++++++++++++++--------------- common/command.c | 3 +- common/dlmalloc.c | 25 +- common/env_common.c | 125 +++++----- common/env_dataflash.c | 114 ++++++---- common/env_eeprom.c | 85 ++++--- common/env_flash.c | 245 ++++++++++--------- common/env_mgdisk.c | 33 +-- common/env_nand.c | 188 ++++++++------ common/env_nowhere.c | 11 +- common/env_nvram.c | 46 +++- common/env_onenand.c | 65 +++--- common/env_sf.c | 132 ++++++----- common/exports.c | 1 - include/environment.h | 5 +- lib/hashtable.c | 32 ++- 18 files changed, 1094 insertions(+), 704 deletions(-)
Applied to "next" branch.
[With changes as suggested by Matthias Fuchs - thanks!]
Best regards,
Wolfgang Denk

On Saturday, July 17, 2010 15:45:48 Wolfgang Denk wrote:
--- a/common/env_flash.c +++ b/common/env_flash.c #ifdef CMD_SAVEENV int saveenv(void) {
- char *saved_data = NULL;
- int rc = 1;
- char flag = OBSOLETE_FLAG, new_flag = ACTIVE_FLAG;
- env_t env_new;
- ssize_t len;
- char *saved_data = NULL;
- char *res;
- int rc = 1;
- char flag = OBSOLETE_FLAG, new_flag = ACTIVE_FLAG;
#if CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE
- ulong up_data = 0;
- ulong up_data = 0;
#endif
- debug ("Protect off %08lX ... %08lX\n",
- debug("Protect off %08lX ... %08lX\n", (ulong)flash_addr, end_addr);
- if (flash_sect_protect (0, (ulong)flash_addr, end_addr)) {
goto Done;
- if (flash_sect_protect(0, (ulong)flash_addr, end_addr)) {
}goto done;
- debug ("Protect off %08lX ... %08lX\n",
- debug("Protect off %08lX ... %08lX\n", (ulong)flash_addr_new, end_addr_new);
- if (flash_sect_protect (0, (ulong)flash_addr_new, end_addr_new)) {
goto Done;
- if (flash_sect_protect(0, (ulong)flash_addr_new, end_addr_new)) {
goto done;
- }
- res = (char *)&env_new.data;
- len = hexport('\0', &res, ENV_SIZE);
- if (len < 0) {
error("Cannot export environment: errno = %d\n", errno);
}goto done;
- env_new.crc = crc32(0, env_new.data, ENV_SIZE);
- env_new.flags = new_flag;
#if CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE up_data = (end_addr_new + 1 - ((long)flash_addr_new + CONFIG_ENV_SIZE));
- debug ("Data to save 0x%x\n", up_data);
- debug("Data to save 0x%lX\n", up_data); if (up_data) { if ((saved_data = malloc(up_data)) == NULL) { printf("Unable to save the rest of sector (%ld)\n", up_data);
goto Done;
} memcpy(saved_data, (void *)((long)flash_addr_new + CONFIG_ENV_SIZE), up_data);goto done;
debug ("Data (start 0x%x, len 0x%x) saved at 0x%x\n",
(long)flash_addr_new + CONFIG_ENV_SIZE,
up_data, saved_data);
debug("Data (start 0x%lX, len 0x%lX) saved at 0x%p\n",
(long)flash_addr_new + CONFIG_ENV_SIZE,
}up_data, saved_data);
#endif
- puts ("Erasing Flash...");
- debug (" %08lX ... %08lX ...",
- puts("Erasing Flash...");
- debug(" %08lX ... %08lX ...", (ulong)flash_addr_new, end_addr_new);
- if (flash_sect_erase ((ulong)flash_addr_new, end_addr_new)) {
goto Done;
- if (flash_sect_erase((ulong)flash_addr_new, end_addr_new)) {
}goto done;
- puts ("Writing to Flash... ");
- debug (" %08lX ... %08lX ...",
- puts("Writing to Flash... ");
- debug(" %08lX ... %08lX ...", (ulong)&(flash_addr_new->data), sizeof(env_ptr->data)+(ulong)&(flash_addr_new->data));
- if ((rc = flash_write((char *)env_ptr->data,
(ulong)&(flash_addr_new->data),
sizeof(env_ptr->data))) ||
(rc = flash_write((char *)&(env_ptr->crc),
(ulong)&(flash_addr_new->crc),
sizeof(env_ptr->crc))) ||
- if ((rc = flash_write((char *)&env_new,
(ulong)flash_addr_new,
(rc = flash_write(&flag, (ulong)&(flash_addr->flags),sizeof(env_new))) ||
sizeof(flash_addr->flags))) ||
(rc = flash_write(&new_flag,
(ulong)&(flash_addr_new->flags),
sizeof(flash_addr_new->flags))))
- {
flash_perror (rc);
goto Done;
sizeof(flash_addr->flags))) ) {
flash_perror(rc);
}goto done;
- puts ("done\n");
#if CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE if (up_data) { /* restore the rest of sector */
debug ("Restoring the rest of data to 0x%x len 0x%x\n",
(long)flash_addr_new + CONFIG_ENV_SIZE, up_data);
debug("Restoring the rest of data to 0x%lX len 0x%lX\n",
if (flash_write(saved_data, (long)flash_addr_new + CONFIG_ENV_SIZE, up_data)) { flash_perror(rc);(long)flash_addr_new + CONFIG_ENV_SIZE, up_data);
goto Done;
} }goto done;
#endif
- puts("done\n");
- { env_t * etmp = flash_addr; ulong ltmp = end_addr;
@@ -220,13 +230,12 @@ int saveenv(void) }
rc = 0; -Done:
+done: if (saved_data)
free (saved_data);
/* try to re-protect */free(saved_data);
- (void) flash_sect_protect (1, (ulong)flash_addr, end_addr);
- (void) flash_sect_protect (1, (ulong)flash_addr_new, end_addr_new);
(void) flash_sect_protect(1, (ulong)flash_addr, end_addr);
(void) flash_sect_protect(1, (ulong)flash_addr_new, end_addr_new);
return rc;
} @@ -244,83 +253,93 @@ int env_init(void)
gd->env_addr = (ulong)&default_environment[0]; gd->env_valid = 0;
- return (0);
- return 0;
}
#ifdef CMD_SAVEENV
int saveenv(void) {
- int len, rc;
- ulong end_addr;
- ulong flash_sect_addr;
-#if defined(CONFIG_ENV_SECT_SIZE) && (CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE) - ulong flash_offset;
- uchar env_buffer[CONFIG_ENV_SECT_SIZE];
-#else
- uchar *env_buffer = (uchar *)env_ptr;
-#endif /* CONFIG_ENV_SECT_SIZE */
- int rcode = 0;
-#if defined(CONFIG_ENV_SECT_SIZE) && (CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE) -
- flash_offset = ((ulong)flash_addr) & (CONFIG_ENV_SECT_SIZE-1);
- flash_sect_addr = ((ulong)flash_addr) & ~(CONFIG_ENV_SECT_SIZE-1);
- debug ( "copy old content: "
"sect_addr: %08lX env_addr: %08lX offset: %08lX\n",
flash_sect_addr, (ulong)flash_addr, flash_offset);
- /* copy old contents to temporary buffer */
- memcpy (env_buffer, (void *)flash_sect_addr, CONFIG_ENV_SECT_SIZE);
- /* copy current environment to temporary buffer */
- memcpy ((uchar *)((unsigned long)env_buffer + flash_offset),
env_ptr,
CONFIG_ENV_SIZE);
- env_t env_new;
- ssize_t len;
- int rc = 1;
- char *res;
- char *saved_data = NULL;
+#if CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE
- ulong up_data = 0;
- len = CONFIG_ENV_SECT_SIZE;
-#else
- flash_sect_addr = (ulong)flash_addr;
- len = CONFIG_ENV_SIZE;
- up_data = (end_addr + 1 - ((long)flash_addr + CONFIG_ENV_SIZE));
- debug("Data to save 0x%lx\n", up_data);
- if (up_data) {
if ((saved_data = malloc(up_data)) == NULL) {
printf("Unable to save the rest of sector (%ld)\n",
up_data);
goto done;
}
memcpy(saved_data,
(void *)((long)flash_addr + CONFIG_ENV_SIZE), up_data);
debug("Data (start 0x%lx, len 0x%lx) saved at 0x%lx\n",
(ulong)flash_addr + CONFIG_ENV_SIZE,
up_data,
(ulong)saved_data);
- }
#endif /* CONFIG_ENV_SECT_SIZE */
- end_addr = flash_sect_addr + len - 1;
- debug("Protect off %08lX ... %08lX\n",
(ulong)flash_addr, end_addr);
- debug ("Protect off %08lX ... %08lX\n",
(ulong)flash_sect_addr, end_addr);
- if (flash_sect_protect(0, (long)flash_addr, end_addr))
goto done;
- if (flash_sect_protect (0, flash_sect_addr, end_addr))
return 1;
- res = (char *)&env_new.data;
- len = hexport('\0', &res, ENV_SIZE);
- if (len < 0) {
error("Cannot export environment: errno = %d\n", errno);
goto done;
- }
- env_new.crc = crc32(0, env_new.data, ENV_SIZE);
- puts ("Erasing Flash...");
- if (flash_sect_erase (flash_sect_addr, end_addr))
return 1;
- puts("Erasing Flash...");
- if (flash_sect_erase((long)flash_addr, end_addr))
goto done;
- puts ("Writing to Flash... ");
- rc = flash_write((char *)env_buffer, flash_sect_addr, len);
- puts("Writing to Flash... ");
- rc = flash_write((char *)&env_new, (long)flash_addr, CONFIG_ENV_SIZE); if (rc != 0) {
flash_perror (rc);
rcode = 1;
- } else {
puts ("done\n");
flash_perror(rc);
}goto done;
+#if CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE
- if (up_data) { /* restore the rest of sector */
debug("Restoring the rest of data to 0x%lx len 0x%lx\n",
(ulong)flash_addr + CONFIG_ENV_SIZE, up_data);
if (flash_write(saved_data,
(long)flash_addr + CONFIG_ENV_SIZE,
up_data)) {
flash_perror(rc);
goto done;
}
- }
+#endif
- puts("done\n");
- rc = 0;
+done:
- if (saved_data)
/* try to re-protect */free(saved_data);
- (void) flash_sect_protect (1, flash_sect_addr, end_addr);
- return rcode;
- (void) flash_sect_protect(1, (long)flash_addr, end_addr);
- return rc;
}
it would seem that somewhere nestled in this rewrite, support for embedded env was broken (CONFIG_ENV_SIZE < CONFIG_ENV_SECT_SIZE). on a bf548-ezkit for example, i see this behavior:
(v2010.09 / before this commit): bfin> save Saving Environment to Flash... . done Un-Protected 1 sectors Erasing Flash... . done Erased 1 sectors Writing to Flash... done . done Protected 1 sectors
(after this commit / v2010.12): bfin> save Saving Environment to Flash... Error: start address not on sector boundary Error: start address not on sector boundary
not sure if anyone has noticed/fixed this yet and would save me from the trouble of finding/fixing the problem ... -mike

On Wednesday, December 29, 2010 20:53:17 Mike Frysinger wrote:
On Saturday, July 17, 2010 15:45:48 Wolfgang Denk wrote:
--- a/common/env_flash.c +++ b/common/env_flash.c int saveenv(void) {
- int len, rc;
- ulong end_addr;
- ulong flash_sect_addr;
-#if defined(CONFIG_ENV_SECT_SIZE) && (CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE) - ulong flash_offset;
- uchar env_buffer[CONFIG_ENV_SECT_SIZE];
-#else
- uchar *env_buffer = (uchar *)env_ptr;
-#endif /* CONFIG_ENV_SECT_SIZE */
- int rcode = 0;
-#if defined(CONFIG_ENV_SECT_SIZE) && (CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE) -
- flash_offset = ((ulong)flash_addr) & (CONFIG_ENV_SECT_SIZE-1);
- flash_sect_addr = ((ulong)flash_addr) & ~(CONFIG_ENV_SECT_SIZE-1);
- debug ( "copy old content: "
"sect_addr: %08lX env_addr: %08lX offset: %08lX\n",
flash_sect_addr, (ulong)flash_addr, flash_offset);
- /* copy old contents to temporary buffer */
- memcpy (env_buffer, (void *)flash_sect_addr, CONFIG_ENV_SECT_SIZE);
- /* copy current environment to temporary buffer */
- memcpy ((uchar *)((unsigned long)env_buffer + flash_offset),
env_ptr,
CONFIG_ENV_SIZE);
- env_t env_new;
- ssize_t len;
- int rc = 1;
- char *res;
- char *saved_data = NULL;
+#if CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE
- ulong up_data = 0;
- len = CONFIG_ENV_SECT_SIZE;
-#else
- flash_sect_addr = (ulong)flash_addr;
- len = CONFIG_ENV_SIZE;
- up_data = (end_addr + 1 - ((long)flash_addr + CONFIG_ENV_SIZE));
- debug("Data to save 0x%lx\n", up_data);
- if (up_data) {
if ((saved_data = malloc(up_data)) == NULL) {
printf("Unable to save the rest of sector (%ld)\n",
up_data);
goto done;
}
memcpy(saved_data,
(void *)((long)flash_addr + CONFIG_ENV_SIZE), up_data);
debug("Data (start 0x%lx, len 0x%lx) saved at 0x%lx\n",
(ulong)flash_addr + CONFIG_ENV_SIZE,
up_data,
(ulong)saved_data);
- }
#endif /* CONFIG_ENV_SECT_SIZE */
it would seem that somewhere nestled in this rewrite, support for embedded env was broken (CONFIG_ENV_SIZE < CONFIG_ENV_SECT_SIZE). on a bf548-ezkit for example, i see this behavior:
(v2010.09 / before this commit): bfin> save Saving Environment to Flash... . done Un-Protected 1 sectors Erasing Flash... . done Erased 1 sectors Writing to Flash... done . done Protected 1 sectors
(after this commit / v2010.12): bfin> save Saving Environment to Flash... Error: start address not on sector boundary Error: start address not on sector boundary
not sure if anyone has noticed/fixed this yet and would save me from the trouble of finding/fixing the problem ...
seems the new saveenv code in the non-redund case has been rewritten completely and doesn't support the same feature set as it used to.
old behavior: - load up the entire sector - overlay new env into sector - round env start addr down to sector start - round env end addr up to sector end - protect/erase/save/protect whole sector
new behavior: - load up the sector data after the env - overlay new env into sector - set env end addr to start of env plus one sector - protect/erase/save/protect whole sector
so the difference is that the new code no longer supports envs which do not start on a sector boundary. it does this so that it doesn't memcpy() as much data out of the flash at the expense of doing more CPU bound math operations (mostly bit twiddling).
so the question is whether the old behavior should be restored. if it not, the documentation should be tweaked to note these requirements and some simple CPP checks added to environment.h so that this issue turns into a build failure. after all, it's easy to detect an env offset that isnt at the start of the sector: #if (CONFIG_ENV_OFFSET & (CONFIG_ENV_SECT_SIZE - 1)) # error env offset not sector aligned #endif -mike

Dear Mike Frysinger,
In message 201012292139.05901.vapier@gentoo.org you wrote:
seems the new saveenv code in the non-redund case has been rewritten completely and doesn't support the same feature set as it used to.
...
so the difference is that the new code no longer supports envs which do not start on a sector boundary. it does this so that it doesn't memcpy() as much data out of the flash at the expense of doing more CPU bound math operations (mostly bit twiddling).
so the question is whether the old behavior should be restored. if it not,
The change was not intentional. Sorry.
I don't have a clear opinion if it's worth the effort to restore the old behavior - it seems it has never been used much (OK, you did).
the documentation should be tweaked to note these requirements and some simple CPP checks added to environment.h so that this issue turns into a build failure. after all, it's easy to detect an env offset that isnt at the start of the sector: #if (CONFIG_ENV_OFFSET & (CONFIG_ENV_SECT_SIZE - 1)) # error env offset not sector aligned #endif
I agree that adding such a test would be a good thing - please submit a patch. Or one to restore the old behaviour, if you prefer.
Thanks.
Wolfgang Denk

In message 1279395948-25864-1-git-send-email-wd@denx.de you wrote:
The following patch series adds some utilities (qsort and hash table based database functions) and uses these to reimplement the code used for the internal handling of environment variables.
Note: the patches are also available for easier testing as branch "hashtable" in the u-boot-testing repository, see git://git.denx.de/u-boot-testing.git
Best regards,
Wolfgang Denk

Wolfgang Denk wrote:
The following patch series adds some utilities (qsort and hash table based database functions) and uses these to reimplement the code used for the internal handling of environment variables.
Motivation:
- Old environment code used a pessimizing implementation:
- variable lookup used linear search => slow
- changed/added variables were added at the end, i. e. most frequently used variables had the slowest access times => slow
- each setenv() would calculate the CRC32 checksum over the whole environment block => slow
Hello Wolfgang,
how many users are out there that have such huge environments that speed is of any concern? The "grepenv" idea however seems to indicate some might have huge environments...
I see the advantages offered, but
Disadvantages:
- Image size grows by typically 5...7 KiB (might shrink a bit again on systems with redundant environment with a following patch series)
Looking at the patches I can't really see where that large increase in image size comes from; but if its really true, it seems to be a very high price to pay for those who get along with the environment as it is. I would at least recommend that all new UI commands are optional; as basics "setenv", "printenv", "saveenv" should do as until now. "export" and "import" must be adding a lot to the code.
Best Regards, Reinhard

On Saturday, July 17, 2010 16:56:43 Reinhard Meyer wrote:
Wolfgang Denk wrote:
Disadvantages:
Image size grows by typically 5...7 KiB (might shrink a bit again on
systems with redundant environment with a following patch series)
Looking at the patches I can't really see where that large increase in image size comes from; but if its really true, it seems to be a very high price to pay for those who get along with the environment as it is.
i wonder if we could #ifdef the sorting/hashing. i tend to agree with Reinhard that it is very common for boards to be deployed with a small env, so i wonder if the normal runtime experience is actually faster, or if the difference is about the same as system noise: boot u-boot, load env with ~20 entries, read a few env vars, boot OS. -mike

Dear Mike Frysinger,
In message 201007171728.27716.vapier@gentoo.org you wrote:
i wonder if we could #ifdef the sorting/hashing. i tend to agree with
The sorting does not take much:
-> size lib/qsort.o text data bss dec hex filename 264 0 0 264 108 lib/qsort.o
The hashing on the other hand might be replacable.
Reinhard that it is very common for boards to be deployed with a small env, so i wonder if the normal runtime experience is actually faster, or if the difference is about the same as system noise: boot u-boot, load env with ~20 entries, read a few env vars, boot OS.
Yes, this is one common use case.
But then, I see also systems that are running pretty complex shell scripts to implement features like reliable software updates etc., and there we see an improvment.
Also, speed is just one aspect - for me the capability of having several "profiles" is much, much more important.
Best regards,
Wolfgang Denk

On Saturday, July 17, 2010 17:41:57 Wolfgang Denk wrote:
Mike Frysinger wrote:
Reinhard that it is very common for boards to be deployed with a small env, so i wonder if the normal runtime experience is actually faster, or if the difference is about the same as system noise: boot u-boot, load env with ~20 entries, read a few env vars, boot OS.
Yes, this is one common use case.
But then, I see also systems that are running pretty complex shell scripts to implement features like reliable software updates etc., and there we see an improvment.
Also, speed is just one aspect - for me the capability of having several "profiles" is much, much more important.
which is why i suggested an ifdef. i didnt say the features you're introducing arent useful to many people, just that they are not necessary to many others. -mike

Dear Reinhard Meyer,
In message 4C42190B.8060102@emk-elektronik.de you wrote:
how many users are out there that have such huge environments that speed is of any concern?
This is not so much a question of huge environments. Even with small environments (say, 2 kb) speed may quickly become an issue - see for example here: http://www.denx.de/wiki/DULG/AN2004_11_BootTimeOptimization (Step 2)
Even worse is the fact that the old code recomputes the CRC over the whole environment for each and every "setenv" - and for example a network download will run 4 (or more) "setenv" commands internally...
Or remember the tricks that were necessary to get netconsole and serial console work in parallel...
And so on... the deeper you look, the more issues you will find with the old code. It has been designed to be simple to implement, but we added a lot of usage modes since, and while I still think it's a pretty efficient format for external storage, we need more efficient ways when internally accessing the environment files.
The "grepenv" idea however seems to indicate some might have huge environments...
I see the advantages offered, but
Disadvantages:
- Image size grows by typically 5...7 KiB (might shrink a bit again on systems with redundant environment with a following patch series)
Yes, it's some 2...3% on many systems...
Looking at the patches I can't really see where that large increase in image size comes from;
Well, the hashtable code itself adds some 3 kB, a few hundret bytes are needed for qsort, etc.
but if its really true, it seems to be a very high price to pay for those who get along with the environment as it is.
I hear you.
I would at least recommend that all new UI commands are optional; as basics "setenv", "printenv", "saveenv" should do as until now. "export" and "import" must be adding a lot to the code.
Actually it's not that much, and the code is needed anyway as it is used to read (import) the environment as stored in flash / nand / whatever into the hash table when booting, and to export it again when running "saveenv". The remaining UI commands are tiny; they don't consume any significant space.
Best regards,
Wolfgang Denk

In message 1279395948-25864-1-git-send-email-wd@denx.de I wrote:
The following patch series adds some utilities (qsort and hash table based database functions) and uses these to reimplement the code used for the internal handling of environment variables.
Motivation:
- Old environment code used a pessimizing implementation:
- variable lookup used linear search => slow
- changed/added variables were added at the end, i. e. most frequently used variables had the slowest access times => slow
- each setenv() would calculate the CRC32 checksum over the whole environment block => slow
Just to give a datapoint for speed:
measured on TQM5200 (MPC5200 at 400 MHz, environment size 16 kB):
OLD implementation:
=> sete ttt 'date;for i in 0 1 2 3 4 5 6 7 8 9 ; do for j in 0 1 2 3 4 5 6 7 8 9 ; do for k in 0 1 2 3 4 5 6 7 8 9 ; do run tt ; done ; done ; done;date' => sete tt 'sete var1 xxx;sete var2 ${var1};sete var1 ${var2};sete var1;sete var2' => run tt => run ttt Date: 1910-07-17 (unknown day) Time: 22:02:31 Date: 1910-07-17 (unknown day) Time: 22:03:27
New implementation:
=> run ttt Date: 1910-07-17 (unknown day) Time: 20:45:51 Date: 1910-07-17 (unknown day) Time: 20:45:53
I. e. we have 56 versus 2 seconds.
I haven't run intensive tests, but in general I think we can see that scripts referncing environment variables will typically run faster by a factor of 10, or more.
Best regards,
Wolfgang Denk

Wolfgang Denk wrote:
Just to give a datapoint for speed:
measured on TQM5200 (MPC5200 at 400 MHz, environment size 16 kB):
OLD implementation:
=> sete ttt 'date;for i in 0 1 2 3 4 5 6 7 8 9 ; do for j in 0 1 2 3 4 5 6 7 8 9 ; do for k in 0 1 2 3 4 5 6 7 8 9 ; do run tt ; done ; done ; done;date' => sete tt 'sete var1 xxx;sete var2 ${var1};sete var1 ${var2};sete var1;sete var2' => run tt => run ttt Date: 1910-07-17 (unknown day) Time: 22:02:31 Date: 1910-07-17 (unknown day) Time: 22:03:27
New implementation:
=> run ttt Date: 1910-07-17 (unknown day) Time: 20:45:51 Date: 1910-07-17 (unknown day) Time: 20:45:53
<joke>I'd propose a contest for the most hilarious use of a BOOTLOADER.....</joke>
But, how does the timing look in the current implementation when the CRC calculation is done only in the saveenv command?
Best Regards, Reinhard

Dear Reinhard Meyer,
In message 4C4291EE.5060102@emk-elektronik.de you wrote:
But, how does the timing look in the current implementation when the CRC calculation is done only in the saveenv command?
I don't know. Much better for the old code, obviously. But you still have the efforts for the linear search, usually way to the end of the environment.
Best regards,
Wolfgang Denk

On Saturday, July 17, 2010 15:45:43 Wolfgang Denk wrote:
The following patch series adds some utilities (qsort and hash table based database functions) and uses these to reimplement the code used for the internal handling of environment variables.
seems to break autocompletion CONFIG_AUTO_COMPLETE :(
if i checkout the previous commit (0fe247b973d9b8f7e2c04cc159fcdd2e64591e55), things work fine. but checking out the env change and it no longer works. -mike

Dear Mike Frysinger,
In message 201010200408.15020.vapier@gentoo.org you wrote:
The following patch series adds some utilities (qsort and hash table based database functions) and uses these to reimplement the code used for the internal handling of environment variables.
seems to break autocompletion CONFIG_AUTO_COMPLETE :(
Yes, this was already noted and discussed before.
I'm still not sure how to (re-) add this optimally.
Best regards,
Wolfgang Denk

On Saturday, July 17, 2010 15:45:43 Wolfgang Denk wrote:
TODO:
drop default env as implemented now; provide a text file based initialization instead (eventually using several text files to incrementally build it from common blocks) and a tool to convert it into a binary blob / object file.
It would be nice if we could add wildcard support for environment variables; this is needed for variable name auto-completion, but it would also be nice to be able to say "printenv ip*" or "printenv *addr*"
Some boards don't link any more due to the grown code size: AR405, CANBT, DU405, PMC440, canyonlands, sc3, sequoia, socrates.
=> cc: Matthias Fuchs matthias.fuchs@esd-electronics.com, Stefan Roese sr@denx.de, Heiko Schocher hs@denx.de
Dropping forceenv() causes build problems on schmoogie
=> cc: Sergey Kubushyn ksi@koi8.net
Build tested on PPC and ARM only; runtime tested with NOR and NAND flash only => needs testing!!
seems that some todo items are missing, or not well implied. for example, it seems that gd->env_addr needs to be punted, and these functions get a "_f" appended to their name since they only work on default_environment[] now: env_get_addr() env_get_char() env_get_char_memory() envmatch() -mike

Dear Mike Frysinger,
In message 201012080456.41492.vapier@gentoo.org you wrote:
seems that some todo items are missing, or not well implied. for example, it seems that gd->env_addr needs to be punted, and these functions get a "_f" appended to their name since they only work on default_environment[] now: env_get_addr() env_get_char() env_get_char_memory() envmatch()
I'm not sure I understand what you mean.
These functions not NOT only operate on the default_environment[]; they are also used to access the normal environment before relocation.
Best regards,
Wolfgang Denk

On Wednesday, December 08, 2010 05:04:42 Wolfgang Denk wrote:
Mike Frysinger wrote:
seems that some todo items are missing, or not well implied. for example, it seems that gd->env_addr needs to be punted, and these functions get a "_f" appended to their name since they only work on default_environment[] now: env_get_addr() env_get_char() env_get_char_memory() envmatch()
I'm not sure I understand what you mean.
These functions not NOT only operate on the default_environment[]; they are also used to access the normal environment before relocation.
my point is that they cannot be used after relocation and once the env is moved into the hash table. currently, they dont return any errors if someone attempts to do so and while it might seem like the funcs are working, they only parse the default env. -mike
participants (7)
-
Jerry Van Baren
-
Kim Phillips
-
Matthias Fuchs
-
Mike Frysinger
-
Reinhard Meyer
-
Sergey Kubushyn
-
Wolfgang Denk