xref: /rk3399_rockchip-uboot/tools/env/fw_env.c (revision 6aff3115b90780933d390d2b471479179927468d)
1 /*
2  * (C) Copyright 2000
3  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
4  *
5  * See file CREDITS for list of people who contributed to this
6  * project.
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License as
10  * published by the Free Software Foundation; either version 2 of
11  * the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
21  * MA 02111-1307 USA
22  */
23 
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <stddef.h>
29 #include <string.h>
30 #include <sys/types.h>
31 #include <sys/ioctl.h>
32 #include <sys/stat.h>
33 #include <unistd.h>
34 #include <linux/mtd/mtd.h>
35 #include "fw_env.h"
36 
37 typedef	unsigned char	uchar;
38 
39 #define	CMD_GETENV	"fw_printenv"
40 #define	CMD_SETENV	"fw_setenv"
41 
42 typedef struct envdev_s {
43 	uchar devname[16]; /* Device name */
44 	ulong env_size;    /* environment size */
45 	ulong erase_size;  /* device erase size */
46 } envdev_t;
47 
48 static envdev_t envdevices[2];
49 static int curdev;
50 
51 #define DEVNAME(i)    envdevices[(i)].devname
52 #define ENVSIZE(i)    envdevices[(i)].env_size
53 #define DEVESIZE(i)   envdevices[(i)].erase_size
54 
55 #define CFG_ENV_SIZE ENVSIZE(curdev)
56 
57 #ifdef HAVE_REDUND
58 #define ENV_SIZE (CFG_ENV_SIZE - sizeof(long) - 1)
59 #else
60 #define ENV_SIZE (CFG_ENV_SIZE - sizeof(long))
61 #endif
62 
63 typedef struct environment_s {
64 	ulong	crc;		/* CRC32 over data bytes	*/
65 	uchar   flags;      /* active or obsolete */
66 	uchar *data;
67 } env_t;
68 
69 static env_t environment;
70 static int valid = 0;
71 
72 #ifdef HAVE_REDUND
73 static uchar active_flag = 1;
74 static uchar obsolete_flag = 0;
75 #endif
76 
77 #define XMK_STR(x)	#x
78 #define MK_STR(x)	XMK_STR(x)
79 
80 static uchar default_environment[] = {
81 #ifdef	CONFIG_BOOTARGS
82 	"bootargs="	CONFIG_BOOTARGS			"\0"
83 #endif
84 #ifdef	CONFIG_BOOTCOMMAND
85 	"bootcmd="	CONFIG_BOOTCOMMAND		"\0"
86 #endif
87 #if (CONFIG_BOOTDELAY >= 0)
88 	"bootdelay="	MK_STR(CONFIG_BOOTDELAY)	"\0"
89 #endif
90 #if (CONFIG_BAUDRATE >= 0)
91 	"baudrate="	MK_STR(CONFIG_BAUDRATE)		"\0"
92 #endif
93 #ifdef	CONFIG_ETHADDR
94 	"ethaddr="	MK_STR(CONFIG_ETHADDR)		"\0"
95 #endif
96 #ifdef	CONFIG_IPADDR
97 	"ipaddr="	MK_STR(CONFIG_IPADDR)		"\0"
98 #endif
99 #ifdef	CONFIG_SERVERIP
100 	"serverip="	MK_STR(CONFIG_SERVERIP)		"\0"
101 #endif
102 	"\0"
103 };
104 
105 static int  flash_io (int mode);
106 static uchar *envmatch(uchar *s1, uchar *s2);
107 static int env_init(void);
108 static int parse_config(void);
109 
110 
111 /*
112  * Search the environment for a variable.
113  * Return the value, if found, or NULL, if not found.
114  */
115 unsigned char *fw_getenv (unsigned char *name)
116 {
117 	uchar *env, *nxt;
118 
119 	if (env_init())
120 		return (NULL);
121 
122 	for (env=environment.data; *env; env=nxt+1) {
123 		uchar *val;
124 
125 		for (nxt=env; *nxt; ++nxt) {
126 			if (nxt >= &environment.data[ENV_SIZE]) {
127 				fprintf (stderr, "## Error: "
128 					"environment not terminated\n");
129 				return (NULL);
130 			}
131 		}
132 		val=envmatch(name, env);
133 		if (!val)
134 			continue;
135 		return (val);
136 	}
137 	return (NULL);
138 }
139 
140 /*
141  * Print the current definition of one, or more, or all
142  * environment variables
143  */
144 void fw_printenv(int argc, char *argv[])
145 {
146 	uchar *env, *nxt;
147 	int i, n_flag;
148 
149 	if (env_init())
150 		return;
151 
152 	if (argc == 1) {		/* Print all env variables	*/
153 		for (env=environment.data; *env; env=nxt+1) {
154 			for (nxt=env; *nxt; ++nxt) {
155 				if (nxt >= &environment.data[ENV_SIZE]) {
156 					fprintf (stderr, "## Error: "
157 						"environment not terminated\n");
158 					return;
159 				}
160 			}
161 
162 			printf("%s\n", env);
163 		}
164 		return;
165 	}
166 
167 	if (strcmp(argv[1], "-n") == 0) {
168 		n_flag = 1;
169 		++argv;
170 		--argc;
171 		if (argc != 2) {
172 			fprintf (stderr, "## Error: "
173 				"`-n' option requires exactly one argument\n");
174 			return;
175 		}
176 	} else {
177 		n_flag = 0;
178 	}
179 
180 	for (i=1; i<argc; ++i) {	/* print single env variables	*/
181 		uchar *name = argv[i];
182 		uchar *val = NULL;
183 
184 		for (env=environment.data; *env; env=nxt+1) {
185 
186 			for (nxt=env; *nxt; ++nxt) {
187 				if (nxt >= &environment.data[ENV_SIZE]) {
188 					fprintf (stderr, "## Error: "
189 						"environment not terminated\n");
190 					return;
191 				}
192 			}
193 			val=envmatch(name, env);
194 			if (val) {
195 				if (!n_flag) {
196 					fputs (name, stdout);
197 					putc  ('=',  stdout);
198 				}
199 				puts  (val);
200 				break;
201 			}
202 		}
203 		if (!val)
204 			fprintf (stderr, "## Error: \"%s\" not defined\n",
205 			 name);
206 	}
207 }
208 
209 /*
210  * Deletes or sets environment variables. Returns errno style error codes:
211  * 0	  - OK
212  * EINVAL - need at least 1 argument
213  * EROFS  - certain variables ("ethaddr", "serial#") cannot be
214  *	    modified or deleted
215  *
216  */
217 int fw_setenv (int argc, char *argv[])
218 {
219 	int  i, len;
220 	uchar *env, *nxt;
221 	uchar *oldval = NULL;
222 	uchar *name;
223 
224 	if (argc < 2) {
225 		return (EINVAL);
226 	}
227 
228 	if (env_init())
229 		return (errno);
230 
231 	name = argv[1];
232 
233 	/*
234 	 * search if variable with this name already exists
235 	 */
236 	for (env=environment.data; *env; env=nxt+1) {
237 		for (nxt=env; *nxt; ++nxt) {
238 			if (nxt >= &environment.data[ENV_SIZE]) {
239 				fprintf (stderr, "## Error: "
240 					"environment not terminated\n");
241 				return (EINVAL);
242 			}
243 		}
244 		if ((oldval=envmatch(name, env)) != NULL)
245 			break;
246 	}
247 
248 	/*
249 	 * Delete any existing definition
250 	 */
251 	if (oldval) {
252 		/*
253 		 * Ethernet Address and serial# can be set only once
254 		 */
255 		if ((strcmp (name, "ethaddr") == 0) ||
256 		    (strcmp (name, "serial#") == 0) ) {
257 			fprintf (stderr, "Can't overwrite \"%s\"\n", name);
258 			return (EROFS);
259 		}
260 
261 		if (*++nxt == '\0') {
262 			*env = '\0';
263 		} else {
264 			for (;;) {
265 				*env = *nxt++;
266 				if ((*env == '\0') && (*nxt == '\0'))
267 					break;
268 				++env;
269 			}
270 		}
271 		*++env = '\0';
272 	}
273 
274 	/* Delete only ? */
275 	if (argc < 3)
276 		goto WRITE_FLASH;
277 
278 	/*
279 	 * Append new definition at the end
280 	 */
281 	for (env=environment.data; *env || *(env+1); ++env)
282 		;
283 	if (env > environment.data)
284 		++env;
285 	/*
286 	 * Overflow when:
287 	 * "name" + "=" + "val" +"\0\0"  > CFG_ENV_SIZE - (env-environment)
288 	 */
289 	len = strlen(name) + 2;
290 	/* add '=' for first arg, ' ' for all others */
291 	for (i=2; i<argc; ++i) {
292 		len += strlen(argv[i]) + 1;
293 	}
294 	if (len > (&environment.data[ENV_SIZE]-env)) {
295 		fprintf (stderr,
296 			"Error: environment overflow, \"%s\" deleted\n",
297 			name);
298 		return (-1);
299 	}
300 	while ((*env = *name++) != '\0')
301 		env++;
302 	for (i=2; i<argc; ++i) {
303 		uchar *val = argv[i];
304 
305 		*env = (i==2) ? '=' : ' ';
306 		while ((*++env = *val++) != '\0')
307 			;
308 	}
309 
310 	/* end is marked with double '\0' */
311 	*++env = '\0';
312 
313 WRITE_FLASH:
314 
315 	/* Update CRC */
316 	environment.crc = crc32(0, environment.data, ENV_SIZE);
317 
318 	/* write environment back to flash */
319 	if (flash_io (O_RDWR)) {
320 		fprintf (stderr,
321 			"Error: can't write fw_env to flash\n");
322 		return (-1);
323 	}
324 
325 	return (0);
326 }
327 
328 static int flash_io (int mode)
329 {
330 	int fd, fdr, rc, otherdev, len, resid;
331 	erase_info_t erase;
332 	char *data;
333 
334 	if ((fd = open(DEVNAME(curdev), mode)) < 0) {
335 		fprintf (stderr,
336 				 "Can't open %s: %s\n",
337 				 DEVNAME(curdev), strerror(errno));
338 		return (-1);
339 	}
340 
341 	len = sizeof(environment.crc) + sizeof(environment.flags);
342 
343 	if (mode == O_RDWR) {
344 #ifdef HAVE_REDUND
345 		/* switch to next partition for writing */
346 		otherdev = !curdev;
347 		if ((fdr = open(DEVNAME(otherdev), mode)) < 0) {
348 			fprintf (stderr,
349 					 "Can't open %s: %s\n",
350 					 DEVNAME(otherdev), strerror(errno));
351 			return (-1);
352 		}
353 #else
354 		otherdev = curdev;
355 		fdr = fd;
356 		len = sizeof(environment.crc);
357 #endif
358 		printf("Unlocking flash...\n");
359 		erase.length = DEVESIZE(otherdev);
360 		erase.start = 0;
361 		ioctl (fdr, MEMUNLOCK, &erase);
362 
363 #ifdef HAVE_REDUND
364 		erase.length = DEVESIZE(curdev);
365 		erase.start = 0;
366 		ioctl (fd, MEMUNLOCK, &erase);
367 		environment.flags = active_flag;
368 #endif
369 		printf("Done\n");
370 		resid = DEVESIZE(otherdev) - CFG_ENV_SIZE;
371 		if (resid) {
372 			if ((data = malloc(resid)) == NULL) {
373 				fprintf(stderr,
374 				  "Cannot malloc %d bytes: %s\n",
375 				  resid, strerror(errno));
376 				return (-1);
377 			}
378 			if (lseek (fdr, CFG_ENV_SIZE, SEEK_SET) == -1) {
379 				fprintf (stderr,
380 				  "seek error on %s: %s\n",
381 				   DEVNAME(curdev), strerror(errno));
382 				return (-1);
383 			}
384 			if ((rc = read (fdr, data, resid)) != resid) {
385 				fprintf (stderr,
386 				  "read error on %s: %s\n",
387 				  DEVNAME(curdev), strerror(errno));
388 				return (-1);
389 			}
390 			if (lseek (fdr, 0, SEEK_SET) == -1) {
391 				fprintf (stderr,
392 				  "seek error on %s: %s\n",
393 				  DEVNAME(curdev), strerror(errno));
394 				return (-1);
395 			}
396 		}
397 
398 		printf("Erasing old environment...\n");
399 
400 		erase.length = DEVESIZE(otherdev);
401 		if (ioctl (fdr, MEMERASE, &erase) != 0) {
402 			fprintf (stderr, "MTD erase error on %s: %s\n",
403 			  DEVNAME(otherdev), strerror(errno));
404 			return (-1);
405 		}
406 
407 		printf("Done\n");
408 
409 		printf("Writing environment to %s...\n",DEVNAME(otherdev));
410 		if (write(fdr, &environment, len) != len) {
411 			fprintf (stderr,
412 			  "CRC write error on %s: %s\n",
413 			  DEVNAME(otherdev), strerror(errno));
414 			return (-1);
415 		}
416 		if (write(fdr, environment.data, ENV_SIZE) != ENV_SIZE) {
417 			fprintf (stderr,
418 			  "Write error on %s: %s\n",
419 			  DEVNAME(otherdev), strerror(errno));
420 			return (-1);
421 		}
422 		if (resid) {
423 			if (write (fdr, data, resid) != resid) {
424 				fprintf (stderr,
425 				 "write error on %s: %s\n",
426 				  DEVNAME(curdev), strerror(errno));
427 				return (-1);
428 			}
429 			free(data);
430 		}
431 #ifdef HAVE_REDUND
432 		/* change flag on current active env partition */
433 		if (lseek (fd, sizeof(ulong), SEEK_SET) == -1) {
434 			fprintf (stderr,
435 			  "seek error on %s: %s\n",
436 			  DEVNAME(curdev), strerror(errno));
437 			return (-1);
438 		}
439 		if (write (fd, &obsolete_flag, sizeof(obsolete_flag)) !=
440 			sizeof(obsolete_flag)) {
441 			fprintf (stderr,
442 			 "Write error on %s: %s\n",
443 			 DEVNAME(curdev), strerror(errno));
444 			return (-1);
445 		}
446 #endif
447 		printf("Done\n");
448 		printf("Locking ...\n");
449 		erase.length = DEVESIZE(otherdev);
450 		erase.start = 0;
451 		ioctl (fdr, MEMLOCK, &erase);
452 #ifdef HAVE_REDUND
453 		erase.length = DEVESIZE(curdev);
454 		erase.start = 0;
455 		ioctl (fd, MEMLOCK, &erase);
456 		if (close(fdr)) {
457 			fprintf (stderr,
458 			 "I/O error on %s: %s\n",
459 			 DEVNAME(otherdev), strerror(errno));
460 			return (-1);
461 		}
462 #endif
463 		printf("Done\n");
464 	} else {
465 #ifndef HAVE_REDUND
466 		len = sizeof(environment.crc);
467 #endif
468 		if (read (fd, &environment, len) != len) {
469 			fprintf (stderr,
470 			 "CRC read error on %s: %s\n",
471 			 DEVNAME(curdev), strerror(errno));
472 			return (-1);
473 		}
474 		if ((rc = read (fd, environment.data, ENV_SIZE)) != ENV_SIZE) {
475 			fprintf (stderr,
476 			 "Read error on %s: %s\n",
477 			  DEVNAME(curdev), strerror(errno));
478 			return (-1);
479 		}
480 	}
481 
482 	if (close(fd)) {
483 		fprintf (stderr,
484 		  "I/O error on %s: %s\n",
485 		  DEVNAME(curdev), strerror(errno));
486 		return (-1);
487 	}
488 
489 	/* everything ok */
490 	return (0);
491 }
492 
493 /*
494  * s1 is either a simple 'name', or a 'name=value' pair.
495  * s2 is a 'name=value' pair.
496  * If the names match, return the value of s2, else NULL.
497  */
498 
499 static uchar *
500 envmatch (uchar *s1, uchar *s2)
501 {
502 
503 	while (*s1 == *s2++)
504 		if (*s1++ == '=')
505 			return(s2);
506 	if (*s1 == '\0' && *(s2-1) == '=')
507 		return(s2);
508 	return(NULL);
509 }
510 
511 /*
512  * Prevent confusion if running from erased flash memory
513  */
514 static int env_init(void)
515 {
516 	int crc1, crc1_ok;
517 	uchar *addr1;
518 #ifdef HAVE_REDUND
519 	int crc2, crc2_ok;
520 	uchar flag1, flag2, *addr2;
521 #endif
522 
523 
524 	if (!valid) {
525 
526 		if (parse_config()) /* should fill envdevices */
527 			return 1;
528 
529 		if ((addr1 = calloc (1, ENV_SIZE)) == NULL) {
530 			fprintf (stderr,
531 			 "Not enough memory for environment (%ld bytes)\n",
532 			 ENV_SIZE);
533 			return (errno);
534 		}
535 
536 		/* read environment from FLASH to local buffer */
537 		environment.data = addr1;
538 		curdev = 0;
539 		if (flash_io (O_RDONLY)) {
540 			return (errno);
541 		}
542 
543 		crc1_ok = ((crc1 = crc32(0, environment.data, ENV_SIZE))
544 			 == environment.crc);
545 #ifndef HAVE_REDUND
546 		if (!crc1_ok) {
547 			fprintf (stderr,
548 			 "Warning: Bad CRC, using default environment\n");
549 			environment.data = default_environment;
550 			free(addr1);
551 		}
552 #else
553 		flag1 = environment.flags;
554 
555 		curdev = 1;
556 		if ((addr2 = calloc (1, ENV_SIZE)) == NULL) {
557 			fprintf (stderr,
558 			 "Not enough memory for environment (%ld bytes)\n",
559 			 ENV_SIZE);
560 			return (errno);
561 		}
562 		environment.data = addr2;
563 
564 		if (flash_io (O_RDONLY)) {
565 			return (errno);
566 		}
567 
568 		crc2_ok = ((crc2 = crc32(0, environment.data, ENV_SIZE))
569 				   == environment.crc);
570 		flag2 = environment.flags;
571 
572 		if (crc1_ok && ! crc2_ok) {
573 			environment.data  = addr1;
574 			environment.flags = flag1;
575 			environment.crc = crc1;
576 			curdev = 0;
577 			free(addr2);
578 		}
579 		else if (! crc1_ok && crc2_ok) {
580 			environment.data  = addr2;
581 			environment.flags = flag2;
582 			environment.crc = crc2;
583 			curdev = 1;
584 			free(addr1);
585 		}
586 		else if (! crc1_ok && ! crc2_ok) {
587 			fprintf (stderr,
588 			 "Warning: Bad CRC, using default environment\n");
589 			environment.data = default_environment;
590 			curdev = 0;
591 			free(addr2);
592 			free(addr1);
593 		}
594 		else if (flag1 == active_flag && flag2 == obsolete_flag) {
595 			environment.data  = addr1;
596 			environment.flags = flag1;
597 			environment.crc = crc1;
598 			curdev = 0;
599 			free(addr2);
600 		}
601 		else if (flag1 == obsolete_flag && flag2 == active_flag) {
602 			environment.data  = addr2;
603 			environment.flags = flag2;
604 			environment.crc = crc2;
605 			curdev = 1;
606 			free(addr1);
607 		}
608 		else if (flag1 == flag2) {
609 			environment.data  = addr1;
610 			environment.flags = flag1;
611 			environment.crc = crc1;
612 			curdev = 0;
613 			free(addr2);
614 		}
615 		else if (flag1 == 0xFF)	{
616 			environment.data  = addr1;
617 			environment.flags = flag1;
618 			environment.crc = crc1;
619 			curdev = 0;
620 			free(addr2);
621 		}
622 		else if (flag2 == 0xFF) {
623 			environment.data  = addr2;
624 			environment.flags = flag2;
625 			environment.crc = crc2;
626 			curdev = 1;
627 			free(addr1);
628 		}
629 #endif
630 		valid = 1;
631 	}
632 	return (0);
633 }
634 
635 
636 static int parse_config()
637 {
638 	struct stat st;
639 
640 	if (stat (DEVICE1_NAME, &st)) {
641 		fprintf (stderr,
642 		 "Cannot access MTD device %s: %s\n",
643 		 DEVICE1_NAME, strerror(errno));
644 		return 1;
645 	}
646 
647 	strcpy(DEVNAME(0), DEVICE1_NAME);
648 	ENVSIZE(0) = ENV1_SIZE;
649 	DEVESIZE(0) = DEVICE1_ESIZE;
650 #ifdef HAVE_REDUND
651 	if (stat (DEVICE2_NAME, &st)) {
652 		fprintf (stderr,
653 		 "Cannot access MTD device %s: %s\n",
654 		 DEVICE2_NAME, strerror(errno));
655 		return 1;
656 	}
657 	strcpy(DEVNAME(1), DEVICE2_NAME);
658 	ENVSIZE(1) = ENV2_SIZE;
659 	DEVESIZE(1) = DEVICE2_ESIZE;
660 #endif
661 	return 0;
662 }
663