summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README37
-rw-r--r--common/Makefile2
-rw-r--r--common/cmd_nvedit.c50
-rw-r--r--common/env_common.c2
-rw-r--r--common/env_flags.c319
-rw-r--r--include/env_callback.h2
-rw-r--r--include/env_default.h3
-rw-r--r--include/env_flags.h76
-rw-r--r--include/environment.h9
-rw-r--r--include/search.h1
-rw-r--r--lib/hashtable.c4
11 files changed, 447 insertions, 58 deletions
diff --git a/README b/README
index ac61a32..acb711a 100644
--- a/README
+++ b/README
@@ -2188,6 +2188,11 @@ CBFS (Coreboot Filesystem) support
serial# is unaffected by this, i. e. it remains
read-only.]
+ The same can be accomplished in a more flexible way
+ for any variable by configuring the type of access
+ to allow for those variables in the ".flags" variable
+ or define CONFIG_ENV_FLAGS_LIST_STATIC.
+
- Protected RAM:
CONFIG_PRAM
@@ -3113,6 +3118,38 @@ Configuration Settings:
cases. This setting can be used to tune behaviour; see
lib/hashtable.c for details.
+- CONFIG_ENV_FLAGS_LIST_DEFAULT
+- CONFIG_ENV_FLAGS_LIST_STATIC
+ Enable validation of the values given to enviroment variables when
+ calling env set. Variables can be restricted to only decimal,
+ hexadecimal, or boolean. If CONFIG_CMD_NET is also defined,
+ the variables can also be restricted to IP address or MAC address.
+
+ The format of the list is:
+ type_attribute = [s|d|x|b|i|m]
+ attributes = type_attribute
+ entry = variable_name[:attributes]
+ list = entry[,list]
+
+ The type attributes are:
+ s - String (default)
+ d - Decimal
+ x - Hexadecimal
+ b - Boolean ([1yYtT|0nNfF])
+ i - IP address
+ m - MAC address
+
+ - CONFIG_ENV_FLAGS_LIST_DEFAULT
+ Define this to a list (string) to define the ".flags"
+ envirnoment variable in the default or embedded environment.
+
+ - CONFIG_ENV_FLAGS_LIST_STATIC
+ Define this to a list (string) to define validation that
+ should be done if an entry is not found in the ".flags"
+ environment variable. To override a setting in the static
+ list, simply add an entry for the same variable name to the
+ ".flags" variable.
+
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/common/Makefile b/common/Makefile
index 04812e9..c774395 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -47,6 +47,7 @@ COBJS-y += cmd_version.o
COBJS-y += env_attr.o
COBJS-y += env_callback.o
COBJS-y += env_common.o
+COBJS-y += env_flags.o
COBJS-$(CONFIG_ENV_IS_IN_DATAFLASH) += env_dataflash.o
COBJS-$(CONFIG_ENV_IS_IN_EEPROM) += env_eeprom.o
XCOBJS-$(CONFIG_ENV_IS_EMBEDDED) += env_embedded.o
@@ -212,6 +213,7 @@ COBJS-$(CONFIG_SPL_NET_SUPPORT) += cmd_nvedit.o
COBJS-$(CONFIG_SPL_NET_SUPPORT) += env_attr.o
COBJS-$(CONFIG_SPL_NET_SUPPORT) += env_callback.o
COBJS-$(CONFIG_SPL_NET_SUPPORT) += env_common.o
+COBJS-$(CONFIG_SPL_NET_SUPPORT) += env_flags.o
COBJS-$(CONFIG_SPL_NET_SUPPORT) += env_nowhere.o
COBJS-$(CONFIG_SPL_NET_SUPPORT) += miiphyutil.o
endif
diff --git a/common/cmd_nvedit.c b/common/cmd_nvedit.c
index cb191cd..f645194 100644
--- a/common/cmd_nvedit.c
+++ b/common/cmd_nvedit.c
@@ -192,57 +192,9 @@ static int do_env_grep(cmd_tbl_t *cmdtp, int flag,
#endif /* CONFIG_SPL_BUILD */
/*
- * Perform consistency checking before setting, replacing, or deleting an
- * environment variable, then (if successful) apply the changes to internals so
- * to make them effective. Code for this function was taken out of
- * _do_env_set(), which now calls it instead.
- * Also called as a callback function by himport_r().
- * Returns 0 in case of success, 1 in case of failure.
- * When (flag & H_FORCE) is set, do not print out any error message and force
- * overwriting of write-once variables.
- */
-
-int env_change_ok(const ENTRY *item, const char *newval, enum env_op op,
- int flag)
-{
-#ifndef CONFIG_ENV_OVERWRITE
- const char *name;
-#if defined(CONFIG_OVERWRITE_ETHADDR_ONCE) && defined(CONFIG_ETHADDR)
- const char *oldval = NULL;
-
- if (op != env_op_create)
- oldval = item->data;
-#endif
-
- name = item->key;
-#endif
-
-#ifndef CONFIG_ENV_OVERWRITE
- /*
- * Some variables like "ethaddr" and "serial#" can be set only once and
- * cannot be deleted, unless CONFIG_ENV_OVERWRITE is defined.
- */
- if (op != env_op_create && /* variable exists */
- (flag & H_FORCE) == 0) { /* and we are not forced */
- if (strcmp(name, "serial#") == 0 ||
- (strcmp(name, "ethaddr") == 0
-#if defined(CONFIG_OVERWRITE_ETHADDR_ONCE) && defined(CONFIG_ETHADDR)
- && strcmp(oldval, __stringify(CONFIG_ETHADDR)) != 0
-#endif /* CONFIG_OVERWRITE_ETHADDR_ONCE && CONFIG_ETHADDR */
- )) {
- printf("Can't overwrite \"%s\"\n", name);
- return 1;
- }
- }
-#endif
-
- return 0;
-}
-
-/*
* Set a new environment variable,
* or replace or delete an existing one.
-*/
+ */
static int _do_env_set(int flag, int argc, char * const argv[])
{
int i, len;
diff --git a/common/env_common.c b/common/env_common.c
index 067fe3f..bb18070 100644
--- a/common/env_common.c
+++ b/common/env_common.c
@@ -40,7 +40,7 @@ DECLARE_GLOBAL_DATA_PTR;
#include <env_default.h>
struct hsearch_data env_htab = {
- .change_ok = env_change_ok,
+ .change_ok = env_flags_validate,
};
static uchar __env_get_char_spec(int index)
diff --git a/common/env_flags.c b/common/env_flags.c
new file mode 100644
index 0000000..a58d614
--- /dev/null
+++ b/common/env_flags.c
@@ -0,0 +1,319 @@
+/*
+ * (C) Copyright 2012
+ * Joe Hershberger, National Instruments, joe.hershberger@ni.com
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <linux/string.h>
+#include <linux/ctype.h>
+
+#include <common.h>
+#include <environment.h>
+
+#ifdef CONFIG_CMD_NET
+#define ENV_FLAGS_NET_VARTYPE_REPS "im"
+#else
+#define ENV_FLAGS_NET_VARTYPE_REPS ""
+#endif
+
+static const char env_flags_vartype_rep[] = "sdxb" ENV_FLAGS_NET_VARTYPE_REPS;
+
+/*
+ * Parse the flags string from a .flags attribute list into the vartype enum.
+ */
+enum env_flags_vartype env_flags_parse_vartype(const char *flags)
+{
+ char *type;
+
+ if (strlen(flags) <= ENV_FLAGS_VARTYPE_LOC)
+ return env_flags_vartype_string;
+
+ type = strchr(env_flags_vartype_rep,
+ flags[ENV_FLAGS_VARTYPE_LOC]);
+
+ if (type != NULL)
+ return (enum env_flags_vartype)
+ (type - &env_flags_vartype_rep[0]);
+
+ printf("## Warning: Unknown environment variable type '%c'\n",
+ flags[ENV_FLAGS_VARTYPE_LOC]);
+ return env_flags_vartype_string;
+}
+
+static inline int is_hex_prefix(const char *value)
+{
+ return value[0] == '0' && (value[1] == 'x' || value[1] == 'X');
+}
+
+static void skip_num(int hex, const char *value, const char **end,
+ int max_digits)
+{
+ int i;
+
+ if (hex && is_hex_prefix(value))
+ value += 2;
+
+ for (i = max_digits; i != 0; i--) {
+ if (hex && !isxdigit(*value))
+ break;
+ if (!hex && !isdigit(*value))
+ break;
+ value++;
+ }
+ if (end != NULL)
+ *end = value;
+}
+
+/*
+ * Based on the declared type enum, validate that the value string complies
+ * with that format
+ */
+static int _env_flags_validate_type(const char *value,
+ enum env_flags_vartype type)
+{
+ const char *end;
+#ifdef CONFIG_CMD_NET
+ const char *cur;
+ int i;
+#endif
+
+ switch (type) {
+ case env_flags_vartype_string:
+ break;
+ case env_flags_vartype_decimal:
+ skip_num(0, value, &end, -1);
+ if (*end != '\0')
+ return -1;
+ break;
+ case env_flags_vartype_hex:
+ skip_num(1, value, &end, -1);
+ if (*end != '\0')
+ return -1;
+ if (value + 2 == end && is_hex_prefix(value))
+ return -1;
+ break;
+ case env_flags_vartype_bool:
+ if (value[0] != '1' && value[0] != 'y' && value[0] != 't' &&
+ value[0] != 'Y' && value[0] != 'T' &&
+ value[0] != '0' && value[0] != 'n' && value[0] != 'f' &&
+ value[0] != 'N' && value[0] != 'F')
+ return -1;
+ if (value[1] != '\0')
+ return -1;
+ break;
+#ifdef CONFIG_CMD_NET
+ case env_flags_vartype_ipaddr:
+ cur = value;
+ for (i = 0; i < 4; i++) {
+ skip_num(0, cur, &end, 3);
+ if (cur == end)
+ return -1;
+ if (i != 3 && *end != '.')
+ return -1;
+ if (i == 3 && *end != '\0')
+ return -1;
+ cur = end + 1;
+ }
+ break;
+ case env_flags_vartype_macaddr:
+ cur = value;
+ for (i = 0; i < 6; i++) {
+ skip_num(1, cur, &end, 2);
+ if (cur == end)
+ return -1;
+ if (cur + 2 == end && is_hex_prefix(cur))
+ return -1;
+ if (i != 5 && *end != ':')
+ return -1;
+ if (i == 5 && *end != '\0')
+ return -1;
+ cur = end + 1;
+ }
+ break;
+#endif
+ case env_flags_vartype_end:
+ return -1;
+ }
+
+ /* OK */
+ return 0;
+}
+
+/*
+ * Look for flags in a provided list and failing that the static list
+ */
+static inline int env_flags_lookup(const char *flags_list, const char *name,
+ char *flags)
+{
+ int ret = 1;
+
+ if (!flags)
+ /* bad parameter */
+ return -1;
+
+ /* try the env first */
+ if (flags_list)
+ ret = env_attr_lookup(flags_list, name, flags);
+
+ if (ret != 0)
+ /* if not found in the env, look in the static list */
+ ret = env_attr_lookup(ENV_FLAGS_LIST_STATIC, name, flags);
+
+ return ret;
+}
+
+/*
+ * Parse the flag charachters from the .flags attribute list into the binary
+ * form to be stored in the environment entry->flags field.
+ */
+static int env_parse_flags_to_bin(const char *flags)
+{
+ return env_flags_parse_vartype(flags) & ENV_FLAGS_VARTYPE_BIN_MASK;
+}
+
+/*
+ * Look for possible flags for a newly added variable
+ * This is called specifically when the variable did not exist in the hash
+ * previously, so the blanket update did not find this variable.
+ */
+void env_flags_init(ENTRY *var_entry)
+{
+ const char *var_name = var_entry->key;
+ const char *flags_list = getenv(ENV_FLAGS_VAR);
+ char flags[ENV_FLAGS_ATTR_MAX_LEN + 1] = "";
+ int ret = 1;
+
+ /* look in the ".flags" and static for a reference to this variable */
+ ret = env_flags_lookup(flags_list, var_name, flags);
+
+ /* if any flags were found, set the binary form to the entry */
+ if (!ret && strlen(flags))
+ var_entry->flags = env_parse_flags_to_bin(flags);
+}
+
+/*
+ * Called on each existing env var prior to the blanket update since removing
+ * a flag in the flag list should remove its flags.
+ */
+static int clear_flags(ENTRY *entry)
+{
+ entry->flags = 0;
+
+ return 0;
+}
+
+/*
+ * Call for each element in the list that defines flags for a variable
+ */
+static int set_flags(const char *name, const char *value)
+{
+ ENTRY e, *ep;
+
+ e.key = name;
+ e.data = NULL;
+ hsearch_r(e, FIND, &ep, &env_htab, 0);
+
+ /* does the env variable actually exist? */
+ if (ep != NULL) {
+ /* the flag list is empty, so clear the flags */
+ if (value == NULL || strlen(value) == 0)
+ ep->flags = 0;
+ else
+ /* assign the requested flags */
+ ep->flags = env_parse_flags_to_bin(value);
+ }
+
+ return 0;
+}
+
+static int on_flags(const char *name, const char *value, enum env_op op,
+ int flags)
+{
+ /* remove all flags */
+ hwalk_r(&env_htab, clear_flags);
+
+ /* configure any static flags */
+ env_attr_walk(ENV_FLAGS_LIST_STATIC, set_flags);
+ /* configure any dynamic flags */
+ env_attr_walk(value, set_flags);
+
+ return 0;
+}
+U_BOOT_ENV_CALLBACK(flags, on_flags);
+
+/*
+ * Perform consistency checking before creating, overwriting, or deleting an
+ * environment variable. Called as a callback function by hsearch_r() and
+ * hdelete_r(). Returns 0 in case of success, 1 in case of failure.
+ * When (flag & H_FORCE) is set, do not print out any error message and force
+ * overwriting of write-once variables.
+ */
+
+int env_flags_validate(const ENTRY *item, const char *newval, enum env_op op,
+ int flag)
+{
+ const char *name;
+#if !defined(CONFIG_ENV_OVERWRITE) && defined(CONFIG_OVERWRITE_ETHADDR_ONCE) \
+&& defined(CONFIG_ETHADDR)
+ const char *oldval = NULL;
+
+ if (op != env_op_create)
+ oldval = item->data;
+#endif
+
+ name = item->key;
+
+ /* Default value for NULL to protect string-manipulating functions */
+ newval = newval ? : "";
+
+#ifndef CONFIG_ENV_OVERWRITE
+ /*
+ * Some variables like "ethaddr" and "serial#" can be set only once and
+ * cannot be deleted, unless CONFIG_ENV_OVERWRITE is defined.
+ */
+ if (op != env_op_create && /* variable exists */
+ (flag & H_FORCE) == 0) { /* and we are not forced */
+ if (strcmp(name, "serial#") == 0 ||
+ (strcmp(name, "ethaddr") == 0
+#if defined(CONFIG_OVERWRITE_ETHADDR_ONCE) && defined(CONFIG_ETHADDR)
+ && strcmp(oldval, __stringify(CONFIG_ETHADDR)) != 0
+#endif /* CONFIG_OVERWRITE_ETHADDR_ONCE && CONFIG_ETHADDR */
+ )) {
+ printf("Can't overwrite \"%s\"\n", name);
+ return 1;
+ }
+ }
+#endif
+
+ /* validate the value to match the variable type */
+ if (op != env_op_delete) {
+ enum env_flags_vartype type = (enum env_flags_vartype)
+ (ENV_FLAGS_VARTYPE_BIN_MASK & item->flags);
+
+ if (_env_flags_validate_type(newval, type) < 0) {
+ printf("## Error: flags type check failure for "
+ "\"%s\" <= \"%s\" (type: %c)\n",
+ name, newval, env_flags_vartype_rep[type]);
+ return -1;
+ }
+ }
+
+ return 0;
+}
diff --git a/include/env_callback.h b/include/env_callback.h
index f52e133..47fdc6f 100644
--- a/include/env_callback.h
+++ b/include/env_callback.h
@@ -24,6 +24,7 @@
#ifndef __ENV_CALLBACK_H__
#define __ENV_CALLBACK_H__
+#include <env_flags.h>
#include <linker_lists.h>
#include <search.h>
@@ -45,6 +46,7 @@
* a new association in the ".callbacks" environment variable.
*/
#define ENV_CALLBACK_LIST_STATIC ENV_CALLBACK_VAR ":callbacks," \
+ ENV_FLAGS_VAR ":flags," \
"baudrate:baudrate," \
"bootfile:bootfile," \
"loadaddr:loadaddr," \
diff --git a/include/env_default.h b/include/env_default.h
index d05eba1..39c5b7c 100644
--- a/include/env_default.h
+++ b/include/env_default.h
@@ -41,6 +41,9 @@ const uchar default_environment[] = {
#ifdef CONFIG_ENV_CALLBACK_LIST_DEFAULT
ENV_CALLBACK_VAR "=" CONFIG_ENV_CALLBACK_LIST_DEFAULT "\0"
#endif
+#ifdef CONFIG_ENV_FLAGS_LIST_DEFAULT
+ ENV_FLAGS_VAR "=" CONFIG_ENV_FLAGS_LIST_DEFAULT "\0"
+#endif
#ifdef CONFIG_BOOTARGS
"bootargs=" CONFIG_BOOTARGS "\0"
#endif
diff --git a/include/env_flags.h b/include/env_flags.h
new file mode 100644
index 0000000..bf25f27
--- /dev/null
+++ b/include/env_flags.h
@@ -0,0 +1,76 @@
+/*
+ * (C) Copyright 2012
+ * Joe Hershberger, National Instruments, joe.hershberger@ni.com
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#ifndef __ENV_FLAGS_H__
+#define __ENV_FLAGS_H__
+
+enum env_flags_vartype {
+ env_flags_vartype_string,
+ env_flags_vartype_decimal,
+ env_flags_vartype_hex,
+ env_flags_vartype_bool,
+#ifdef CONFIG_CMD_NET
+ env_flags_vartype_ipaddr,
+ env_flags_vartype_macaddr,
+#endif
+ env_flags_vartype_end
+};
+
+#define ENV_FLAGS_VAR ".flags"
+#define ENV_FLAGS_ATTR_MAX_LEN 2
+#define ENV_FLAGS_VARTYPE_LOC 0
+
+#ifndef CONFIG_ENV_FLAGS_LIST_STATIC
+#define CONFIG_ENV_FLAGS_LIST_STATIC ""
+#endif
+
+#define ENV_FLAGS_LIST_STATIC \
+ CONFIG_ENV_FLAGS_LIST_STATIC
+
+/*
+ * Parse the flags string from a .flags attribute list into the vartype enum.
+ */
+enum env_flags_vartype env_flags_parse_vartype(const char *flags);
+
+#include <search.h>
+
+/*
+ * When adding a variable to the environment, initialize the flags for that
+ * variable.
+ */
+void env_flags_init(ENTRY *var_entry);
+
+/*
+ * Validate the newval for to conform with the requirements defined by its flags
+ */
+int env_flags_validate(const ENTRY *item, const char *newval, enum env_op op,
+ int flag);
+
+/*
+ * These are the binary flags used in the environment entry->flags variable to
+ * decribe properties of veriables in the table
+ */
+#define ENV_FLAGS_VARTYPE_BIN_MASK 0x00000007
+/* The actual variable type values use the enum value (within the mask) */
+
+#endif /* __ENV_FLAGS_H__ */
diff --git a/include/environment.h b/include/environment.h
index 6c30215..00e59ba 100644
--- a/include/environment.h
+++ b/include/environment.h
@@ -166,6 +166,7 @@ extern void env_reloc(void);
#include <env_attr.h>
#include <env_callback.h>
+#include <env_flags.h>
#include <search.h>
extern struct hsearch_data env_htab;
@@ -189,14 +190,6 @@ int set_default_vars(int nvars, char * const vars[]);
/* Import from binary representation into hash table */
int env_import(const char *buf, int check);
-/*
- * Check if variable "item" can be changed to newval
- * When (flag & H_FORCE) is set, it does not print out any error
- * message and forces overwriting of write-once variables.
- */
-int env_change_ok(const ENTRY *item, const char *newval, enum env_op op,
- int flag);
-
#endif /* DO_DEPS_ONLY */
#endif /* _ENVIRONMENT_H_ */
diff --git a/include/search.h b/include/search.h
index d68e24a..13d3be6 100644
--- a/include/search.h
+++ b/include/search.h
@@ -49,6 +49,7 @@ typedef struct entry {
char *data;
int (*callback)(const char *name, const char *value, enum env_op op,
int flags);
+ int flags;
} ENTRY;
/* Opaque type for internal use. */
diff --git a/lib/hashtable.c b/lib/hashtable.c
index e922666..07ebfb2 100644
--- a/lib/hashtable.c
+++ b/lib/hashtable.c
@@ -55,6 +55,7 @@
#endif
#include <env_callback.h>
+#include <env_flags.h>
#include <search.h>
/*
@@ -412,6 +413,8 @@ int hsearch_r(ENTRY item, ACTION action, ENTRY ** retval,
/* This is a new entry, so look up a possible callback */
env_callback_init(&htab->table[idx].entry);
+ /* Also look for flags */
+ env_flags_init(&htab->table[idx].entry);
/* check for permission */
if (htab->change_ok != NULL && htab->change_ok(
@@ -465,6 +468,7 @@ static void _hdelete(const char *key, struct hsearch_data *htab, ENTRY *ep,
free((void *)ep->key);
free(ep->data);
ep->callback = NULL;
+ ep->flags = 0;
htab->table[idx].used = -1;
--htab->filled;