1 // SPDX-License-Identifier: GPL-2.0+
3 * (C) Copyright 2000-2010
4 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
6 * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com>
7 * Andreas Heppel <aheppel@sysgo.de>
11 #include <bootstage.h>
14 #include <env_internal.h>
17 #include <asm/global_data.h>
18 #include <linux/printk.h>
19 #include <linux/stddef.h>
23 #include <u-boot/crc.h>
24 #include <dm/ofnode.h>
28 DECLARE_GLOBAL_DATA_PTR
;
30 /************************************************************************
31 * Default settings to be used when no valid environment is found
33 #include <env_default.h>
35 struct hsearch_data env_htab
= {
36 .change_ok
= env_flags_validate
,
40 * This variable is incremented each time we set an environment variable so we
41 * can be check via env_get_id() to see if the environment has changed or not.
42 * This makes it possible to reread an environment variable only if the
43 * environment was changed, typically used by networking code.
45 static int env_id
= 1;
57 int env_do_env_set(int flag
, int argc
, char *const argv
[], int env_flag
)
60 char *name
, *value
, *s
;
61 struct env_entry e
, *ep
;
63 debug("Initial value for argc=%d\n", argc
);
65 #if !IS_ENABLED(CONFIG_SPL_BUILD) && IS_ENABLED(CONFIG_CMD_NVEDIT_EFI)
66 if (argc
> 1 && argv
[1][0] == '-' && argv
[1][1] == 'e')
67 return do_env_set_efi(NULL
, flag
, --argc
, ++argv
);
70 while (argc
> 1 && **(argv
+ 1) == '-') {
84 debug("Final value for argc=%d\n", argc
);
87 if (strchr(name
, '=')) {
88 printf("## Error: illegal character '=' "
89 "in variable name \"%s\"\n", name
);
96 if (argc
< 3 || argv
[2] == NULL
) {
97 int rc
= hdelete_r(name
, &env_htab
, env_flag
);
99 /* If the variable didn't exist, don't report an error */
100 return rc
&& rc
!= -ENOENT
? 1 : 0;
104 * Insert / replace new value
106 for (i
= 2, len
= 0; i
< argc
; ++i
)
107 len
+= strlen(argv
[i
]) + 1;
111 printf("## Can't malloc %d bytes\n", len
);
114 for (i
= 2, s
= value
; i
< argc
; ++i
) {
117 while ((*s
++ = *v
++) != '\0')
126 hsearch_r(e
, ENV_ENTER
, &ep
, &env_htab
, env_flag
);
129 printf("## Error inserting \"%s\" variable, errno=%d\n",
137 int env_set(const char *varname
, const char *varvalue
)
139 const char * const argv
[4] = { "setenv", varname
, varvalue
, NULL
};
141 /* before import into hashtable */
142 if (!(gd
->flags
& GD_FLG_ENV_READY
))
145 if (varvalue
== NULL
|| varvalue
[0] == '\0')
146 return env_do_env_set(0, 2, (char * const *)argv
, H_PROGRAMMATIC
);
148 return env_do_env_set(0, 3, (char * const *)argv
, H_PROGRAMMATIC
);
152 * Set an environment variable to an integer value
154 * @param varname Environment variable to set
155 * @param value Value to set it to
156 * Return: 0 if ok, 1 on error
158 int env_set_ulong(const char *varname
, ulong value
)
160 /* TODO: this should be unsigned */
161 char *str
= simple_itoa(value
);
163 return env_set(varname
, str
);
167 * Set an environment variable to an value in hex
169 * @param varname Environment variable to set
170 * @param value Value to set it to
171 * Return: 0 if ok, 1 on error
173 int env_set_hex(const char *varname
, ulong value
)
177 sprintf(str
, "%lx", value
);
178 return env_set(varname
, str
);
181 ulong
env_get_hex(const char *varname
, ulong default_val
)
187 s
= env_get(varname
);
189 value
= hextoul(s
, &endp
);
196 int eth_env_get_enetaddr(const char *name
, uint8_t *enetaddr
)
198 string_to_enetaddr(env_get(name
), enetaddr
);
199 return is_valid_ethaddr(enetaddr
);
202 int eth_env_set_enetaddr(const char *name
, const uint8_t *enetaddr
)
204 char buf
[ARP_HLEN_ASCII
+ 1];
206 if (eth_env_get_enetaddr(name
, (uint8_t *)buf
))
209 sprintf(buf
, "%pM", enetaddr
);
211 return env_set(name
, buf
);
215 * Look up variable from environment,
216 * return address of storage for that variable,
217 * or NULL if not found
219 char *env_get(const char *name
)
221 if (gd
->flags
& GD_FLG_ENV_READY
) { /* after import into hashtable */
222 struct env_entry e
, *ep
;
228 hsearch_r(e
, ENV_FIND
, &ep
, &env_htab
, 0);
230 return ep
? ep
->data
: NULL
;
233 /* restricted capabilities before import */
234 if (env_get_f(name
, (char *)(gd
->env_buf
), sizeof(gd
->env_buf
)) >= 0)
235 return (char *)(gd
->env_buf
);
241 * Like env_get, but prints an error if envvar isn't defined in the
242 * environment. It always returns what env_get does, so it can be used in
243 * place of env_get without changing error handling otherwise.
245 char *from_env(const char *envvar
)
249 ret
= env_get(envvar
);
252 printf("missing environment variable: %s\n", envvar
);
257 static int env_get_from_linear(const char *env
, const char *name
, char *buf
,
263 if (name
== NULL
|| *name
== '\0')
266 name_len
= strlen(name
);
268 for (p
= env
; *p
!= '\0'; p
= end
+ 1) {
272 for (end
= p
; *end
!= '\0'; ++end
)
273 if (end
- env
>= CONFIG_ENV_SIZE
)
276 if (strncmp(name
, p
, name_len
) || p
[name_len
] != '=')
278 value
= &p
[name_len
+ 1];
281 memcpy(buf
, value
, min(len
, res
+ 1));
285 printf("env_buf [%u bytes] too small for value of \"%s\"\n",
296 * Look up variable from environment for restricted C runtime env.
298 int env_get_f(const char *name
, char *buf
, unsigned len
)
302 if (gd
->env_valid
== ENV_INVALID
)
303 env
= default_environment
;
305 env
= (const char *)gd
->env_addr
;
307 return env_get_from_linear(env
, name
, buf
, len
);
311 * Decode the integer value of an environment variable and return it.
313 * @param name Name of environment variable
314 * @param base Number base to use (normally 10, or 16 for hex)
315 * @param default_val Default value to return if the variable is not
317 * Return: the decoded value, or default_val if not found
319 ulong
env_get_ulong(const char *name
, int base
, ulong default_val
)
322 * We can use env_get() here, even before relocation, since the
323 * environment variable value is an integer and thus short.
325 const char *str
= env_get(name
);
327 return str
? simple_strtoul(str
, NULL
, base
) : default_val
;
331 * Read an environment variable as a boolean
332 * Return -1 if variable does not exist (default to true)
334 int env_get_yesno(const char *var
)
336 char *s
= env_get(var
);
340 return (*s
== '1' || *s
== 'y' || *s
== 'Y' || *s
== 't' || *s
== 'T') ?
344 bool env_get_autostart(void)
346 return env_get_yesno("autostart") == 1;
350 * Look up the variable from the default environment
352 char *env_get_default(const char *name
)
356 ret
= env_get_default_into(name
, (char *)(gd
->env_buf
),
357 sizeof(gd
->env_buf
));
359 return (char *)(gd
->env_buf
);
365 * Look up the variable from the default environment and store its value in buf
367 int env_get_default_into(const char *name
, char *buf
, unsigned int len
)
369 return env_get_from_linear(default_environment
, name
, buf
, len
);
372 void env_set_default(const char *s
, int flags
)
375 if ((flags
& H_INTERACTIVE
) == 0) {
376 printf("*** Warning - %s, "
377 "using default environment\n\n", s
);
382 debug("Using default environment\n");
386 if (himport_r(&env_htab
, default_environment
,
387 sizeof(default_environment
), '\0', flags
, 0,
389 pr_err("## Error: Environment import failed: errno = %d\n",
394 gd
->flags
|= GD_FLG_ENV_READY
;
395 gd
->flags
|= GD_FLG_ENV_DEFAULT
;
399 /* [re]set individual variables to their value in the default environment */
400 int env_set_default_vars(int nvars
, char * const vars
[], int flags
)
403 * Special use-case: import from default environment
404 * (and use \0 as a separator)
406 flags
|= H_NOCLEAR
| H_DEFAULT
;
407 return himport_r(&env_htab
, default_environment
,
408 sizeof(default_environment
), '\0',
409 flags
, 0, nvars
, vars
);
413 * Check if CRC is valid and (if yes) import the environment.
414 * Note that "buf" may or may not be aligned.
416 int env_import(const char *buf
, int check
, int flags
)
418 env_t
*ep
= (env_t
*)buf
;
423 memcpy(&crc
, &ep
->crc
, sizeof(crc
));
425 if (crc32(0, ep
->data
, ENV_SIZE
) != crc
) {
426 env_set_default("bad CRC", 0);
427 return -ENOMSG
; /* needed for env_load() */
431 if (himport_r(&env_htab
, (char *)ep
->data
, ENV_SIZE
, '\0', flags
, 0,
433 gd
->flags
|= GD_FLG_ENV_READY
;
437 pr_err("Cannot import environment: errno = %d\n", errno
);
439 env_set_default("import failed", 0);
444 #ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT
445 static unsigned char env_flags
;
447 int env_check_redund(const char *buf1
, int buf1_read_fail
,
448 const char *buf2
, int buf2_read_fail
)
450 int crc1_ok
= 0, crc2_ok
= 0;
451 env_t
*tmp_env1
, *tmp_env2
;
453 tmp_env1
= (env_t
*)buf1
;
454 tmp_env2
= (env_t
*)buf2
;
456 if (buf1_read_fail
&& buf2_read_fail
) {
457 puts("*** Error - No Valid Environment Area found\n");
459 } else if (buf1_read_fail
|| buf2_read_fail
) {
460 puts("*** Warning - some problems detected ");
461 puts("reading environment; recovered successfully\n");
465 crc1_ok
= crc32(0, tmp_env1
->data
, ENV_SIZE
) ==
468 crc2_ok
= crc32(0, tmp_env2
->data
, ENV_SIZE
) ==
471 if (!crc1_ok
&& !crc2_ok
) {
472 gd
->env_valid
= ENV_INVALID
;
473 return -ENOMSG
; /* needed for env_load() */
474 } else if (crc1_ok
&& !crc2_ok
) {
475 gd
->env_valid
= ENV_VALID
;
476 } else if (!crc1_ok
&& crc2_ok
) {
477 gd
->env_valid
= ENV_REDUND
;
479 /* both ok - check serial */
480 if (tmp_env1
->flags
== 255 && tmp_env2
->flags
== 0)
481 gd
->env_valid
= ENV_REDUND
;
482 else if (tmp_env2
->flags
== 255 && tmp_env1
->flags
== 0)
483 gd
->env_valid
= ENV_VALID
;
484 else if (tmp_env1
->flags
> tmp_env2
->flags
)
485 gd
->env_valid
= ENV_VALID
;
486 else if (tmp_env2
->flags
> tmp_env1
->flags
)
487 gd
->env_valid
= ENV_REDUND
;
488 else /* flags are equal - almost impossible */
489 gd
->env_valid
= ENV_VALID
;
495 int env_import_redund(const char *buf1
, int buf1_read_fail
,
496 const char *buf2
, int buf2_read_fail
,
502 ret
= env_check_redund(buf1
, buf1_read_fail
, buf2
, buf2_read_fail
);
505 env_set_default("bad env area", 0);
507 } else if (ret
== -ENOMSG
) {
508 env_set_default("bad CRC", 0);
512 if (gd
->env_valid
== ENV_VALID
)
517 env_flags
= ep
->flags
;
519 return env_import((char *)ep
, 0, flags
);
521 #endif /* CONFIG_SYS_REDUNDAND_ENVIRONMENT */
523 /* Export the environment and generate CRC for it. */
524 int env_export(env_t
*env_out
)
529 res
= (char *)env_out
->data
;
530 len
= hexport_r(&env_htab
, '\0', 0, &res
, ENV_SIZE
, 0, NULL
);
532 pr_err("Cannot export environment: errno = %d\n", errno
);
536 env_out
->crc
= crc32(0, env_out
->data
, ENV_SIZE
);
538 #ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT
539 env_out
->flags
= ++env_flags
; /* increase the serial */
545 void env_relocate(void)
547 if (gd
->env_valid
== ENV_INVALID
) {
548 #if defined(CONFIG_ENV_IS_NOWHERE) || defined(CONFIG_SPL_BUILD)
549 /* Environment not changable */
550 env_set_default(NULL
, 0);
552 bootstage_error(BOOTSTAGE_ID_NET_CHECKSUM
);
553 env_set_default("bad CRC", 0);
560 #ifdef CONFIG_AUTO_COMPLETE
561 int env_complete(char *var
, int maxv
, char *cmdv
[], int bufsz
, char *buf
,
564 struct env_entry
*match
;
569 * When doing $ completion, the first character should
570 * obviously be a '$'.
578 * The second one, if present, should be a '{', as some
579 * configuration of the u-boot shell expand ${var} but not
584 else if (var
[0] != '\0')
593 while ((idx
= hmatch_r(var
, idx
, &match
, &env_htab
))) {
594 int vallen
= strlen(match
->key
) + 1;
596 if (found
>= maxv
- 2 ||
597 bufsz
< vallen
+ (dollar_comp
? 3 : 0))
602 /* Add the '${' prefix to each var when doing $ completion. */
609 memcpy(buf
, match
->key
, vallen
);
615 * This one is a bit odd: vallen already contains the
616 * '\0' character but we need to add the '}' suffix,
617 * hence the buf - 1 here. strcpy() will add the '\0'
618 * character just after '}'. buf is then incremented
619 * to account for the extra '}' we just added.
621 strcpy(buf
- 1, "}");
626 qsort(cmdv
, found
, sizeof(cmdv
[0]), strcmp_compar
);
629 cmdv
[found
++] = dollar_comp
? "${...}" : "...";
636 #ifdef CONFIG_ENV_IMPORT_FDT
637 void env_import_fdt(void)
644 path
= env_get("env_fdt_path");
645 if (!path
|| !path
[0])
648 node
= ofnode_path(path
);
649 if (!ofnode_valid(node
)) {
650 printf("Warning: device tree node '%s' not found\n", path
);
654 for (res
= ofnode_first_property(node
, &prop
);
656 res
= ofnode_next_property(&prop
)) {
657 const char *name
, *val
;
659 val
= ofprop_get_property(&prop
, &name
, NULL
);