1 /*
2  * (C) Copyright 2000-2002
3  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
4  *
5  * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com>
6  * Andreas Heppel <aheppel@sysgo.de>
7 
8  * See file CREDITS for list of people who contributed to this
9  * project.
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License as
13  * published by the Free Software Foundation; either version 2 of
14  * the License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
24  * MA 02111-1307 USA
25  */
26 
27 /**************************************************************************
28  *
29  * Support for persistent environment data
30  *
31  * The "environment" is stored as a list of '\0' terminated
32  * "name=value" strings. The end of the list is marked by a double
33  * '\0'. New entries are always added at the end. Deleting an entry
34  * shifts the remaining entries to the front. Replacing an entry is a
35  * combination of deleting the old value and adding the new one.
36  *
37  * The environment is preceeded by a 32 bit CRC over the data part.
38  *
39  **************************************************************************
40  */
41 
42 #include <common.h>
43 #include <command.h>
44 #include <environment.h>
45 #if defined(CONFIG_CMD_EDITENV)
46 #include <malloc.h>
47 #endif
48 #include <watchdog.h>
49 #include <serial.h>
50 #include <linux/stddef.h>
51 #include <asm/byteorder.h>
52 #if defined(CONFIG_CMD_NET)
53 #include <net.h>
54 #endif
55 
56 DECLARE_GLOBAL_DATA_PTR;
57 
58 #if !defined(CONFIG_ENV_IS_IN_EEPROM)	&& \
59     !defined(CONFIG_ENV_IS_IN_FLASH)	&& \
60     !defined(CONFIG_ENV_IS_IN_DATAFLASH)	&& \
61     !defined(CONFIG_ENV_IS_IN_MG_DISK)	&& \
62     !defined(CONFIG_ENV_IS_IN_NAND)	&& \
63     !defined(CONFIG_ENV_IS_IN_NVRAM)	&& \
64     !defined(CONFIG_ENV_IS_IN_ONENAND)	&& \
65     !defined(CONFIG_ENV_IS_IN_SPI_FLASH)	&& \
66     !defined(CONFIG_ENV_IS_NOWHERE)
67 # error Define one of CONFIG_ENV_IS_IN_{EEPROM|FLASH|DATAFLASH|ONENAND|\
68 SPI_FLASH|MG_DISK|NVRAM|NOWHERE}
69 #endif
70 
71 #define XMK_STR(x)	#x
72 #define MK_STR(x)	XMK_STR(x)
73 
74 /************************************************************************
75 ************************************************************************/
76 
77 /*
78  * Table with supported baudrates (defined in config_xyz.h)
79  */
80 static const unsigned long baudrate_table[] = CONFIG_SYS_BAUDRATE_TABLE;
81 #define	N_BAUDRATES (sizeof(baudrate_table) / sizeof(baudrate_table[0]))
82 
83 /*
84  * This variable is incremented on each do_setenv (), so it can
85  * be used via get_env_id() as an indication, if the environment
86  * has changed or not. So it is possible to reread an environment
87  * variable only if the environment was changed ... done so for
88  * example in NetInitLoop()
89  */
90 static int env_id = 1;
91 
get_env_id(void)92 int get_env_id (void)
93 {
94 	return env_id;
95 }
96 /************************************************************************
97  * Command interface: print one or all environment variables
98  */
99 
100 /*
101  * state 0: finish printing this string and return (matched!)
102  * state 1: no matching to be done; print everything
103  * state 2: continue searching for matched name
104  */
printenv(char * name,int state)105 static int printenv(char *name, int state)
106 {
107 	int i, j;
108 	char c, buf[17];
109 
110 	i = 0;
111 	buf[16] = '\0';
112 
113 	while (state && env_get_char(i) != '\0') {
114 		if (state == 2 && envmatch((uchar *)name, i) >= 0)
115 			state = 0;
116 
117 		j = 0;
118 		do {
119 			buf[j++] = c = env_get_char(i++);
120 			if (j == sizeof(buf) - 1) {
121 				if (state <= 1)
122 					puts(buf);
123 				j = 0;
124 			}
125 		} while (c != '\0');
126 
127 		if (state <= 1) {
128 			if (j)
129 				puts(buf);
130 			putc('\n');
131 		}
132 
133 		if (ctrlc())
134 			return -1;
135 	}
136 
137 	if (state == 0)
138 		i = 0;
139 	return i;
140 }
141 
do_printenv(cmd_tbl_t * cmdtp,int flag,int argc,char * argv[])142 int do_printenv (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
143 {
144 	int i;
145 	int rcode = 0;
146 
147 	if (argc == 1) {
148 		/* print all env vars */
149 		rcode = printenv(NULL, 1);
150 		if (rcode < 0)
151 			return 1;
152 		printf("\nEnvironment size: %d/%ld bytes\n",
153 			rcode, (ulong)ENV_SIZE);
154 		return 0;
155 	}
156 
157 	/* print selected env vars */
158 	for (i = 1; i < argc; ++i) {
159 		char *name = argv[i];
160 		if (printenv(name, 2)) {
161 			printf("## Error: \"%s\" not defined\n", name);
162 			++rcode;
163 		}
164 	}
165 
166 	return rcode;
167 }
168 
169 /************************************************************************
170  * Set a new environment variable,
171  * or replace or delete an existing one.
172  *
173  * This function will ONLY work with a in-RAM copy of the environment
174  */
175 
_do_setenv(int flag,int argc,char * argv[])176 int _do_setenv (int flag, int argc, char *argv[])
177 {
178 	int   i, len, oldval;
179 	int   console = -1;
180 	uchar *env, *nxt = NULL;
181 	char *name;
182 	bd_t *bd = gd->bd;
183 
184 	uchar *env_data = env_get_addr(0);
185 
186 	if (!env_data)	/* need copy in RAM */
187 		return 1;
188 
189 	name = argv[1];
190 
191 	if (strchr(name, '=')) {
192 		printf ("## Error: illegal character '=' in variable name \"%s\"\n", name);
193 		return 1;
194 	}
195 
196 	env_id++;
197 	/*
198 	 * search if variable with this name already exists
199 	 */
200 	oldval = -1;
201 	for (env=env_data; *env; env=nxt+1) {
202 		for (nxt=env; *nxt; ++nxt)
203 			;
204 		if ((oldval = envmatch((uchar *)name, env-env_data)) >= 0)
205 			break;
206 	}
207 
208 	/* Check for console redirection */
209 	if (strcmp(name,"stdin") == 0) {
210 		console = stdin;
211 	} else if (strcmp(name,"stdout") == 0) {
212 		console = stdout;
213 	} else if (strcmp(name,"stderr") == 0) {
214 		console = stderr;
215 	}
216 
217 	if (console != -1) {
218 		if (argc < 3) {		/* Cannot delete it! */
219 			printf("Can't delete \"%s\"\n", name);
220 			return 1;
221 		}
222 
223 #ifdef CONFIG_CONSOLE_MUX
224 		i = iomux_doenv(console, argv[2]);
225 		if (i)
226 			return i;
227 #else
228 		/* Try assigning specified device */
229 		if (console_assign (console, argv[2]) < 0)
230 			return 1;
231 
232 #ifdef CONFIG_SERIAL_MULTI
233 		if (serial_assign (argv[2]) < 0)
234 			return 1;
235 #endif
236 #endif /* CONFIG_CONSOLE_MUX */
237 	}
238 
239 	/*
240 	 * Delete any existing definition
241 	 */
242 	if (oldval >= 0) {
243 #ifndef CONFIG_ENV_OVERWRITE
244 
245 		/*
246 		 * Ethernet Address and serial# can be set only once,
247 		 * ver is readonly.
248 		 */
249 		if (
250 #ifdef CONFIG_HAS_UID
251 		/* Allow serial# forced overwrite with 0xdeaf4add flag */
252 		    ((strcmp (name, "serial#") == 0) && (flag != 0xdeaf4add)) ||
253 #else
254 		    (strcmp (name, "serial#") == 0) ||
255 #endif
256 		    ((strcmp (name, "ethaddr") == 0)
257 #if defined(CONFIG_OVERWRITE_ETHADDR_ONCE) && defined(CONFIG_ETHADDR)
258 		     && (strcmp ((char *)env_get_addr(oldval),MK_STR(CONFIG_ETHADDR)) != 0)
259 #endif	/* CONFIG_OVERWRITE_ETHADDR_ONCE && CONFIG_ETHADDR */
260 		    ) ) {
261 			printf ("Can't overwrite \"%s\"\n", name);
262 			return 1;
263 		}
264 #endif
265 
266 		/*
267 		 * Switch to new baudrate if new baudrate is supported
268 		 */
269 		if (strcmp(argv[1],"baudrate") == 0) {
270 			int baudrate = simple_strtoul(argv[2], NULL, 10);
271 			int i;
272 			for (i=0; i<N_BAUDRATES; ++i) {
273 				if (baudrate == baudrate_table[i])
274 					break;
275 			}
276 			if (i == N_BAUDRATES) {
277 				printf ("## Baudrate %d bps not supported\n",
278 					baudrate);
279 				return 1;
280 			}
281 			printf ("## Switch baudrate to %d bps and press ENTER ...\n",
282 				baudrate);
283 			udelay(50000);
284 			gd->baudrate = baudrate;
285 #if defined(CONFIG_PPC) || defined(CONFIG_MCF52x2)
286 			gd->bd->bi_baudrate = baudrate;
287 #endif
288 
289 			serial_setbrg ();
290 			udelay(50000);
291 			for (;;) {
292 				if (getc() == '\r')
293 				      break;
294 			}
295 		}
296 
297 		if (*++nxt == '\0') {
298 			if (env > env_data) {
299 				env--;
300 			} else {
301 				*env = '\0';
302 			}
303 		} else {
304 			for (;;) {
305 				*env = *nxt++;
306 				if ((*env == '\0') && (*nxt == '\0'))
307 					break;
308 				++env;
309 			}
310 		}
311 		*++env = '\0';
312 	}
313 
314 	/* Delete only ? */
315 	if ((argc < 3) || argv[2] == NULL) {
316 		env_crc_update ();
317 		return 0;
318 	}
319 
320 	/*
321 	 * Append new definition at the end
322 	 */
323 	for (env=env_data; *env || *(env+1); ++env)
324 		;
325 	if (env > env_data)
326 		++env;
327 	/*
328 	 * Overflow when:
329 	 * "name" + "=" + "val" +"\0\0"  > ENV_SIZE - (env-env_data)
330 	 */
331 	len = strlen(name) + 2;
332 	/* add '=' for first arg, ' ' for all others */
333 	for (i=2; i<argc; ++i) {
334 		len += strlen(argv[i]) + 1;
335 	}
336 	if (len > (&env_data[ENV_SIZE]-env)) {
337 		printf ("## Error: environment overflow, \"%s\" deleted\n", name);
338 		return 1;
339 	}
340 	while ((*env = *name++) != '\0')
341 		env++;
342 	for (i=2; i<argc; ++i) {
343 		char *val = argv[i];
344 
345 		*env = (i==2) ? '=' : ' ';
346 		while ((*++env = *val++) != '\0')
347 			;
348 	}
349 
350 	/* end is marked with double '\0' */
351 	*++env = '\0';
352 
353 	/* Update CRC */
354 	env_crc_update ();
355 
356 	/*
357 	 * Some variables should be updated when the corresponding
358 	 * entry in the enviornment is changed
359 	 */
360 
361 	if (strcmp(argv[1],"ethaddr") == 0)
362 		return 0;
363 
364 	if (strcmp(argv[1],"ipaddr") == 0) {
365 		char *s = argv[2];	/* always use only one arg */
366 		char *e;
367 		unsigned long addr;
368 		bd->bi_ip_addr = 0;
369 		for (addr=0, i=0; i<4; ++i) {
370 			ulong val = s ? simple_strtoul(s, &e, 10) : 0;
371 			addr <<= 8;
372 			addr  |= (val & 0xFF);
373 			if (s) s = (*e) ? e+1 : e;
374 		}
375 		bd->bi_ip_addr = htonl(addr);
376 		return 0;
377 	}
378 	if (strcmp(argv[1],"loadaddr") == 0) {
379 		load_addr = simple_strtoul(argv[2], NULL, 16);
380 		return 0;
381 	}
382 #if defined(CONFIG_CMD_NET)
383 	if (strcmp(argv[1],"bootfile") == 0) {
384 		copy_filename (BootFile, argv[2], sizeof(BootFile));
385 		return 0;
386 	}
387 #endif
388 
389 #ifdef CONFIG_AMIGAONEG3SE
390 	if (strcmp(argv[1], "vga_fg_color") == 0 ||
391 	    strcmp(argv[1], "vga_bg_color") == 0 ) {
392 		extern void video_set_color(unsigned char attr);
393 		extern unsigned char video_get_attr(void);
394 
395 		video_set_color(video_get_attr());
396 		return 0;
397 	}
398 #endif	/* CONFIG_AMIGAONEG3SE */
399 
400 	return 0;
401 }
402 
setenv(char * varname,char * varvalue)403 int setenv (char *varname, char *varvalue)
404 {
405 	char *argv[4] = { "setenv", varname, varvalue, NULL };
406 	if ((varvalue == NULL) || (varvalue[0] == '\0'))
407 		return _do_setenv (0, 2, argv);
408 	else
409 		return _do_setenv (0, 3, argv);
410 }
411 
412 #ifdef CONFIG_HAS_UID
forceenv(char * varname,char * varvalue)413 void forceenv (char *varname, char *varvalue)
414 {
415 	char *argv[4] = { "forceenv", varname, varvalue, NULL };
416 	_do_setenv (0xdeaf4add, 3, argv);
417 }
418 #endif
419 
do_setenv(cmd_tbl_t * cmdtp,int flag,int argc,char * argv[])420 int do_setenv (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
421 {
422 	if (argc < 2) {
423 		cmd_usage(cmdtp);
424 		return 1;
425 	}
426 
427 	return _do_setenv (flag, argc, argv);
428 }
429 
430 /************************************************************************
431  * Prompt for environment variable
432  */
433 
434 #if defined(CONFIG_CMD_ASKENV)
do_askenv(cmd_tbl_t * cmdtp,int flag,int argc,char * argv[])435 int do_askenv ( cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
436 {
437 	extern char console_buffer[CONFIG_SYS_CBSIZE];
438 	char message[CONFIG_SYS_CBSIZE];
439 	int size = CONFIG_SYS_CBSIZE - 1;
440 	int len;
441 	char *local_args[4];
442 
443 	local_args[0] = argv[0];
444 	local_args[1] = argv[1];
445 	local_args[2] = NULL;
446 	local_args[3] = NULL;
447 
448 	if (argc < 2) {
449 		cmd_usage(cmdtp);
450 		return 1;
451 	}
452 	/* Check the syntax */
453 	switch (argc) {
454 	case 1:
455 		cmd_usage(cmdtp);
456 		return 1;
457 
458 	case 2:		/* askenv envname */
459 		sprintf (message, "Please enter '%s':", argv[1]);
460 		break;
461 
462 	case 3:		/* askenv envname size */
463 		sprintf (message, "Please enter '%s':", argv[1]);
464 		size = simple_strtoul (argv[2], NULL, 10);
465 		break;
466 
467 	default:	/* askenv envname message1 ... messagen size */
468 	    {
469 		int i;
470 		int pos = 0;
471 
472 		for (i = 2; i < argc - 1; i++) {
473 			if (pos) {
474 				message[pos++] = ' ';
475 			}
476 			strcpy (message+pos, argv[i]);
477 			pos += strlen(argv[i]);
478 		}
479 		message[pos] = '\0';
480 		size = simple_strtoul (argv[argc - 1], NULL, 10);
481 	    }
482 		break;
483 	}
484 
485 	if (size >= CONFIG_SYS_CBSIZE)
486 		size = CONFIG_SYS_CBSIZE - 1;
487 
488 	if (size <= 0)
489 		return 1;
490 
491 	/* prompt for input */
492 	len = readline (message);
493 
494 	if (size < len)
495 		console_buffer[size] = '\0';
496 
497 	len = 2;
498 	if (console_buffer[0] != '\0') {
499 		local_args[2] = console_buffer;
500 		len = 3;
501 	}
502 
503 	/* Continue calling setenv code */
504 	return _do_setenv (flag, len, local_args);
505 }
506 #endif
507 
508 /************************************************************************
509  * Interactively edit an environment variable
510  */
511 #if defined(CONFIG_CMD_EDITENV)
do_editenv(cmd_tbl_t * cmdtp,int flag,int argc,char * argv[])512 int do_editenv(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
513 {
514 	char buffer[CONFIG_SYS_CBSIZE];
515 	char *init_val;
516 	int len;
517 
518 	if (argc < 2) {
519 		cmd_usage(cmdtp);
520 		return 1;
521 	}
522 
523 	/* Set read buffer to initial value or empty sting */
524 	init_val = getenv(argv[1]);
525 	if (init_val)
526 		len = sprintf(buffer, "%s", init_val);
527 	else
528 		buffer[0] = '\0';
529 
530 	readline_into_buffer("edit: ", buffer);
531 
532 	return setenv(argv[1], buffer);
533 }
534 #endif /* CONFIG_CMD_EDITENV */
535 
536 /************************************************************************
537  * Look up variable from environment,
538  * return address of storage for that variable,
539  * or NULL if not found
540  */
541 
getenv(char * name)542 char *getenv (char *name)
543 {
544 	int i, nxt;
545 
546 	WATCHDOG_RESET();
547 
548 	for (i=0; env_get_char(i) != '\0'; i=nxt+1) {
549 		int val;
550 
551 		for (nxt=i; env_get_char(nxt) != '\0'; ++nxt) {
552 			if (nxt >= CONFIG_ENV_SIZE) {
553 				return (NULL);
554 			}
555 		}
556 		if ((val=envmatch((uchar *)name, i)) < 0)
557 			continue;
558 		return ((char *)env_get_addr(val));
559 	}
560 
561 	return (NULL);
562 }
563 
getenv_r(char * name,char * buf,unsigned len)564 int getenv_r (char *name, char *buf, unsigned len)
565 {
566 	int i, nxt;
567 
568 	for (i=0; env_get_char(i) != '\0'; i=nxt+1) {
569 		int val, n;
570 
571 		for (nxt=i; env_get_char(nxt) != '\0'; ++nxt) {
572 			if (nxt >= CONFIG_ENV_SIZE) {
573 				return (-1);
574 			}
575 		}
576 		if ((val=envmatch((uchar *)name, i)) < 0)
577 			continue;
578 		/* found; copy out */
579 		n = 0;
580 		while ((len > n++) && (*buf++ = env_get_char(val++)) != '\0')
581 			;
582 		if (len == n)
583 			*buf = '\0';
584 		return (n);
585 	}
586 	return (-1);
587 }
588 
589 #if defined(CONFIG_CMD_SAVEENV) && !defined(CONFIG_ENV_IS_NOWHERE)
590 
do_saveenv(cmd_tbl_t * cmdtp,int flag,int argc,char * argv[])591 int do_saveenv (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
592 {
593 	extern char * env_name_spec;
594 
595 	printf ("Saving Environment to %s...\n", env_name_spec);
596 
597 	return (saveenv() ? 1 : 0);
598 }
599 
600 U_BOOT_CMD(
601 	saveenv, 1, 0,	do_saveenv,
602 	"save environment variables to persistent storage",
603 	""
604 );
605 
606 #endif
607 
608 
609 /************************************************************************
610  * Match a name / name=value pair
611  *
612  * s1 is either a simple 'name', or a 'name=value' pair.
613  * i2 is the environment index for a 'name2=value2' pair.
614  * If the names match, return the index for the value2, else NULL.
615  */
616 
envmatch(uchar * s1,int i2)617 int envmatch (uchar *s1, int i2)
618 {
619 
620 	while (*s1 == env_get_char(i2++))
621 		if (*s1++ == '=')
622 			return(i2);
623 	if (*s1 == '\0' && env_get_char(i2-1) == '=')
624 		return(i2);
625 	return(-1);
626 }
627 
628 
629 /**************************************************/
630 
631 #if defined(CONFIG_CMD_EDITENV)
632 U_BOOT_CMD(
633 	editenv, 2, 0,	do_editenv,
634 	"edit environment variable",
635 	"name\n"
636 	"    - edit environment variable 'name'"
637 );
638 #endif
639 
640 U_BOOT_CMD(
641 	printenv, CONFIG_SYS_MAXARGS, 1,	do_printenv,
642 	"print environment variables",
643 	"\n    - print values of all environment variables\n"
644 	"printenv name ...\n"
645 	"    - print value of environment variable 'name'"
646 );
647 
648 U_BOOT_CMD(
649 	setenv, CONFIG_SYS_MAXARGS, 0,	do_setenv,
650 	"set environment variables",
651 	"name value ...\n"
652 	"    - set environment variable 'name' to 'value ...'\n"
653 	"setenv name\n"
654 	"    - delete environment variable 'name'"
655 );
656 
657 #if defined(CONFIG_CMD_ASKENV)
658 
659 U_BOOT_CMD(
660 	askenv,	CONFIG_SYS_MAXARGS,	1,	do_askenv,
661 	"get environment variables from stdin",
662 	"name [message] [size]\n"
663 	"    - get environment variable 'name' from stdin (max 'size' chars)\n"
664 	"askenv name\n"
665 	"    - get environment variable 'name' from stdin\n"
666 	"askenv name size\n"
667 	"    - get environment variable 'name' from stdin (max 'size' chars)\n"
668 	"askenv name [message] size\n"
669 	"    - display 'message' string and get environment variable 'name'"
670 	"from stdin (max 'size' chars)"
671 );
672 #endif
673 
674 #if defined(CONFIG_CMD_RUN)
675 int do_run (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);
676 U_BOOT_CMD(
677 	run,	CONFIG_SYS_MAXARGS,	1,	do_run,
678 	"run commands in an environment variable",
679 	"var [...]\n"
680 	"    - run the commands in the environment variable(s) 'var'"
681 );
682 #endif
683