xref: /openbmc/u-boot/env/common.c (revision 0da90255083681a02b24528f80da9d4062ff634a)
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