xref: /openbmc/u-boot/tools/env/fw_env.c (revision 15855700)
1 /*
2  * (C) Copyright 2000-2008
3  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
4  *
5  * (C) Copyright 2008
6  * Guennadi Liakhovetski, DENX Software Engineering, lg@denx.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 #include <errno.h>
28 #include <fcntl.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <stddef.h>
32 #include <string.h>
33 #include <sys/types.h>
34 #include <sys/ioctl.h>
35 #include <sys/stat.h>
36 #include <unistd.h>
37 
38 #ifdef MTD_OLD
39 # include <stdint.h>
40 # include <linux/mtd/mtd.h>
41 #else
42 # define  __user	/* nothing */
43 # include <mtd/mtd-user.h>
44 #endif
45 
46 #include "fw_env.h"
47 
48 #define	CMD_GETENV	"fw_printenv"
49 #define	CMD_SETENV	"fw_setenv"
50 
51 #define min(x, y) ({				\
52 	typeof(x) _min1 = (x);			\
53 	typeof(y) _min2 = (y);			\
54 	(void) (&_min1 == &_min2);		\
55 	_min1 < _min2 ? _min1 : _min2; })
56 
57 struct envdev_s {
58 	char devname[16];		/* Device name */
59 	ulong devoff;			/* Device offset */
60 	ulong env_size;			/* environment size */
61 	ulong erase_size;		/* device erase size */
62 	ulong env_sectors;		/* number of environment sectors */
63 	uint8_t mtd_type;		/* type of the MTD device */
64 };
65 
66 static struct envdev_s envdevices[2] =
67 {
68 	{
69 		.mtd_type = MTD_ABSENT,
70 	}, {
71 		.mtd_type = MTD_ABSENT,
72 	},
73 };
74 static int dev_current;
75 
76 #define DEVNAME(i)    envdevices[(i)].devname
77 #define DEVOFFSET(i)  envdevices[(i)].devoff
78 #define ENVSIZE(i)    envdevices[(i)].env_size
79 #define DEVESIZE(i)   envdevices[(i)].erase_size
80 #define ENVSECTORS(i) envdevices[(i)].env_sectors
81 #define DEVTYPE(i)    envdevices[(i)].mtd_type
82 
83 #define CONFIG_ENV_SIZE ENVSIZE(dev_current)
84 
85 #define ENV_SIZE      getenvsize()
86 
87 struct env_image_single {
88 	uint32_t	crc;	/* CRC32 over data bytes    */
89 	char		data[];
90 };
91 
92 struct env_image_redundant {
93 	uint32_t	crc;	/* CRC32 over data bytes    */
94 	unsigned char	flags;	/* active or obsolete */
95 	char		data[];
96 };
97 
98 enum flag_scheme {
99 	FLAG_NONE,
100 	FLAG_BOOLEAN,
101 	FLAG_INCREMENTAL,
102 };
103 
104 struct environment {
105 	void			*image;
106 	uint32_t		*crc;
107 	unsigned char		*flags;
108 	char			*data;
109 	enum flag_scheme	flag_scheme;
110 };
111 
112 static struct environment environment = {
113 	.flag_scheme = FLAG_NONE,
114 };
115 
116 static int HaveRedundEnv = 0;
117 
118 static unsigned char active_flag = 1;
119 /* obsolete_flag must be 0 to efficiently set it on NOR flash without erasing */
120 static unsigned char obsolete_flag = 0;
121 
122 
123 #define XMK_STR(x)	#x
124 #define MK_STR(x)	XMK_STR(x)
125 
126 static char default_environment[] = {
127 #if defined(CONFIG_BOOTARGS)
128 	"bootargs=" CONFIG_BOOTARGS "\0"
129 #endif
130 #if defined(CONFIG_BOOTCOMMAND)
131 	"bootcmd=" CONFIG_BOOTCOMMAND "\0"
132 #endif
133 #if defined(CONFIG_RAMBOOTCOMMAND)
134 	"ramboot=" CONFIG_RAMBOOTCOMMAND "\0"
135 #endif
136 #if defined(CONFIG_NFSBOOTCOMMAND)
137 	"nfsboot=" CONFIG_NFSBOOTCOMMAND "\0"
138 #endif
139 #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
140 	"bootdelay=" MK_STR (CONFIG_BOOTDELAY) "\0"
141 #endif
142 #if defined(CONFIG_BAUDRATE) && (CONFIG_BAUDRATE >= 0)
143 	"baudrate=" MK_STR (CONFIG_BAUDRATE) "\0"
144 #endif
145 #ifdef	CONFIG_LOADS_ECHO
146 	"loads_echo=" MK_STR (CONFIG_LOADS_ECHO) "\0"
147 #endif
148 #ifdef	CONFIG_ETHADDR
149 	"ethaddr=" MK_STR (CONFIG_ETHADDR) "\0"
150 #endif
151 #ifdef	CONFIG_ETH1ADDR
152 	"eth1addr=" MK_STR (CONFIG_ETH1ADDR) "\0"
153 #endif
154 #ifdef	CONFIG_ETH2ADDR
155 	"eth2addr=" MK_STR (CONFIG_ETH2ADDR) "\0"
156 #endif
157 #ifdef	CONFIG_ETH3ADDR
158 	"eth3addr=" MK_STR (CONFIG_ETH3ADDR) "\0"
159 #endif
160 #ifdef	CONFIG_ETH4ADDR
161 	"eth4addr=" MK_STR (CONFIG_ETH4ADDR) "\0"
162 #endif
163 #ifdef	CONFIG_ETH5ADDR
164 	"eth5addr=" MK_STR (CONFIG_ETH5ADDR) "\0"
165 #endif
166 #ifdef	CONFIG_ETHPRIME
167 	"ethprime=" CONFIG_ETHPRIME "\0"
168 #endif
169 #ifdef	CONFIG_IPADDR
170 	"ipaddr=" MK_STR (CONFIG_IPADDR) "\0"
171 #endif
172 #ifdef	CONFIG_SERVERIP
173 	"serverip=" MK_STR (CONFIG_SERVERIP) "\0"
174 #endif
175 #ifdef	CONFIG_SYS_AUTOLOAD
176 	"autoload=" CONFIG_SYS_AUTOLOAD "\0"
177 #endif
178 #ifdef	CONFIG_ROOTPATH
179 	"rootpath=" MK_STR (CONFIG_ROOTPATH) "\0"
180 #endif
181 #ifdef	CONFIG_GATEWAYIP
182 	"gatewayip=" MK_STR (CONFIG_GATEWAYIP) "\0"
183 #endif
184 #ifdef	CONFIG_NETMASK
185 	"netmask=" MK_STR (CONFIG_NETMASK) "\0"
186 #endif
187 #ifdef	CONFIG_HOSTNAME
188 	"hostname=" MK_STR (CONFIG_HOSTNAME) "\0"
189 #endif
190 #ifdef	CONFIG_BOOTFILE
191 	"bootfile=" MK_STR (CONFIG_BOOTFILE) "\0"
192 #endif
193 #ifdef	CONFIG_LOADADDR
194 	"loadaddr=" MK_STR (CONFIG_LOADADDR) "\0"
195 #endif
196 #ifdef	CONFIG_PREBOOT
197 	"preboot=" CONFIG_PREBOOT "\0"
198 #endif
199 #ifdef	CONFIG_CLOCKS_IN_MHZ
200 	"clocks_in_mhz=" "1" "\0"
201 #endif
202 #if defined(CONFIG_PCI_BOOTDELAY) && (CONFIG_PCI_BOOTDELAY > 0)
203 	"pcidelay=" MK_STR (CONFIG_PCI_BOOTDELAY) "\0"
204 #endif
205 #ifdef  CONFIG_EXTRA_ENV_SETTINGS
206 	CONFIG_EXTRA_ENV_SETTINGS
207 #endif
208 	"\0"		/* Termimate struct environment data with 2 NULs */
209 };
210 
211 static int flash_io (int mode);
212 static char *envmatch (char * s1, char * s2);
213 static int env_init (void);
214 static int parse_config (void);
215 
216 #if defined(CONFIG_FILE)
217 static int get_config (char *);
218 #endif
219 static inline ulong getenvsize (void)
220 {
221 	ulong rc = CONFIG_ENV_SIZE - sizeof (long);
222 
223 	if (HaveRedundEnv)
224 		rc -= sizeof (char);
225 	return rc;
226 }
227 
228 /*
229  * Search the environment for a variable.
230  * Return the value, if found, or NULL, if not found.
231  */
232 char *fw_getenv (char *name)
233 {
234 	char *env, *nxt;
235 
236 	if (env_init ())
237 		return NULL;
238 
239 	for (env = environment.data; *env; env = nxt + 1) {
240 		char *val;
241 
242 		for (nxt = env; *nxt; ++nxt) {
243 			if (nxt >= &environment.data[ENV_SIZE]) {
244 				fprintf (stderr, "## Error: "
245 					"environment not terminated\n");
246 				return NULL;
247 			}
248 		}
249 		val = envmatch (name, env);
250 		if (!val)
251 			continue;
252 		return val;
253 	}
254 	return NULL;
255 }
256 
257 /*
258  * Print the current definition of one, or more, or all
259  * environment variables
260  */
261 int fw_printenv (int argc, char *argv[])
262 {
263 	char *env, *nxt;
264 	int i, n_flag;
265 	int rc = 0;
266 
267 	if (env_init ())
268 		return -1;
269 
270 	if (argc == 1) {		/* Print all env variables  */
271 		for (env = environment.data; *env; env = nxt + 1) {
272 			for (nxt = env; *nxt; ++nxt) {
273 				if (nxt >= &environment.data[ENV_SIZE]) {
274 					fprintf (stderr, "## Error: "
275 						"environment not terminated\n");
276 					return -1;
277 				}
278 			}
279 
280 			printf ("%s\n", env);
281 		}
282 		return 0;
283 	}
284 
285 	if (strcmp (argv[1], "-n") == 0) {
286 		n_flag = 1;
287 		++argv;
288 		--argc;
289 		if (argc != 2) {
290 			fprintf (stderr, "## Error: "
291 				"`-n' option requires exactly one argument\n");
292 			return -1;
293 		}
294 	} else {
295 		n_flag = 0;
296 	}
297 
298 	for (i = 1; i < argc; ++i) {	/* print single env variables   */
299 		char *name = argv[i];
300 		char *val = NULL;
301 
302 		for (env = environment.data; *env; env = nxt + 1) {
303 
304 			for (nxt = env; *nxt; ++nxt) {
305 				if (nxt >= &environment.data[ENV_SIZE]) {
306 					fprintf (stderr, "## Error: "
307 						"environment not terminated\n");
308 					return -1;
309 				}
310 			}
311 			val = envmatch (name, env);
312 			if (val) {
313 				if (!n_flag) {
314 					fputs (name, stdout);
315 					putc ('=', stdout);
316 				}
317 				puts (val);
318 				break;
319 			}
320 		}
321 		if (!val) {
322 			fprintf (stderr, "## Error: \"%s\" not defined\n", name);
323 			rc = -1;
324 		}
325 	}
326 
327 	return rc;
328 }
329 
330 /*
331  * Deletes or sets environment variables. Returns -1 and sets errno error codes:
332  * 0	  - OK
333  * EINVAL - need at least 1 argument
334  * EROFS  - certain variables ("ethaddr", "serial#") cannot be
335  *	    modified or deleted
336  *
337  */
338 int fw_setenv (int argc, char *argv[])
339 {
340 	int i, len;
341 	char *env, *nxt;
342 	char *oldval = NULL;
343 	char *name;
344 
345 	if (argc < 2) {
346 		errno = EINVAL;
347 		return -1;
348 	}
349 
350 	if (env_init ())
351 		return -1;
352 
353 	name = argv[1];
354 
355 	/*
356 	 * search if variable with this name already exists
357 	 */
358 	for (nxt = env = environment.data; *env; env = nxt + 1) {
359 		for (nxt = env; *nxt; ++nxt) {
360 			if (nxt >= &environment.data[ENV_SIZE]) {
361 				fprintf (stderr, "## Error: "
362 					"environment not terminated\n");
363 				errno = EINVAL;
364 				return -1;
365 			}
366 		}
367 		if ((oldval = envmatch (name, env)) != NULL)
368 			break;
369 	}
370 
371 	/*
372 	 * Delete any existing definition
373 	 */
374 	if (oldval) {
375 		/*
376 		 * Ethernet Address and serial# can be set only once
377 		 */
378 		if ((strcmp (name, "ethaddr") == 0) ||
379 			(strcmp (name, "serial#") == 0)) {
380 			fprintf (stderr, "Can't overwrite \"%s\"\n", name);
381 			errno = EROFS;
382 			return -1;
383 		}
384 
385 		if (*++nxt == '\0') {
386 			*env = '\0';
387 		} else {
388 			for (;;) {
389 				*env = *nxt++;
390 				if ((*env == '\0') && (*nxt == '\0'))
391 					break;
392 				++env;
393 			}
394 		}
395 		*++env = '\0';
396 	}
397 
398 	/* Delete only ? */
399 	if (argc < 3)
400 		goto WRITE_FLASH;
401 
402 	/*
403 	 * Append new definition at the end
404 	 */
405 	for (env = environment.data; *env || *(env + 1); ++env);
406 	if (env > environment.data)
407 		++env;
408 	/*
409 	 * Overflow when:
410 	 * "name" + "=" + "val" +"\0\0"  > CONFIG_ENV_SIZE - (env-environment)
411 	 */
412 	len = strlen (name) + 2;
413 	/* add '=' for first arg, ' ' for all others */
414 	for (i = 2; i < argc; ++i) {
415 		len += strlen (argv[i]) + 1;
416 	}
417 	if (len > (&environment.data[ENV_SIZE] - env)) {
418 		fprintf (stderr,
419 			"Error: environment overflow, \"%s\" deleted\n",
420 			name);
421 		return -1;
422 	}
423 	while ((*env = *name++) != '\0')
424 		env++;
425 	for (i = 2; i < argc; ++i) {
426 		char *val = argv[i];
427 
428 		*env = (i == 2) ? '=' : ' ';
429 		while ((*++env = *val++) != '\0');
430 	}
431 
432 	/* end is marked with double '\0' */
433 	*++env = '\0';
434 
435   WRITE_FLASH:
436 
437 	/*
438 	 * Update CRC
439 	 */
440 	*environment.crc = crc32 (0, (uint8_t *) environment.data, ENV_SIZE);
441 
442 	/* write environment back to flash */
443 	if (flash_io (O_RDWR)) {
444 		fprintf (stderr, "Error: can't write fw_env to flash\n");
445 		return -1;
446 	}
447 
448 	return 0;
449 }
450 
451 /*
452  * Test for bad block on NAND, just returns 0 on NOR, on NAND:
453  * 0	- block is good
454  * > 0	- block is bad
455  * < 0	- failed to test
456  */
457 static int flash_bad_block (int fd, uint8_t mtd_type, loff_t *blockstart)
458 {
459 	if (mtd_type == MTD_NANDFLASH) {
460 		int badblock = ioctl (fd, MEMGETBADBLOCK, blockstart);
461 
462 		if (badblock < 0) {
463 			perror ("Cannot read bad block mark");
464 			return badblock;
465 		}
466 
467 		if (badblock) {
468 #ifdef DEBUG
469 			fprintf (stderr, "Bad block at 0x%llx, "
470 				 "skipping\n", *blockstart);
471 #endif
472 			return badblock;
473 		}
474 	}
475 
476 	return 0;
477 }
478 
479 /*
480  * Read data from flash at an offset into a provided buffer. On NAND it skips
481  * bad blocks but makes sure it stays within ENVSECTORS (dev) starting from
482  * the DEVOFFSET (dev) block. On NOR the loop is only run once.
483  */
484 static int flash_read_buf (int dev, int fd, void *buf, size_t count,
485 			   off_t offset, uint8_t mtd_type)
486 {
487 	size_t blocklen;	/* erase / write length - one block on NAND,
488 				   0 on NOR */
489 	size_t processed = 0;	/* progress counter */
490 	size_t readlen = count;	/* current read length */
491 	off_t top_of_range;	/* end of the last block we may use */
492 	off_t block_seek;	/* offset inside the current block to the start
493 				   of the data */
494 	loff_t blockstart;	/* running start of the current block -
495 				   MEMGETBADBLOCK needs 64 bits */
496 	int rc;
497 
498 	/*
499 	 * Start of the first block to be read, relies on the fact, that
500 	 * erase sector size is always a power of 2
501 	 */
502 	blockstart = offset & ~(DEVESIZE (dev) - 1);
503 
504 	/* Offset inside a block */
505 	block_seek = offset - blockstart;
506 
507 	if (mtd_type == MTD_NANDFLASH) {
508 		/*
509 		 * NAND: calculate which blocks we are reading. We have
510 		 * to read one block at a time to skip bad blocks.
511 		 */
512 		blocklen = DEVESIZE (dev);
513 
514 		/*
515 		 * To calculate the top of the range, we have to use the
516 		 * global DEVOFFSET (dev), which can be different from offset
517 		 */
518 		top_of_range = (DEVOFFSET (dev) & ~(blocklen - 1)) +
519 			ENVSECTORS (dev) * blocklen;
520 
521 		/* Limit to one block for the first read */
522 		if (readlen > blocklen - block_seek)
523 			readlen = blocklen - block_seek;
524 	} else {
525 		blocklen = 0;
526 		top_of_range = offset + count;
527 	}
528 
529 	/* This only runs once on NOR flash */
530 	while (processed < count) {
531 		rc = flash_bad_block (fd, mtd_type, &blockstart);
532 		if (rc < 0)		/* block test failed */
533 			return -1;
534 
535 		if (blockstart + block_seek + readlen > top_of_range) {
536 			/* End of range is reached */
537 			fprintf (stderr,
538 				 "Too few good blocks within range\n");
539 			return -1;
540 		}
541 
542 		if (rc) {		/* block is bad */
543 			blockstart += blocklen;
544 			continue;
545 		}
546 
547 		/*
548 		 * If a block is bad, we retry in the next block at the same
549 		 * offset - see common/env_nand.c::writeenv()
550 		 */
551 		lseek (fd, blockstart + block_seek, SEEK_SET);
552 
553 		rc = read (fd, buf + processed, readlen);
554 		if (rc != readlen) {
555 			fprintf (stderr, "Read error on %s: %s\n",
556 				 DEVNAME (dev), strerror (errno));
557 			return -1;
558 		}
559 #ifdef DEBUG
560 		fprintf (stderr, "Read 0x%x bytes at 0x%llx\n",
561 			 rc, blockstart + block_seek);
562 #endif
563 		processed += readlen;
564 		readlen = min (blocklen, count - processed);
565 		block_seek = 0;
566 		blockstart += blocklen;
567 	}
568 
569 	return processed;
570 }
571 
572 /*
573  * Write count bytes at offset, but stay within ENVSETCORS (dev) sectors of
574  * DEVOFFSET (dev). Similar to the read case above, on NOR we erase and write
575  * the whole data at once.
576  */
577 static int flash_write_buf (int dev, int fd, void *buf, size_t count,
578 			    off_t offset, uint8_t mtd_type)
579 {
580 	void *data;
581 	struct erase_info_user erase;
582 	size_t blocklen;	/* length of NAND block / NOR erase sector */
583 	size_t erase_len;	/* whole area that can be erased - may include
584 				   bad blocks */
585 	size_t erasesize;	/* erase / write length - one block on NAND,
586 				   whole area on NOR */
587 	size_t processed = 0;	/* progress counter */
588 	size_t write_total;	/* total size to actually write - excludinig
589 				   bad blocks */
590 	off_t erase_offset;	/* offset to the first erase block (aligned)
591 				   below offset */
592 	off_t block_seek;	/* offset inside the erase block to the start
593 				   of the data */
594 	off_t top_of_range;	/* end of the last block we may use */
595 	loff_t blockstart;	/* running start of the current block -
596 				   MEMGETBADBLOCK needs 64 bits */
597 	int rc;
598 
599 	blocklen = DEVESIZE (dev);
600 
601 	/* Erase sector size is always a power of 2 */
602 	top_of_range = (DEVOFFSET (dev) & ~(blocklen - 1)) +
603 		ENVSECTORS (dev) * blocklen;
604 
605 	erase_offset = offset & ~(blocklen - 1);
606 
607 	/* Maximum area we may use */
608 	erase_len = top_of_range - erase_offset;
609 
610 	blockstart = erase_offset;
611 	/* Offset inside a block */
612 	block_seek = offset - erase_offset;
613 
614 	/*
615 	 * Data size we actually have to write: from the start of the block
616 	 * to the start of the data, then count bytes of data, and to the
617 	 * end of the block
618 	 */
619 	write_total = (block_seek + count + blocklen - 1) & ~(blocklen - 1);
620 
621 	/*
622 	 * Support data anywhere within erase sectors: read out the complete
623 	 * area to be erased, replace the environment image, write the whole
624 	 * block back again.
625 	 */
626 	if (write_total > count) {
627 		data = malloc (erase_len);
628 		if (!data) {
629 			fprintf (stderr,
630 				 "Cannot malloc %u bytes: %s\n",
631 				 erase_len, strerror (errno));
632 			return -1;
633 		}
634 
635 		rc = flash_read_buf (dev, fd, data, write_total, erase_offset,
636 				     mtd_type);
637 		if (write_total != rc)
638 			return -1;
639 
640 		/* Overwrite the old environment */
641 		memcpy (data + block_seek, buf, count);
642 	} else {
643 		/*
644 		 * We get here, iff offset is block-aligned and count is a
645 		 * multiple of blocklen - see write_total calculation above
646 		 */
647 		data = buf;
648 	}
649 
650 	if (mtd_type == MTD_NANDFLASH) {
651 		/*
652 		 * NAND: calculate which blocks we are writing. We have
653 		 * to write one block at a time to skip bad blocks.
654 		 */
655 		erasesize = blocklen;
656 	} else {
657 		erasesize = erase_len;
658 	}
659 
660 	erase.length = erasesize;
661 
662 	/* This only runs once on NOR flash */
663 	while (processed < write_total) {
664 		rc = flash_bad_block (fd, mtd_type, &blockstart);
665 		if (rc < 0)		/* block test failed */
666 			return rc;
667 
668 		if (blockstart + erasesize > top_of_range) {
669 			fprintf (stderr, "End of range reached, aborting\n");
670 			return -1;
671 		}
672 
673 		if (rc) {		/* block is bad */
674 			blockstart += blocklen;
675 			continue;
676 		}
677 
678 		erase.start = blockstart;
679 		ioctl (fd, MEMUNLOCK, &erase);
680 
681 		if (ioctl (fd, MEMERASE, &erase) != 0) {
682 			fprintf (stderr, "MTD erase error on %s: %s\n",
683 				 DEVNAME (dev),
684 				 strerror (errno));
685 			return -1;
686 		}
687 
688 		if (lseek (fd, blockstart, SEEK_SET) == -1) {
689 			fprintf (stderr,
690 				 "Seek error on %s: %s\n",
691 				 DEVNAME (dev), strerror (errno));
692 			return -1;
693 		}
694 
695 #ifdef DEBUG
696 		printf ("Write 0x%x bytes at 0x%llx\n", erasesize, blockstart);
697 #endif
698 		if (write (fd, data + processed, erasesize) != erasesize) {
699 			fprintf (stderr, "Write error on %s: %s\n",
700 				 DEVNAME (dev), strerror (errno));
701 			return -1;
702 		}
703 
704 		ioctl (fd, MEMLOCK, &erase);
705 
706 		processed  += blocklen;
707 		block_seek = 0;
708 		blockstart += blocklen;
709 	}
710 
711 	if (write_total > count)
712 		free (data);
713 
714 	return processed;
715 }
716 
717 /*
718  * Set obsolete flag at offset - NOR flash only
719  */
720 static int flash_flag_obsolete (int dev, int fd, off_t offset)
721 {
722 	int rc;
723 
724 	/* This relies on the fact, that obsolete_flag == 0 */
725 	rc = lseek (fd, offset, SEEK_SET);
726 	if (rc < 0) {
727 		fprintf (stderr, "Cannot seek to set the flag on %s \n",
728 			 DEVNAME (dev));
729 		return rc;
730 	}
731 	rc = write (fd, &obsolete_flag, sizeof (obsolete_flag));
732 	if (rc < 0)
733 		perror ("Could not set obsolete flag");
734 
735 	return rc;
736 }
737 
738 static int flash_write (int fd_current, int fd_target, int dev_target)
739 {
740 	int rc;
741 
742 	switch (environment.flag_scheme) {
743 	case FLAG_NONE:
744 		break;
745 	case FLAG_INCREMENTAL:
746 		(*environment.flags)++;
747 		break;
748 	case FLAG_BOOLEAN:
749 		*environment.flags = active_flag;
750 		break;
751 	default:
752 		fprintf (stderr, "Unimplemented flash scheme %u \n",
753 			 environment.flag_scheme);
754 		return -1;
755 	}
756 
757 #ifdef DEBUG
758 	printf ("Writing new environment at 0x%lx on %s\n",
759 		DEVOFFSET (dev_target), DEVNAME (dev_target));
760 #endif
761 	rc = flash_write_buf (dev_target, fd_target, environment.image,
762 			      CONFIG_ENV_SIZE, DEVOFFSET (dev_target),
763 			      DEVTYPE(dev_target));
764 	if (rc < 0)
765 		return rc;
766 
767 	if (environment.flag_scheme == FLAG_BOOLEAN) {
768 		/* Have to set obsolete flag */
769 		off_t offset = DEVOFFSET (dev_current) +
770 			offsetof (struct env_image_redundant, flags);
771 #ifdef DEBUG
772 		printf ("Setting obsolete flag in environment at 0x%lx on %s\n",
773 			DEVOFFSET (dev_current), DEVNAME (dev_current));
774 #endif
775 		flash_flag_obsolete (dev_current, fd_current, offset);
776 	}
777 
778 	return 0;
779 }
780 
781 static int flash_read (int fd)
782 {
783 	struct mtd_info_user mtdinfo;
784 	int rc;
785 
786 	rc = ioctl (fd, MEMGETINFO, &mtdinfo);
787 	if (rc < 0) {
788 		perror ("Cannot get MTD information");
789 		return -1;
790 	}
791 
792 	if (mtdinfo.type != MTD_NORFLASH && mtdinfo.type != MTD_NANDFLASH) {
793 		fprintf (stderr, "Unsupported flash type %u\n", mtdinfo.type);
794 		return -1;
795 	}
796 
797 	DEVTYPE(dev_current) = mtdinfo.type;
798 
799 	rc = flash_read_buf (dev_current, fd, environment.image, CONFIG_ENV_SIZE,
800 			     DEVOFFSET (dev_current), mtdinfo.type);
801 
802 	return (rc != CONFIG_ENV_SIZE) ? -1 : 0;
803 }
804 
805 static int flash_io (int mode)
806 {
807 	int fd_current, fd_target, rc, dev_target;
808 
809 	/* dev_current: fd_current, erase_current */
810 	fd_current = open (DEVNAME (dev_current), mode);
811 	if (fd_current < 0) {
812 		fprintf (stderr,
813 			 "Can't open %s: %s\n",
814 			 DEVNAME (dev_current), strerror (errno));
815 		return -1;
816 	}
817 
818 	if (mode == O_RDWR) {
819 		if (HaveRedundEnv) {
820 			/* switch to next partition for writing */
821 			dev_target = !dev_current;
822 			/* dev_target: fd_target, erase_target */
823 			fd_target = open (DEVNAME (dev_target), mode);
824 			if (fd_target < 0) {
825 				fprintf (stderr,
826 					 "Can't open %s: %s\n",
827 					 DEVNAME (dev_target),
828 					 strerror (errno));
829 				rc = -1;
830 				goto exit;
831 			}
832 		} else {
833 			dev_target = dev_current;
834 			fd_target = fd_current;
835 		}
836 
837 		rc = flash_write (fd_current, fd_target, dev_target);
838 
839 		if (HaveRedundEnv) {
840 			if (close (fd_target)) {
841 				fprintf (stderr,
842 					"I/O error on %s: %s\n",
843 					DEVNAME (dev_target),
844 					strerror (errno));
845 				rc = -1;
846 			}
847 		}
848 	} else {
849 		rc = flash_read (fd_current);
850 	}
851 
852 exit:
853 	if (close (fd_current)) {
854 		fprintf (stderr,
855 			 "I/O error on %s: %s\n",
856 			 DEVNAME (dev_current), strerror (errno));
857 		return -1;
858 	}
859 
860 	return rc;
861 }
862 
863 /*
864  * s1 is either a simple 'name', or a 'name=value' pair.
865  * s2 is a 'name=value' pair.
866  * If the names match, return the value of s2, else NULL.
867  */
868 
869 static char *envmatch (char * s1, char * s2)
870 {
871 
872 	while (*s1 == *s2++)
873 		if (*s1++ == '=')
874 			return s2;
875 	if (*s1 == '\0' && *(s2 - 1) == '=')
876 		return s2;
877 	return NULL;
878 }
879 
880 /*
881  * Prevent confusion if running from erased flash memory
882  */
883 static int env_init (void)
884 {
885 	int crc0, crc0_ok;
886 	char flag0;
887 	void *addr0;
888 
889 	int crc1, crc1_ok;
890 	char flag1;
891 	void *addr1;
892 
893 	struct env_image_single *single;
894 	struct env_image_redundant *redundant;
895 
896 	if (parse_config ())		/* should fill envdevices */
897 		return -1;
898 
899 	addr0 = calloc (1, CONFIG_ENV_SIZE);
900 	if (addr0 == NULL) {
901 		fprintf (stderr,
902 			"Not enough memory for environment (%ld bytes)\n",
903 			CONFIG_ENV_SIZE);
904 		return -1;
905 	}
906 
907 	/* read environment from FLASH to local buffer */
908 	environment.image = addr0;
909 
910 	if (HaveRedundEnv) {
911 		redundant = addr0;
912 		environment.crc		= &redundant->crc;
913 		environment.flags	= &redundant->flags;
914 		environment.data	= redundant->data;
915 	} else {
916 		single = addr0;
917 		environment.crc		= &single->crc;
918 		environment.flags	= NULL;
919 		environment.data	= single->data;
920 	}
921 
922 	dev_current = 0;
923 	if (flash_io (O_RDONLY))
924 		return -1;
925 
926 	crc0 = crc32 (0, (uint8_t *) environment.data, ENV_SIZE);
927 	crc0_ok = (crc0 == *environment.crc);
928 	if (!HaveRedundEnv) {
929 		if (!crc0_ok) {
930 			fprintf (stderr,
931 				"Warning: Bad CRC, using default environment\n");
932 			memcpy(environment.data, default_environment, sizeof default_environment);
933 		}
934 	} else {
935 		flag0 = *environment.flags;
936 
937 		dev_current = 1;
938 		addr1 = calloc (1, CONFIG_ENV_SIZE);
939 		if (addr1 == NULL) {
940 			fprintf (stderr,
941 				"Not enough memory for environment (%ld bytes)\n",
942 				CONFIG_ENV_SIZE);
943 			return -1;
944 		}
945 		redundant = addr1;
946 
947 		/*
948 		 * have to set environment.image for flash_read(), careful -
949 		 * other pointers in environment still point inside addr0
950 		 */
951 		environment.image = addr1;
952 		if (flash_io (O_RDONLY))
953 			return -1;
954 
955 		/* Check flag scheme compatibility */
956 		if (DEVTYPE(dev_current) == MTD_NORFLASH &&
957 		    DEVTYPE(!dev_current) == MTD_NORFLASH) {
958 			environment.flag_scheme = FLAG_BOOLEAN;
959 		} else if (DEVTYPE(dev_current) == MTD_NANDFLASH &&
960 			   DEVTYPE(!dev_current) == MTD_NANDFLASH) {
961 			environment.flag_scheme = FLAG_INCREMENTAL;
962 		} else {
963 			fprintf (stderr, "Incompatible flash types!\n");
964 			return -1;
965 		}
966 
967 		crc1 = crc32 (0, (uint8_t *) redundant->data, ENV_SIZE);
968 		crc1_ok = (crc1 == redundant->crc);
969 		flag1 = redundant->flags;
970 
971 		if (crc0_ok && !crc1_ok) {
972 			dev_current = 0;
973 		} else if (!crc0_ok && crc1_ok) {
974 			dev_current = 1;
975 		} else if (!crc0_ok && !crc1_ok) {
976 			fprintf (stderr,
977 				"Warning: Bad CRC, using default environment\n");
978 			memcpy (environment.data, default_environment,
979 				sizeof default_environment);
980 			dev_current = 0;
981 		} else {
982 			switch (environment.flag_scheme) {
983 			case FLAG_BOOLEAN:
984 				if (flag0 == active_flag &&
985 				    flag1 == obsolete_flag) {
986 					dev_current = 0;
987 				} else if (flag0 == obsolete_flag &&
988 					   flag1 == active_flag) {
989 					dev_current = 1;
990 				} else if (flag0 == flag1) {
991 					dev_current = 0;
992 				} else if (flag0 == 0xFF) {
993 					dev_current = 0;
994 				} else if (flag1 == 0xFF) {
995 					dev_current = 1;
996 				} else {
997 					dev_current = 0;
998 				}
999 				break;
1000 			case FLAG_INCREMENTAL:
1001 				if ((flag0 == 255 && flag1 == 0) ||
1002 				    flag1 > flag0)
1003 					dev_current = 1;
1004 				else if ((flag1 == 255 && flag0 == 0) ||
1005 					 flag0 > flag1)
1006 					dev_current = 0;
1007 				else /* flags are equal - almost impossible */
1008 					dev_current = 0;
1009 				break;
1010 			default:
1011 				fprintf (stderr, "Unknown flag scheme %u \n",
1012 					 environment.flag_scheme);
1013 				return -1;
1014 			}
1015 		}
1016 
1017 		/*
1018 		 * If we are reading, we don't need the flag and the CRC any
1019 		 * more, if we are writing, we will re-calculate CRC and update
1020 		 * flags before writing out
1021 		 */
1022 		if (dev_current) {
1023 			environment.image	= addr1;
1024 			environment.crc		= &redundant->crc;
1025 			environment.flags	= &redundant->flags;
1026 			environment.data	= redundant->data;
1027 			free (addr0);
1028 		} else {
1029 			environment.image	= addr0;
1030 			/* Other pointers are already set */
1031 			free (addr1);
1032 		}
1033 	}
1034 	return 0;
1035 }
1036 
1037 
1038 static int parse_config ()
1039 {
1040 	struct stat st;
1041 
1042 #if defined(CONFIG_FILE)
1043 	/* Fills in DEVNAME(), ENVSIZE(), DEVESIZE(). Or don't. */
1044 	if (get_config (CONFIG_FILE)) {
1045 		fprintf (stderr,
1046 			"Cannot parse config file: %s\n", strerror (errno));
1047 		return -1;
1048 	}
1049 #else
1050 	strcpy (DEVNAME (0), DEVICE1_NAME);
1051 	DEVOFFSET (0) = DEVICE1_OFFSET;
1052 	ENVSIZE (0) = ENV1_SIZE;
1053 	DEVESIZE (0) = DEVICE1_ESIZE;
1054 	ENVSECTORS (0) = DEVICE1_ENVSECTORS;
1055 #ifdef HAVE_REDUND
1056 	strcpy (DEVNAME (1), DEVICE2_NAME);
1057 	DEVOFFSET (1) = DEVICE2_OFFSET;
1058 	ENVSIZE (1) = ENV2_SIZE;
1059 	DEVESIZE (1) = DEVICE2_ESIZE;
1060 	ENVSECTORS (1) = DEVICE2_ENVSECTORS;
1061 	HaveRedundEnv = 1;
1062 #endif
1063 #endif
1064 	if (stat (DEVNAME (0), &st)) {
1065 		fprintf (stderr,
1066 			"Cannot access MTD device %s: %s\n",
1067 			DEVNAME (0), strerror (errno));
1068 		return -1;
1069 	}
1070 
1071 	if (HaveRedundEnv && stat (DEVNAME (1), &st)) {
1072 		fprintf (stderr,
1073 			"Cannot access MTD device %s: %s\n",
1074 			DEVNAME (1), strerror (errno));
1075 		return -1;
1076 	}
1077 	return 0;
1078 }
1079 
1080 #if defined(CONFIG_FILE)
1081 static int get_config (char *fname)
1082 {
1083 	FILE *fp;
1084 	int i = 0;
1085 	int rc;
1086 	char dump[128];
1087 
1088 	fp = fopen (fname, "r");
1089 	if (fp == NULL)
1090 		return -1;
1091 
1092 	while (i < 2 && fgets (dump, sizeof (dump), fp)) {
1093 		/* Skip incomplete conversions and comment strings */
1094 		if (dump[0] == '#')
1095 			continue;
1096 
1097 		rc = sscanf (dump, "%s %lx %lx %lx %lx",
1098 			     DEVNAME (i),
1099 			     &DEVOFFSET (i),
1100 			     &ENVSIZE (i),
1101 			     &DEVESIZE (i),
1102 			     &ENVSECTORS (i));
1103 
1104 		if (rc < 4)
1105 			continue;
1106 
1107 		if (rc < 5)
1108 			/* Default - 1 sector */
1109 			ENVSECTORS (i) = 1;
1110 
1111 		i++;
1112 	}
1113 	fclose (fp);
1114 
1115 	HaveRedundEnv = i - 1;
1116 	if (!i) {			/* No valid entries found */
1117 		errno = EINVAL;
1118 		return -1;
1119 	} else
1120 		return 0;
1121 }
1122 #endif
1123