1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * (C) Copyright 2000-2010
4  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
5  *
6  * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com>
7  * Andreas Heppel <aheppel@sysgo.de>
8  */
9 
10 #include <common.h>
11 #include <bootstage.h>
12 #include <command.h>
13 #include <env.h>
14 #include <env_internal.h>
15 #include <log.h>
16 #include <sort.h>
17 #include <asm/global_data.h>
18 #include <linux/stddef.h>
19 #include <search.h>
20 #include <errno.h>
21 #include <malloc.h>
22 #include <u-boot/crc.h>
23 #include <dm/ofnode.h>
24 #include <net.h>
25 #include <watchdog.h>
26 
27 DECLARE_GLOBAL_DATA_PTR;
28 
29 /************************************************************************
30  * Default settings to be used when no valid environment is found
31  */
32 #include <env_default.h>
33 
34 struct hsearch_data env_htab = {
35 	.change_ok = env_flags_validate,
36 };
37 
38 /*
39  * This env_set() function is defined in cmd/nvedit.c, since it calls
40  * _do_env_set(), whis is a static function in that file.
41  *
42  * int env_set(const char *varname, const char *varvalue);
43  */
44 
45 /**
46  * Set an environment variable to an integer value
47  *
48  * @param varname	Environment variable to set
49  * @param value		Value to set it to
50  * Return: 0 if ok, 1 on error
51  */
env_set_ulong(const char * varname,ulong value)52 int env_set_ulong(const char *varname, ulong value)
53 {
54 	/* TODO: this should be unsigned */
55 	char *str = simple_itoa(value);
56 
57 	return env_set(varname, str);
58 }
59 
60 /**
61  * Set an environment variable to an value in hex
62  *
63  * @param varname	Environment variable to set
64  * @param value		Value to set it to
65  * Return: 0 if ok, 1 on error
66  */
env_set_hex(const char * varname,ulong value)67 int env_set_hex(const char *varname, ulong value)
68 {
69 	char str[17];
70 
71 	sprintf(str, "%lx", value);
72 	return env_set(varname, str);
73 }
74 
env_get_hex(const char * varname,ulong default_val)75 ulong env_get_hex(const char *varname, ulong default_val)
76 {
77 	const char *s;
78 	ulong value;
79 	char *endp;
80 
81 	s = env_get(varname);
82 	if (s)
83 		value = hextoul(s, &endp);
84 	if (!s || endp == s)
85 		return default_val;
86 
87 	return value;
88 }
89 
eth_env_get_enetaddr(const char * name,uint8_t * enetaddr)90 int eth_env_get_enetaddr(const char *name, uint8_t *enetaddr)
91 {
92 	string_to_enetaddr(env_get(name), enetaddr);
93 	return is_valid_ethaddr(enetaddr);
94 }
95 
eth_env_set_enetaddr(const char * name,const uint8_t * enetaddr)96 int eth_env_set_enetaddr(const char *name, const uint8_t *enetaddr)
97 {
98 	char buf[ARP_HLEN_ASCII + 1];
99 
100 	if (eth_env_get_enetaddr(name, (uint8_t *)buf))
101 		return -EEXIST;
102 
103 	sprintf(buf, "%pM", enetaddr);
104 
105 	return env_set(name, buf);
106 }
107 
108 /*
109  * Look up variable from environment,
110  * return address of storage for that variable,
111  * or NULL if not found
112  */
env_get(const char * name)113 char *env_get(const char *name)
114 {
115 	if (gd->flags & GD_FLG_ENV_READY) { /* after import into hashtable */
116 		struct env_entry e, *ep;
117 
118 		schedule();
119 
120 		e.key	= name;
121 		e.data	= NULL;
122 		hsearch_r(e, ENV_FIND, &ep, &env_htab, 0);
123 
124 		return ep ? ep->data : NULL;
125 	}
126 
127 	/* restricted capabilities before import */
128 	if (env_get_f(name, (char *)(gd->env_buf), sizeof(gd->env_buf)) >= 0)
129 		return (char *)(gd->env_buf);
130 
131 	return NULL;
132 }
133 
134 /*
135  * Like env_get, but prints an error if envvar isn't defined in the
136  * environment.  It always returns what env_get does, so it can be used in
137  * place of env_get without changing error handling otherwise.
138  */
from_env(const char * envvar)139 char *from_env(const char *envvar)
140 {
141 	char *ret;
142 
143 	ret = env_get(envvar);
144 
145 	if (!ret)
146 		printf("missing environment variable: %s\n", envvar);
147 
148 	return ret;
149 }
150 
env_get_from_linear(const char * env,const char * name,char * buf,unsigned len)151 static int env_get_from_linear(const char *env, const char *name, char *buf,
152 			       unsigned len)
153 {
154 	const char *p, *end;
155 	size_t name_len;
156 
157 	if (name == NULL || *name == '\0')
158 		return -1;
159 
160 	name_len = strlen(name);
161 
162 	for (p = env; *p != '\0'; p = end + 1) {
163 		const char *value;
164 		unsigned res;
165 
166 		for (end = p; *end != '\0'; ++end)
167 			if (end - env >= CONFIG_ENV_SIZE)
168 				return -1;
169 
170 		if (strncmp(name, p, name_len) || p[name_len] != '=')
171 			continue;
172 		value = &p[name_len + 1];
173 
174 		res = end - value;
175 		memcpy(buf, value, min(len, res + 1));
176 
177 		if (len <= res) {
178 			buf[len - 1] = '\0';
179 			printf("env_buf [%u bytes] too small for value of \"%s\"\n",
180 			       len, name);
181 		}
182 
183 		return res;
184 	}
185 
186 	return -1;
187 }
188 
189 /*
190  * Look up variable from environment for restricted C runtime env.
191  */
env_get_f(const char * name,char * buf,unsigned len)192 int env_get_f(const char *name, char *buf, unsigned len)
193 {
194 	const char *env;
195 
196 	if (gd->env_valid == ENV_INVALID)
197 		env = default_environment;
198 	else
199 		env = (const char *)gd->env_addr;
200 
201 	return env_get_from_linear(env, name, buf, len);
202 }
203 
204 /**
205  * Decode the integer value of an environment variable and return it.
206  *
207  * @param name		Name of environment variable
208  * @param base		Number base to use (normally 10, or 16 for hex)
209  * @param default_val	Default value to return if the variable is not
210  *			found
211  * Return: the decoded value, or default_val if not found
212  */
env_get_ulong(const char * name,int base,ulong default_val)213 ulong env_get_ulong(const char *name, int base, ulong default_val)
214 {
215 	/*
216 	 * We can use env_get() here, even before relocation, since the
217 	 * environment variable value is an integer and thus short.
218 	 */
219 	const char *str = env_get(name);
220 
221 	return str ? simple_strtoul(str, NULL, base) : default_val;
222 }
223 
224 /*
225  * Read an environment variable as a boolean
226  * Return -1 if variable does not exist (default to true)
227  */
env_get_yesno(const char * var)228 int env_get_yesno(const char *var)
229 {
230 	char *s = env_get(var);
231 
232 	if (s == NULL)
233 		return -1;
234 	return (*s == '1' || *s == 'y' || *s == 'Y' || *s == 't' || *s == 'T') ?
235 		1 : 0;
236 }
237 
env_get_autostart(void)238 bool env_get_autostart(void)
239 {
240 	return env_get_yesno("autostart") == 1;
241 }
242 
243 /*
244  * Look up the variable from the default environment
245  */
env_get_default(const char * name)246 char *env_get_default(const char *name)
247 {
248 	if (env_get_from_linear(default_environment, name,
249 				(char *)(gd->env_buf),
250 				sizeof(gd->env_buf)) >= 0)
251 		return (char *)(gd->env_buf);
252 
253 	return NULL;
254 }
255 
env_set_default(const char * s,int flags)256 void env_set_default(const char *s, int flags)
257 {
258 	if (s) {
259 		if ((flags & H_INTERACTIVE) == 0) {
260 			printf("*** Warning - %s, "
261 				"using default environment\n\n", s);
262 		} else {
263 			puts(s);
264 		}
265 	} else {
266 		debug("Using default environment\n");
267 	}
268 
269 	flags |= H_DEFAULT;
270 	if (himport_r(&env_htab, default_environment,
271 			sizeof(default_environment), '\0', flags, 0,
272 			0, NULL) == 0) {
273 		pr_err("## Error: Environment import failed: errno = %d\n",
274 		       errno);
275 		return;
276 	}
277 
278 	gd->flags |= GD_FLG_ENV_READY;
279 	gd->flags |= GD_FLG_ENV_DEFAULT;
280 }
281 
282 
283 /* [re]set individual variables to their value in the default environment */
env_set_default_vars(int nvars,char * const vars[],int flags)284 int env_set_default_vars(int nvars, char * const vars[], int flags)
285 {
286 	/*
287 	 * Special use-case: import from default environment
288 	 * (and use \0 as a separator)
289 	 */
290 	flags |= H_NOCLEAR | H_DEFAULT;
291 	return himport_r(&env_htab, default_environment,
292 				sizeof(default_environment), '\0',
293 				flags, 0, nvars, vars);
294 }
295 
296 /*
297  * Check if CRC is valid and (if yes) import the environment.
298  * Note that "buf" may or may not be aligned.
299  */
env_import(const char * buf,int check,int flags)300 int env_import(const char *buf, int check, int flags)
301 {
302 	env_t *ep = (env_t *)buf;
303 
304 	if (check) {
305 		uint32_t crc;
306 
307 		memcpy(&crc, &ep->crc, sizeof(crc));
308 
309 		if (crc32(0, ep->data, ENV_SIZE) != crc) {
310 			env_set_default("bad CRC", 0);
311 			return -ENOMSG; /* needed for env_load() */
312 		}
313 	}
314 
315 	if (himport_r(&env_htab, (char *)ep->data, ENV_SIZE, '\0', flags, 0,
316 			0, NULL)) {
317 		gd->flags |= GD_FLG_ENV_READY;
318 		return 0;
319 	}
320 
321 	pr_err("Cannot import environment: errno = %d\n", errno);
322 
323 	env_set_default("import failed", 0);
324 
325 	return -EIO;
326 }
327 
328 #ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT
329 static unsigned char env_flags;
330 
env_check_redund(const char * buf1,int buf1_read_fail,const char * buf2,int buf2_read_fail)331 int env_check_redund(const char *buf1, int buf1_read_fail,
332 		     const char *buf2, int buf2_read_fail)
333 {
334 	int crc1_ok = 0, crc2_ok = 0;
335 	env_t *tmp_env1, *tmp_env2;
336 
337 	tmp_env1 = (env_t *)buf1;
338 	tmp_env2 = (env_t *)buf2;
339 
340 	if (buf1_read_fail && buf2_read_fail) {
341 		puts("*** Error - No Valid Environment Area found\n");
342 		return -EIO;
343 	} else if (buf1_read_fail || buf2_read_fail) {
344 		puts("*** Warning - some problems detected ");
345 		puts("reading environment; recovered successfully\n");
346 	}
347 
348 	if (!buf1_read_fail)
349 		crc1_ok = crc32(0, tmp_env1->data, ENV_SIZE) ==
350 				tmp_env1->crc;
351 	if (!buf2_read_fail)
352 		crc2_ok = crc32(0, tmp_env2->data, ENV_SIZE) ==
353 				tmp_env2->crc;
354 
355 	if (!crc1_ok && !crc2_ok) {
356 		return -ENOMSG; /* needed for env_load() */
357 	} else if (crc1_ok && !crc2_ok) {
358 		gd->env_valid = ENV_VALID;
359 	} else if (!crc1_ok && crc2_ok) {
360 		gd->env_valid = ENV_REDUND;
361 	} else {
362 		/* both ok - check serial */
363 		if (tmp_env1->flags == 255 && tmp_env2->flags == 0)
364 			gd->env_valid = ENV_REDUND;
365 		else if (tmp_env2->flags == 255 && tmp_env1->flags == 0)
366 			gd->env_valid = ENV_VALID;
367 		else if (tmp_env1->flags > tmp_env2->flags)
368 			gd->env_valid = ENV_VALID;
369 		else if (tmp_env2->flags > tmp_env1->flags)
370 			gd->env_valid = ENV_REDUND;
371 		else /* flags are equal - almost impossible */
372 			gd->env_valid = ENV_VALID;
373 	}
374 
375 	return 0;
376 }
377 
env_import_redund(const char * buf1,int buf1_read_fail,const char * buf2,int buf2_read_fail,int flags)378 int env_import_redund(const char *buf1, int buf1_read_fail,
379 		      const char *buf2, int buf2_read_fail,
380 		      int flags)
381 {
382 	env_t *ep;
383 	int ret;
384 
385 	ret = env_check_redund(buf1, buf1_read_fail, buf2, buf2_read_fail);
386 
387 	if (ret == -EIO) {
388 		env_set_default("bad env area", 0);
389 		return -EIO;
390 	} else if (ret == -ENOMSG) {
391 		env_set_default("bad CRC", 0);
392 		return -ENOMSG;
393 	}
394 
395 	if (gd->env_valid == ENV_VALID)
396 		ep = (env_t *)buf1;
397 	else
398 		ep = (env_t *)buf2;
399 
400 	env_flags = ep->flags;
401 
402 	return env_import((char *)ep, 0, flags);
403 }
404 #endif /* CONFIG_SYS_REDUNDAND_ENVIRONMENT */
405 
406 /* Export the environment and generate CRC for it. */
env_export(env_t * env_out)407 int env_export(env_t *env_out)
408 {
409 	char *res;
410 	ssize_t	len;
411 
412 	res = (char *)env_out->data;
413 	len = hexport_r(&env_htab, '\0', 0, &res, ENV_SIZE, 0, NULL);
414 	if (len < 0) {
415 		pr_err("Cannot export environment: errno = %d\n", errno);
416 		return 1;
417 	}
418 
419 	env_out->crc = crc32(0, env_out->data, ENV_SIZE);
420 
421 #ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT
422 	env_out->flags = ++env_flags; /* increase the serial */
423 #endif
424 
425 	return 0;
426 }
427 
env_relocate(void)428 void env_relocate(void)
429 {
430 #if defined(CONFIG_NEEDS_MANUAL_RELOC)
431 	env_reloc();
432 	env_fix_drivers();
433 	env_htab.change_ok += gd->reloc_off;
434 #endif
435 	if (gd->env_valid == ENV_INVALID) {
436 #if defined(CONFIG_ENV_IS_NOWHERE) || defined(CONFIG_SPL_BUILD)
437 		/* Environment not changable */
438 		env_set_default(NULL, 0);
439 #else
440 		bootstage_error(BOOTSTAGE_ID_NET_CHECKSUM);
441 		env_set_default("bad CRC", 0);
442 #endif
443 	} else {
444 		env_load();
445 	}
446 }
447 
448 #ifdef CONFIG_AUTO_COMPLETE
env_complete(char * var,int maxv,char * cmdv[],int bufsz,char * buf,bool dollar_comp)449 int env_complete(char *var, int maxv, char *cmdv[], int bufsz, char *buf,
450 		 bool dollar_comp)
451 {
452 	struct env_entry *match;
453 	int found, idx;
454 
455 	if (dollar_comp) {
456 		/*
457 		 * When doing $ completion, the first character should
458 		 * obviously be a '$'.
459 		 */
460 		if (var[0] != '$')
461 			return 0;
462 
463 		var++;
464 
465 		/*
466 		 * The second one, if present, should be a '{', as some
467 		 * configuration of the u-boot shell expand ${var} but not
468 		 * $var.
469 		 */
470 		if (var[0] == '{')
471 			var++;
472 		else if (var[0] != '\0')
473 			return 0;
474 	}
475 
476 	idx = 0;
477 	found = 0;
478 	cmdv[0] = NULL;
479 
480 
481 	while ((idx = hmatch_r(var, idx, &match, &env_htab))) {
482 		int vallen = strlen(match->key) + 1;
483 
484 		if (found >= maxv - 2 ||
485 		    bufsz < vallen + (dollar_comp ? 3 : 0))
486 			break;
487 
488 		cmdv[found++] = buf;
489 
490 		/* Add the '${' prefix to each var when doing $ completion. */
491 		if (dollar_comp) {
492 			strcpy(buf, "${");
493 			buf += 2;
494 			bufsz -= 3;
495 		}
496 
497 		memcpy(buf, match->key, vallen);
498 		buf += vallen;
499 		bufsz -= vallen;
500 
501 		if (dollar_comp) {
502 			/*
503 			 * This one is a bit odd: vallen already contains the
504 			 * '\0' character but we need to add the '}' suffix,
505 			 * hence the buf - 1 here. strcpy() will add the '\0'
506 			 * character just after '}'. buf is then incremented
507 			 * to account for the extra '}' we just added.
508 			 */
509 			strcpy(buf - 1, "}");
510 			buf++;
511 		}
512 	}
513 
514 	qsort(cmdv, found, sizeof(cmdv[0]), strcmp_compar);
515 
516 	if (idx)
517 		cmdv[found++] = dollar_comp ? "${...}" : "...";
518 
519 	cmdv[found] = NULL;
520 	return found;
521 }
522 #endif
523 
524 #ifdef CONFIG_ENV_IMPORT_FDT
env_import_fdt(void)525 void env_import_fdt(void)
526 {
527 	const char *path;
528 	struct ofprop prop;
529 	ofnode node;
530 	int res;
531 
532 	path = env_get("env_fdt_path");
533 	if (!path || !path[0])
534 		return;
535 
536 	node = ofnode_path(path);
537 	if (!ofnode_valid(node)) {
538 		printf("Warning: device tree node '%s' not found\n", path);
539 		return;
540 	}
541 
542 	for (res = ofnode_first_property(node, &prop);
543 	     !res;
544 	     res = ofnode_next_property(&prop)) {
545 		const char *name, *val;
546 
547 		val = ofprop_get_property(&prop, &name, NULL);
548 		env_set(name, val);
549 	}
550 }
551 #endif
552