183d290c5STom Rini // SPDX-License-Identifier: GPL-2.0+
20649cd0dSSimon Glass /*
30649cd0dSSimon Glass * (C) Copyright 2000-2010
40649cd0dSSimon Glass * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
50649cd0dSSimon Glass *
60649cd0dSSimon Glass * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com>
70649cd0dSSimon Glass * Andreas Heppel <aheppel@sysgo.de>
80649cd0dSSimon Glass */
90649cd0dSSimon Glass
100649cd0dSSimon Glass #include <common.h>
110649cd0dSSimon Glass #include <command.h>
120649cd0dSSimon Glass #include <environment.h>
130649cd0dSSimon Glass #include <linux/stddef.h>
140649cd0dSSimon Glass #include <search.h>
150649cd0dSSimon Glass #include <errno.h>
160649cd0dSSimon Glass #include <malloc.h>
170649cd0dSSimon Glass
180649cd0dSSimon Glass DECLARE_GLOBAL_DATA_PTR;
190649cd0dSSimon Glass
200649cd0dSSimon Glass /************************************************************************
210649cd0dSSimon Glass * Default settings to be used when no valid environment is found
220649cd0dSSimon Glass */
230649cd0dSSimon Glass #include <env_default.h>
240649cd0dSSimon Glass
250649cd0dSSimon Glass struct hsearch_data env_htab = {
260649cd0dSSimon Glass .change_ok = env_flags_validate,
270649cd0dSSimon Glass };
280649cd0dSSimon Glass
290649cd0dSSimon Glass /*
300649cd0dSSimon Glass * Read an environment variable as a boolean
310649cd0dSSimon Glass * Return -1 if variable does not exist (default to true)
320649cd0dSSimon Glass */
env_get_yesno(const char * var)33bfebc8c9SSimon Glass int env_get_yesno(const char *var)
340649cd0dSSimon Glass {
3500caae6dSSimon Glass char *s = env_get(var);
360649cd0dSSimon Glass
370649cd0dSSimon Glass if (s == NULL)
380649cd0dSSimon Glass return -1;
390649cd0dSSimon Glass return (*s == '1' || *s == 'y' || *s == 'Y' || *s == 't' || *s == 'T') ?
400649cd0dSSimon Glass 1 : 0;
410649cd0dSSimon Glass }
420649cd0dSSimon Glass
430649cd0dSSimon Glass /*
440649cd0dSSimon Glass * Look up the variable from the default environment
450649cd0dSSimon Glass */
env_get_default(const char * name)46723806ccSSimon Glass char *env_get_default(const char *name)
470649cd0dSSimon Glass {
480649cd0dSSimon Glass char *ret_val;
490649cd0dSSimon Glass unsigned long really_valid = gd->env_valid;
500649cd0dSSimon Glass unsigned long real_gd_flags = gd->flags;
510649cd0dSSimon Glass
520649cd0dSSimon Glass /* Pretend that the image is bad. */
530649cd0dSSimon Glass gd->flags &= ~GD_FLG_ENV_READY;
542d7cb5b4SSimon Glass gd->env_valid = ENV_INVALID;
5500caae6dSSimon Glass ret_val = env_get(name);
560649cd0dSSimon Glass gd->env_valid = really_valid;
570649cd0dSSimon Glass gd->flags = real_gd_flags;
580649cd0dSSimon Glass return ret_val;
590649cd0dSSimon Glass }
600649cd0dSSimon Glass
set_default_env(const char * s,int flags)61c5d548a9SYaniv Levinsky void set_default_env(const char *s, int flags)
620649cd0dSSimon Glass {
630649cd0dSSimon Glass if (sizeof(default_environment) > ENV_SIZE) {
640649cd0dSSimon Glass puts("*** Error - default environment is too large\n\n");
650649cd0dSSimon Glass return;
660649cd0dSSimon Glass }
670649cd0dSSimon Glass
680649cd0dSSimon Glass if (s) {
69c5d548a9SYaniv Levinsky if ((flags & H_INTERACTIVE) == 0) {
700649cd0dSSimon Glass printf("*** Warning - %s, "
71c5d548a9SYaniv Levinsky "using default environment\n\n", s);
720649cd0dSSimon Glass } else {
730649cd0dSSimon Glass puts(s);
740649cd0dSSimon Glass }
750649cd0dSSimon Glass } else {
7658ae9990SMaxime Ripard debug("Using default environment\n");
770649cd0dSSimon Glass }
780649cd0dSSimon Glass
790649cd0dSSimon Glass if (himport_r(&env_htab, (char *)default_environment,
800649cd0dSSimon Glass sizeof(default_environment), '\0', flags, 0,
810649cd0dSSimon Glass 0, NULL) == 0)
826c90f623SQuentin Schulz pr_err("## Error: Environment import failed: errno = %d\n",
836c90f623SQuentin Schulz errno);
840649cd0dSSimon Glass
850649cd0dSSimon Glass gd->flags |= GD_FLG_ENV_READY;
860649cd0dSSimon Glass gd->flags |= GD_FLG_ENV_DEFAULT;
870649cd0dSSimon Glass }
880649cd0dSSimon Glass
890649cd0dSSimon Glass
900649cd0dSSimon Glass /* [re]set individual variables to their value in the default environment */
set_default_vars(int nvars,char * const vars[],int flags)91477f8116SYaniv Levinsky int set_default_vars(int nvars, char * const vars[], int flags)
920649cd0dSSimon Glass {
930649cd0dSSimon Glass /*
940649cd0dSSimon Glass * Special use-case: import from default environment
950649cd0dSSimon Glass * (and use \0 as a separator)
960649cd0dSSimon Glass */
975a04264eSYaniv Levinsky flags |= H_NOCLEAR;
980649cd0dSSimon Glass return himport_r(&env_htab, (const char *)default_environment,
990649cd0dSSimon Glass sizeof(default_environment), '\0',
100477f8116SYaniv Levinsky flags, 0, nvars, vars);
1010649cd0dSSimon Glass }
1020649cd0dSSimon Glass
1030649cd0dSSimon Glass /*
1040649cd0dSSimon Glass * Check if CRC is valid and (if yes) import the environment.
1050649cd0dSSimon Glass * Note that "buf" may or may not be aligned.
1060649cd0dSSimon Glass */
env_import(const char * buf,int check)1070649cd0dSSimon Glass int env_import(const char *buf, int check)
1080649cd0dSSimon Glass {
1090649cd0dSSimon Glass env_t *ep = (env_t *)buf;
1100649cd0dSSimon Glass
1110649cd0dSSimon Glass if (check) {
1120649cd0dSSimon Glass uint32_t crc;
1130649cd0dSSimon Glass
1140649cd0dSSimon Glass memcpy(&crc, &ep->crc, sizeof(crc));
1150649cd0dSSimon Glass
1160649cd0dSSimon Glass if (crc32(0, ep->data, ENV_SIZE) != crc) {
117c5d548a9SYaniv Levinsky set_default_env("bad CRC", 0);
118*49d04c58SSam Protsenko return -ENOMSG; /* needed for env_load() */
1190649cd0dSSimon Glass }
1200649cd0dSSimon Glass }
1210649cd0dSSimon Glass
1220649cd0dSSimon Glass if (himport_r(&env_htab, (char *)ep->data, ENV_SIZE, '\0', 0, 0,
1230649cd0dSSimon Glass 0, NULL)) {
1240649cd0dSSimon Glass gd->flags |= GD_FLG_ENV_READY;
12542a1820bSSimon Goldschmidt return 0;
1260649cd0dSSimon Glass }
1270649cd0dSSimon Glass
1289b643e31SMasahiro Yamada pr_err("Cannot import environment: errno = %d\n", errno);
1290649cd0dSSimon Glass
130c5d548a9SYaniv Levinsky set_default_env("import failed", 0);
1310649cd0dSSimon Glass
13242a1820bSSimon Goldschmidt return -EIO;
1330649cd0dSSimon Glass }
1340649cd0dSSimon Glass
1350649cd0dSSimon Glass #ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT
1360649cd0dSSimon Glass static unsigned char env_flags;
1370649cd0dSSimon Glass
env_import_redund(const char * buf1,int buf1_read_fail,const char * buf2,int buf2_read_fail)13831f044bdSSimon Goldschmidt int env_import_redund(const char *buf1, int buf1_read_fail,
13931f044bdSSimon Goldschmidt const char *buf2, int buf2_read_fail)
1400649cd0dSSimon Glass {
1410649cd0dSSimon Glass int crc1_ok, crc2_ok;
1420649cd0dSSimon Glass env_t *ep, *tmp_env1, *tmp_env2;
1430649cd0dSSimon Glass
1440649cd0dSSimon Glass tmp_env1 = (env_t *)buf1;
1450649cd0dSSimon Glass tmp_env2 = (env_t *)buf2;
1460649cd0dSSimon Glass
14731f044bdSSimon Goldschmidt if (buf1_read_fail && buf2_read_fail) {
14831f044bdSSimon Goldschmidt puts("*** Error - No Valid Environment Area found\n");
14931f044bdSSimon Goldschmidt } else if (buf1_read_fail || buf2_read_fail) {
15031f044bdSSimon Goldschmidt puts("*** Warning - some problems detected ");
15131f044bdSSimon Goldschmidt puts("reading environment; recovered successfully\n");
15231f044bdSSimon Goldschmidt }
15331f044bdSSimon Goldschmidt
15431f044bdSSimon Goldschmidt if (buf1_read_fail && buf2_read_fail) {
155c5d548a9SYaniv Levinsky set_default_env("bad env area", 0);
15631f044bdSSimon Goldschmidt return -EIO;
15731f044bdSSimon Goldschmidt } else if (!buf1_read_fail && buf2_read_fail) {
15831f044bdSSimon Goldschmidt gd->env_valid = ENV_VALID;
15931f044bdSSimon Goldschmidt return env_import((char *)tmp_env1, 1);
16031f044bdSSimon Goldschmidt } else if (buf1_read_fail && !buf2_read_fail) {
16131f044bdSSimon Goldschmidt gd->env_valid = ENV_REDUND;
16231f044bdSSimon Goldschmidt return env_import((char *)tmp_env2, 1);
16331f044bdSSimon Goldschmidt }
16431f044bdSSimon Goldschmidt
1650649cd0dSSimon Glass crc1_ok = crc32(0, tmp_env1->data, ENV_SIZE) ==
1660649cd0dSSimon Glass tmp_env1->crc;
1670649cd0dSSimon Glass crc2_ok = crc32(0, tmp_env2->data, ENV_SIZE) ==
1680649cd0dSSimon Glass tmp_env2->crc;
1690649cd0dSSimon Glass
1700649cd0dSSimon Glass if (!crc1_ok && !crc2_ok) {
171c5d548a9SYaniv Levinsky set_default_env("bad CRC", 0);
172*49d04c58SSam Protsenko return -ENOMSG; /* needed for env_load() */
1730649cd0dSSimon Glass } else if (crc1_ok && !crc2_ok) {
1742d7cb5b4SSimon Glass gd->env_valid = ENV_VALID;
1750649cd0dSSimon Glass } else if (!crc1_ok && crc2_ok) {
1762d7cb5b4SSimon Glass gd->env_valid = ENV_REDUND;
1770649cd0dSSimon Glass } else {
1780649cd0dSSimon Glass /* both ok - check serial */
1790649cd0dSSimon Glass if (tmp_env1->flags == 255 && tmp_env2->flags == 0)
1802d7cb5b4SSimon Glass gd->env_valid = ENV_REDUND;
1810649cd0dSSimon Glass else if (tmp_env2->flags == 255 && tmp_env1->flags == 0)
1822d7cb5b4SSimon Glass gd->env_valid = ENV_VALID;
1830649cd0dSSimon Glass else if (tmp_env1->flags > tmp_env2->flags)
1842d7cb5b4SSimon Glass gd->env_valid = ENV_VALID;
1850649cd0dSSimon Glass else if (tmp_env2->flags > tmp_env1->flags)
1862d7cb5b4SSimon Glass gd->env_valid = ENV_REDUND;
1870649cd0dSSimon Glass else /* flags are equal - almost impossible */
1882d7cb5b4SSimon Glass gd->env_valid = ENV_VALID;
1890649cd0dSSimon Glass }
1900649cd0dSSimon Glass
1912d7cb5b4SSimon Glass if (gd->env_valid == ENV_VALID)
1920649cd0dSSimon Glass ep = tmp_env1;
1930649cd0dSSimon Glass else
1940649cd0dSSimon Glass ep = tmp_env2;
1950649cd0dSSimon Glass
1960649cd0dSSimon Glass env_flags = ep->flags;
1970649cd0dSSimon Glass return env_import((char *)ep, 0);
1980649cd0dSSimon Glass }
1990649cd0dSSimon Glass #endif /* CONFIG_SYS_REDUNDAND_ENVIRONMENT */
2000649cd0dSSimon Glass
2010649cd0dSSimon Glass /* Export the environment and generate CRC for it. */
env_export(env_t * env_out)2020649cd0dSSimon Glass int env_export(env_t *env_out)
2030649cd0dSSimon Glass {
2040649cd0dSSimon Glass char *res;
2050649cd0dSSimon Glass ssize_t len;
2060649cd0dSSimon Glass
2070649cd0dSSimon Glass res = (char *)env_out->data;
2080649cd0dSSimon Glass len = hexport_r(&env_htab, '\0', 0, &res, ENV_SIZE, 0, NULL);
2090649cd0dSSimon Glass if (len < 0) {
2109b643e31SMasahiro Yamada pr_err("Cannot export environment: errno = %d\n", errno);
2110649cd0dSSimon Glass return 1;
2120649cd0dSSimon Glass }
2130649cd0dSSimon Glass
2140649cd0dSSimon Glass env_out->crc = crc32(0, env_out->data, ENV_SIZE);
2150649cd0dSSimon Glass
2160649cd0dSSimon Glass #ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT
2170649cd0dSSimon Glass env_out->flags = ++env_flags; /* increase the serial */
2180649cd0dSSimon Glass #endif
2190649cd0dSSimon Glass
2200649cd0dSSimon Glass return 0;
2210649cd0dSSimon Glass }
2220649cd0dSSimon Glass
env_relocate(void)2230649cd0dSSimon Glass void env_relocate(void)
2240649cd0dSSimon Glass {
2250649cd0dSSimon Glass #if defined(CONFIG_NEEDS_MANUAL_RELOC)
2260649cd0dSSimon Glass env_reloc();
2277bcdf195SSiva Durga Prasad Paladugu env_fix_drivers();
2280649cd0dSSimon Glass env_htab.change_ok += gd->reloc_off;
2290649cd0dSSimon Glass #endif
2302d7cb5b4SSimon Glass if (gd->env_valid == ENV_INVALID) {
2310649cd0dSSimon Glass #if defined(CONFIG_ENV_IS_NOWHERE) || defined(CONFIG_SPL_BUILD)
2320649cd0dSSimon Glass /* Environment not changable */
233c5d548a9SYaniv Levinsky set_default_env(NULL, 0);
2340649cd0dSSimon Glass #else
2350649cd0dSSimon Glass bootstage_error(BOOTSTAGE_ID_NET_CHECKSUM);
236c5d548a9SYaniv Levinsky set_default_env("bad CRC", 0);
2370649cd0dSSimon Glass #endif
2380649cd0dSSimon Glass } else {
239310fb14bSSimon Glass env_load();
2400649cd0dSSimon Glass }
2410649cd0dSSimon Glass }
2420649cd0dSSimon Glass
24303dcf17dSBoris Brezillon #ifdef CONFIG_AUTO_COMPLETE
env_complete(char * var,int maxv,char * cmdv[],int bufsz,char * buf,bool dollar_comp)24403dcf17dSBoris Brezillon int env_complete(char *var, int maxv, char *cmdv[], int bufsz, char *buf,
24503dcf17dSBoris Brezillon bool dollar_comp)
2460649cd0dSSimon Glass {
2470649cd0dSSimon Glass ENTRY *match;
2480649cd0dSSimon Glass int found, idx;
2490649cd0dSSimon Glass
25003dcf17dSBoris Brezillon if (dollar_comp) {
25103dcf17dSBoris Brezillon /*
25203dcf17dSBoris Brezillon * When doing $ completion, the first character should
25303dcf17dSBoris Brezillon * obviously be a '$'.
25403dcf17dSBoris Brezillon */
25503dcf17dSBoris Brezillon if (var[0] != '$')
25603dcf17dSBoris Brezillon return 0;
25703dcf17dSBoris Brezillon
25803dcf17dSBoris Brezillon var++;
25903dcf17dSBoris Brezillon
26003dcf17dSBoris Brezillon /*
26103dcf17dSBoris Brezillon * The second one, if present, should be a '{', as some
26203dcf17dSBoris Brezillon * configuration of the u-boot shell expand ${var} but not
26303dcf17dSBoris Brezillon * $var.
26403dcf17dSBoris Brezillon */
26503dcf17dSBoris Brezillon if (var[0] == '{')
26603dcf17dSBoris Brezillon var++;
26703dcf17dSBoris Brezillon else if (var[0] != '\0')
26803dcf17dSBoris Brezillon return 0;
26903dcf17dSBoris Brezillon }
27003dcf17dSBoris Brezillon
2710649cd0dSSimon Glass idx = 0;
2720649cd0dSSimon Glass found = 0;
2730649cd0dSSimon Glass cmdv[0] = NULL;
2740649cd0dSSimon Glass
27503dcf17dSBoris Brezillon
2760649cd0dSSimon Glass while ((idx = hmatch_r(var, idx, &match, &env_htab))) {
2770649cd0dSSimon Glass int vallen = strlen(match->key) + 1;
2780649cd0dSSimon Glass
27903dcf17dSBoris Brezillon if (found >= maxv - 2 ||
28003dcf17dSBoris Brezillon bufsz < vallen + (dollar_comp ? 3 : 0))
2810649cd0dSSimon Glass break;
2820649cd0dSSimon Glass
2830649cd0dSSimon Glass cmdv[found++] = buf;
28403dcf17dSBoris Brezillon
28503dcf17dSBoris Brezillon /* Add the '${' prefix to each var when doing $ completion. */
28603dcf17dSBoris Brezillon if (dollar_comp) {
28703dcf17dSBoris Brezillon strcpy(buf, "${");
28803dcf17dSBoris Brezillon buf += 2;
28903dcf17dSBoris Brezillon bufsz -= 3;
29003dcf17dSBoris Brezillon }
29103dcf17dSBoris Brezillon
2920649cd0dSSimon Glass memcpy(buf, match->key, vallen);
2930649cd0dSSimon Glass buf += vallen;
2940649cd0dSSimon Glass bufsz -= vallen;
29503dcf17dSBoris Brezillon
29603dcf17dSBoris Brezillon if (dollar_comp) {
29703dcf17dSBoris Brezillon /*
29803dcf17dSBoris Brezillon * This one is a bit odd: vallen already contains the
29903dcf17dSBoris Brezillon * '\0' character but we need to add the '}' suffix,
30003dcf17dSBoris Brezillon * hence the buf - 1 here. strcpy() will add the '\0'
30103dcf17dSBoris Brezillon * character just after '}'. buf is then incremented
30203dcf17dSBoris Brezillon * to account for the extra '}' we just added.
30303dcf17dSBoris Brezillon */
30403dcf17dSBoris Brezillon strcpy(buf - 1, "}");
30503dcf17dSBoris Brezillon buf++;
30603dcf17dSBoris Brezillon }
3070649cd0dSSimon Glass }
3080649cd0dSSimon Glass
3090649cd0dSSimon Glass qsort(cmdv, found, sizeof(cmdv[0]), strcmp_compar);
3100649cd0dSSimon Glass
3110649cd0dSSimon Glass if (idx)
31203dcf17dSBoris Brezillon cmdv[found++] = dollar_comp ? "${...}" : "...";
3130649cd0dSSimon Glass
3140649cd0dSSimon Glass cmdv[found] = NULL;
3150649cd0dSSimon Glass return found;
3160649cd0dSSimon Glass }
3170649cd0dSSimon Glass #endif
318