diff options
Diffstat (limited to 'tools/env/fw_env.c')
| -rw-r--r-- | tools/env/fw_env.c | 876 | 
1 files changed, 610 insertions, 266 deletions
| diff --git a/tools/env/fw_env.c b/tools/env/fw_env.c index b8bca91c3..a46205d86 100644 --- a/tools/env/fw_env.c +++ b/tools/env/fw_env.c @@ -2,6 +2,9 @@   * (C) Copyright 2000-2008   * Wolfgang Denk, DENX Software Engineering, wd@denx.de.   * + * (C) Copyright 2008 + * Guennadi Liakhovetski, DENX Software Engineering, lg@denx.de. + *   * See file CREDITS for list of people who contributed to this   * project.   * @@ -33,6 +36,7 @@  #include <unistd.h>  #ifdef MTD_OLD +# include <stdint.h>  # include <linux/mtd/mtd.h>  #else  # define  __user	/* nothing */ @@ -44,36 +48,75 @@  #define	CMD_GETENV	"fw_printenv"  #define	CMD_SETENV	"fw_setenv" -typedef struct envdev_s { +#define min(x, y) ({				\ +	typeof(x) _min1 = (x);			\ +	typeof(y) _min2 = (y);			\ +	(void) (&_min1 == &_min2);		\ +	_min1 < _min2 ? _min1 : _min2; }) + +struct envdev_s {  	char devname[16];		/* Device name */  	ulong devoff;			/* Device offset */  	ulong env_size;			/* environment size */  	ulong erase_size;		/* device erase size */ -} envdev_t; +	ulong env_sectors;		/* number of environment sectors */ +	uint8_t mtd_type;		/* type of the MTD device */ +}; -static envdev_t envdevices[2]; -static int curdev; +static struct envdev_s envdevices[2] = +{ +	{ +		.mtd_type = MTD_ABSENT, +	}, { +		.mtd_type = MTD_ABSENT, +	}, +}; +static int dev_current;  #define DEVNAME(i)    envdevices[(i)].devname  #define DEVOFFSET(i)  envdevices[(i)].devoff  #define ENVSIZE(i)    envdevices[(i)].env_size  #define DEVESIZE(i)   envdevices[(i)].erase_size +#define ENVSECTORS(i) envdevices[(i)].env_sectors +#define DEVTYPE(i)    envdevices[(i)].mtd_type -#define CFG_ENV_SIZE ENVSIZE(curdev) +#define CONFIG_ENV_SIZE ENVSIZE(dev_current)  #define ENV_SIZE      getenvsize() -typedef struct environment_s { -	ulong crc;			/* CRC32 over data bytes    */ -	unsigned char flags;		/* active or obsolete */ -	char *data; -} env_t; +struct env_image_single { +	uint32_t	crc;	/* CRC32 over data bytes    */ +	char		data[]; +}; -static env_t environment; +struct env_image_redundant { +	uint32_t	crc;	/* CRC32 over data bytes    */ +	unsigned char	flags;	/* active or obsolete */ +	char		data[]; +}; + +enum flag_scheme { +	FLAG_NONE, +	FLAG_BOOLEAN, +	FLAG_INCREMENTAL, +}; + +struct environment { +	void			*image; +	uint32_t		*crc; +	unsigned char		*flags; +	char			*data; +	enum flag_scheme	flag_scheme; +}; + +static struct environment environment = { +	.flag_scheme = FLAG_NONE, +};  static int HaveRedundEnv = 0;  static unsigned char active_flag = 1; +/* obsolete_flag must be 0 to efficiently set it on NOR flash without erasing */  static unsigned char obsolete_flag = 0; @@ -114,6 +157,12 @@ static char default_environment[] = {  #ifdef	CONFIG_ETH3ADDR  	"eth3addr=" MK_STR (CONFIG_ETH3ADDR) "\0"  #endif +#ifdef	CONFIG_ETH4ADDR +	"eth4addr=" MK_STR (CONFIG_ETH4ADDR) "\0" +#endif +#ifdef	CONFIG_ETH5ADDR +	"eth5addr=" MK_STR (CONFIG_ETH5ADDR) "\0" +#endif  #ifdef	CONFIG_ETHPRIME  	"ethprime=" CONFIG_ETHPRIME "\0"  #endif @@ -123,8 +172,8 @@ static char default_environment[] = {  #ifdef	CONFIG_SERVERIP  	"serverip=" MK_STR (CONFIG_SERVERIP) "\0"  #endif -#ifdef	CFG_AUTOLOAD -	"autoload=" CFG_AUTOLOAD "\0" +#ifdef	CONFIG_SYS_AUTOLOAD +	"autoload=" CONFIG_SYS_AUTOLOAD "\0"  #endif  #ifdef	CONFIG_ROOTPATH  	"rootpath=" MK_STR (CONFIG_ROOTPATH) "\0" @@ -156,7 +205,7 @@ static char default_environment[] = {  #ifdef  CONFIG_EXTRA_ENV_SETTINGS  	CONFIG_EXTRA_ENV_SETTINGS  #endif -	"\0"			/* Termimate env_t data with 2 NULs */ +	"\0"		/* Termimate struct environment data with 2 NULs */  };  static int flash_io (int mode); @@ -169,7 +218,7 @@ static int get_config (char *);  #endif  static inline ulong getenvsize (void)  { -	ulong rc = CFG_ENV_SIZE - sizeof (long); +	ulong rc = CONFIG_ENV_SIZE - sizeof (long);  	if (HaveRedundEnv)  		rc -= sizeof (char); @@ -185,7 +234,7 @@ char *fw_getenv (char *name)  	char *env, *nxt;  	if (env_init ()) -		return (NULL); +		return NULL;  	for (env = environment.data; *env; env = nxt + 1) {  		char *val; @@ -194,15 +243,15 @@ char *fw_getenv (char *name)  			if (nxt >= &environment.data[ENV_SIZE]) {  				fprintf (stderr, "## Error: "  					"environment not terminated\n"); -				return (NULL); +				return NULL;  			}  		}  		val = envmatch (name, env);  		if (!val)  			continue; -		return (val); +		return val;  	} -	return (NULL); +	return NULL;  }  /* @@ -216,7 +265,7 @@ int fw_printenv (int argc, char *argv[])  	int rc = 0;  	if (env_init ()) -		return (-1); +		return -1;  	if (argc == 1) {		/* Print all env variables  */  		for (env = environment.data; *env; env = nxt + 1) { @@ -224,13 +273,13 @@ int fw_printenv (int argc, char *argv[])  				if (nxt >= &environment.data[ENV_SIZE]) {  					fprintf (stderr, "## Error: "  						"environment not terminated\n"); -					return (-1); +					return -1;  				}  			}  			printf ("%s\n", env);  		} -		return (0); +		return 0;  	}  	if (strcmp (argv[1], "-n") == 0) { @@ -240,7 +289,7 @@ int fw_printenv (int argc, char *argv[])  		if (argc != 2) {  			fprintf (stderr, "## Error: "  				"`-n' option requires exactly one argument\n"); -			return (-1); +			return -1;  		}  	} else {  		n_flag = 0; @@ -256,7 +305,7 @@ int fw_printenv (int argc, char *argv[])  				if (nxt >= &environment.data[ENV_SIZE]) {  					fprintf (stderr, "## Error: "  						"environment not terminated\n"); -					return (-1); +					return -1;  				}  			}  			val = envmatch (name, env); @@ -275,11 +324,11 @@ int fw_printenv (int argc, char *argv[])  		}  	} -	return (rc); +	return rc;  }  /* - * Deletes or sets environment variables. Returns errno style error codes: + * Deletes or sets environment variables. Returns -1 and sets errno error codes:   * 0	  - OK   * EINVAL - need at least 1 argument   * EROFS  - certain variables ("ethaddr", "serial#") cannot be @@ -294,11 +343,12 @@ int fw_setenv (int argc, char *argv[])  	char *name;  	if (argc < 2) { -		return (EINVAL); +		errno = EINVAL; +		return -1;  	}  	if (env_init ()) -		return (errno); +		return -1;  	name = argv[1]; @@ -310,7 +360,8 @@ int fw_setenv (int argc, char *argv[])  			if (nxt >= &environment.data[ENV_SIZE]) {  				fprintf (stderr, "## Error: "  					"environment not terminated\n"); -				return (EINVAL); +				errno = EINVAL; +				return -1;  			}  		}  		if ((oldval = envmatch (name, env)) != NULL) @@ -327,7 +378,8 @@ int fw_setenv (int argc, char *argv[])  		if ((strcmp (name, "ethaddr") == 0) ||  			(strcmp (name, "serial#") == 0)) {  			fprintf (stderr, "Can't overwrite \"%s\"\n", name); -			return (EROFS); +			errno = EROFS; +			return -1;  		}  		if (*++nxt == '\0') { @@ -355,7 +407,7 @@ int fw_setenv (int argc, char *argv[])  		++env;  	/*  	 * Overflow when: -	 * "name" + "=" + "val" +"\0\0"  > CFG_ENV_SIZE - (env-environment) +	 * "name" + "=" + "val" +"\0\0"  > CONFIG_ENV_SIZE - (env-environment)  	 */  	len = strlen (name) + 2;  	/* add '=' for first arg, ' ' for all others */ @@ -366,7 +418,7 @@ int fw_setenv (int argc, char *argv[])  		fprintf (stderr,  			"Error: environment overflow, \"%s\" deleted\n",  			name); -		return (-1); +		return -1;  	}  	while ((*env = *name++) != '\0')  		env++; @@ -382,195 +434,430 @@ int fw_setenv (int argc, char *argv[])    WRITE_FLASH: -	/* Update CRC */ -	environment.crc = crc32 (0, (uint8_t*) environment.data, ENV_SIZE); +	/* +	 * Update CRC +	 */ +	*environment.crc = crc32 (0, (uint8_t *) environment.data, ENV_SIZE);  	/* write environment back to flash */  	if (flash_io (O_RDWR)) {  		fprintf (stderr, "Error: can't write fw_env to flash\n"); -		return (-1); +		return -1;  	} -	return (0); +	return 0;  } -static int flash_io (int mode) +/* + * Test for bad block on NAND, just returns 0 on NOR, on NAND: + * 0	- block is good + * > 0	- block is bad + * < 0	- failed to test + */ +static int flash_bad_block (int fd, uint8_t mtd_type, loff_t *blockstart)  { -	int fd, fdr, rc, otherdev, len, resid; -	erase_info_t erase; -	char *data = NULL; +	if (mtd_type == MTD_NANDFLASH) { +		int badblock = ioctl (fd, MEMGETBADBLOCK, blockstart); -	if ((fd = open (DEVNAME (curdev), mode)) < 0) { -		fprintf (stderr, -			"Can't open %s: %s\n", -			DEVNAME (curdev), strerror (errno)); -		return (-1); +		if (badblock < 0) { +			perror ("Cannot read bad block mark"); +			return badblock; +		} + +		if (badblock) { +#ifdef DEBUG +			fprintf (stderr, "Bad block at 0x%llx, " +				 "skipping\n", *blockstart); +#endif +			return badblock; +		}  	} -	len = sizeof (environment.crc); -	if (HaveRedundEnv) { -		len += sizeof (environment.flags); +	return 0; +} + +/* + * Read data from flash at an offset into a provided buffer. On NAND it skips + * bad blocks but makes sure it stays within ENVSECTORS (dev) starting from + * the DEVOFFSET (dev) block. On NOR the loop is only run once. + */ +static int flash_read_buf (int dev, int fd, void *buf, size_t count, +			   off_t offset, uint8_t mtd_type) +{ +	size_t blocklen;	/* erase / write length - one block on NAND, +				   0 on NOR */ +	size_t processed = 0;	/* progress counter */ +	size_t readlen = count;	/* current read length */ +	off_t top_of_range;	/* end of the last block we may use */ +	off_t block_seek;	/* offset inside the current block to the start +				   of the data */ +	loff_t blockstart;	/* running start of the current block - +				   MEMGETBADBLOCK needs 64 bits */ +	int rc; + +	/* +	 * Start of the first block to be read, relies on the fact, that +	 * erase sector size is always a power of 2 +	 */ +	blockstart = offset & ~(DEVESIZE (dev) - 1); + +	/* Offset inside a block */ +	block_seek = offset - blockstart; + +	if (mtd_type == MTD_NANDFLASH) { +		/* +		 * NAND: calculate which blocks we are reading. We have +		 * to read one block at a time to skip bad blocks. +		 */ +		blocklen = DEVESIZE (dev); + +		/* +		 * To calculate the top of the range, we have to use the +		 * global DEVOFFSET (dev), which can be different from offset +		 */ +		top_of_range = (DEVOFFSET (dev) & ~(blocklen - 1)) + +			ENVSECTORS (dev) * blocklen; + +		/* Limit to one block for the first read */ +		if (readlen > blocklen - block_seek) +			readlen = blocklen - block_seek; +	} else { +		blocklen = 0; +		top_of_range = offset + count;  	} -	if (mode == O_RDWR) { -		if (HaveRedundEnv) { -			/* switch to next partition for writing */ -			otherdev = !curdev; -			if ((fdr = open (DEVNAME (otherdev), mode)) < 0) { -				fprintf (stderr, -					"Can't open %s: %s\n", -					DEVNAME (otherdev), -					strerror (errno)); -				return (-1); -			} -		} else { -			otherdev = curdev; -			fdr = fd; -		} -		printf ("Unlocking flash...\n"); -		erase.length = DEVESIZE (otherdev); -		erase.start = DEVOFFSET (otherdev); -		ioctl (fdr, MEMUNLOCK, &erase); +	/* This only runs once on NOR flash */ +	while (processed < count) { +		rc = flash_bad_block (fd, mtd_type, &blockstart); +		if (rc < 0)		/* block test failed */ +			return -1; -		if (HaveRedundEnv) { -			erase.length = DEVESIZE (curdev); -			erase.start = DEVOFFSET (curdev); -			ioctl (fd, MEMUNLOCK, &erase); -			environment.flags = active_flag; +		if (blockstart + block_seek + readlen > top_of_range) { +			/* End of range is reached */ +			fprintf (stderr, +				 "Too few good blocks within range\n"); +			return -1;  		} -		printf ("Done\n"); -		resid = DEVESIZE (otherdev) - CFG_ENV_SIZE; -		if (resid) { -			if ((data = malloc (resid)) == NULL) { -				fprintf (stderr, -					"Cannot malloc %d bytes: %s\n", -					resid, -					strerror (errno)); -				return (-1); -			} -			if (lseek (fdr, DEVOFFSET (otherdev) + CFG_ENV_SIZE, SEEK_SET) -				== -1) { -				fprintf (stderr, "seek error on %s: %s\n", -					DEVNAME (otherdev), -					strerror (errno)); -				return (-1); -			} -			if ((rc = read (fdr, data, resid)) != resid) { -				fprintf (stderr, -					"read error on %s: %s\n", -					DEVNAME (otherdev), -					strerror (errno)); -				return (-1); -			} +		if (rc) {		/* block is bad */ +			blockstart += blocklen; +			continue;  		} -		printf ("Erasing old environment...\n"); +		/* +		 * If a block is bad, we retry in the next block at the same +		 * offset - see common/env_nand.c::writeenv() +		 */ +		lseek (fd, blockstart + block_seek, SEEK_SET); -		erase.length = DEVESIZE (otherdev); -		erase.start = DEVOFFSET (otherdev); -		if (ioctl (fdr, MEMERASE, &erase) != 0) { -			fprintf (stderr, "MTD erase error on %s: %s\n", -				DEVNAME (otherdev), -				strerror (errno)); -			return (-1); +		rc = read (fd, buf + processed, readlen); +		if (rc != readlen) { +			fprintf (stderr, "Read error on %s: %s\n", +				 DEVNAME (dev), strerror (errno)); +			return -1;  		} +#ifdef DEBUG +		fprintf (stderr, "Read 0x%x bytes at 0x%llx\n", +			 rc, blockstart + block_seek); +#endif +		processed += readlen; +		readlen = min (blocklen, count - processed); +		block_seek = 0; +		blockstart += blocklen; +	} + +	return processed; +} + +/* + * Write count bytes at offset, but stay within ENVSETCORS (dev) sectors of + * DEVOFFSET (dev). Similar to the read case above, on NOR we erase and write + * the whole data at once. + */ +static int flash_write_buf (int dev, int fd, void *buf, size_t count, +			    off_t offset, uint8_t mtd_type) +{ +	void *data; +	struct erase_info_user erase; +	size_t blocklen;	/* length of NAND block / NOR erase sector */ +	size_t erase_len;	/* whole area that can be erased - may include +				   bad blocks */ +	size_t erasesize;	/* erase / write length - one block on NAND, +				   whole area on NOR */ +	size_t processed = 0;	/* progress counter */ +	size_t write_total;	/* total size to actually write - excludinig +				   bad blocks */ +	off_t erase_offset;	/* offset to the first erase block (aligned) +				   below offset */ +	off_t block_seek;	/* offset inside the erase block to the start +				   of the data */ +	off_t top_of_range;	/* end of the last block we may use */ +	loff_t blockstart;	/* running start of the current block - +				   MEMGETBADBLOCK needs 64 bits */ +	int rc; + +	blocklen = DEVESIZE (dev); + +	/* Erase sector size is always a power of 2 */ +	top_of_range = (DEVOFFSET (dev) & ~(blocklen - 1)) + +		ENVSECTORS (dev) * blocklen; -		printf ("Done\n"); +	erase_offset = offset & ~(blocklen - 1); -		printf ("Writing environment to %s...\n", DEVNAME (otherdev)); -		if (lseek (fdr, DEVOFFSET (otherdev), SEEK_SET) == -1) { +	/* Maximum area we may use */ +	erase_len = top_of_range - erase_offset; + +	blockstart = erase_offset; +	/* Offset inside a block */ +	block_seek = offset - erase_offset; + +	/* +	 * Data size we actually have to write: from the start of the block +	 * to the start of the data, then count bytes of data, and to the +	 * end of the block +	 */ +	write_total = (block_seek + count + blocklen - 1) & ~(blocklen - 1); + +	/* +	 * Support data anywhere within erase sectors: read out the complete +	 * area to be erased, replace the environment image, write the whole +	 * block back again. +	 */ +	if (write_total > count) { +		data = malloc (erase_len); +		if (!data) {  			fprintf (stderr, -				"seek error on %s: %s\n", -				DEVNAME (otherdev), strerror (errno)); -			return (-1); +				 "Cannot malloc %u bytes: %s\n", +				 erase_len, strerror (errno)); +			return -1;  		} -		if (write (fdr, &environment, len) != len) { -			fprintf (stderr, -				"CRC write error on %s: %s\n", -				DEVNAME (otherdev), strerror (errno)); -			return (-1); + +		rc = flash_read_buf (dev, fd, data, write_total, erase_offset, +				     mtd_type); +		if (write_total != rc) +			return -1; + +		/* Overwrite the old environment */ +		memcpy (data + block_seek, buf, count); +	} else { +		/* +		 * We get here, iff offset is block-aligned and count is a +		 * multiple of blocklen - see write_total calculation above +		 */ +		data = buf; +	} + +	if (mtd_type == MTD_NANDFLASH) { +		/* +		 * NAND: calculate which blocks we are writing. We have +		 * to write one block at a time to skip bad blocks. +		 */ +		erasesize = blocklen; +	} else { +		erasesize = erase_len; +	} + +	erase.length = erasesize; + +	/* This only runs once on NOR flash */ +	while (processed < write_total) { +		rc = flash_bad_block (fd, mtd_type, &blockstart); +		if (rc < 0)		/* block test failed */ +			return rc; + +		if (blockstart + erasesize > top_of_range) { +			fprintf (stderr, "End of range reached, aborting\n"); +			return -1;  		} -		if (write (fdr, environment.data, ENV_SIZE) != ENV_SIZE) { + +		if (rc) {		/* block is bad */ +			blockstart += blocklen; +			continue; +		} + +		erase.start = blockstart; +		ioctl (fd, MEMUNLOCK, &erase); + +		if (ioctl (fd, MEMERASE, &erase) != 0) { +			fprintf (stderr, "MTD erase error on %s: %s\n", +				 DEVNAME (dev), +				 strerror (errno)); +			return -1; +		} + +		if (lseek (fd, blockstart, SEEK_SET) == -1) {  			fprintf (stderr, -				"Write error on %s: %s\n", -				DEVNAME (otherdev), strerror (errno)); -			return (-1); +				 "Seek error on %s: %s\n", +				 DEVNAME (dev), strerror (errno)); +			return -1;  		} -		if (resid) { -			if (write (fdr, data, resid) != resid) { -				fprintf (stderr, -					"write error on %s: %s\n", -					DEVNAME (curdev), strerror (errno)); -				return (-1); -			} -			free (data); + +#ifdef DEBUG +		printf ("Write 0x%x bytes at 0x%llx\n", erasesize, blockstart); +#endif +		if (write (fd, data + processed, erasesize) != erasesize) { +			fprintf (stderr, "Write error on %s: %s\n", +				 DEVNAME (dev), strerror (errno)); +			return -1;  		} + +		ioctl (fd, MEMLOCK, &erase); + +		processed  += blocklen; +		block_seek = 0; +		blockstart += blocklen; +	} + +	if (write_total > count) +		free (data); + +	return processed; +} + +/* + * Set obsolete flag at offset - NOR flash only + */ +static int flash_flag_obsolete (int dev, int fd, off_t offset) +{ +	int rc; + +	/* This relies on the fact, that obsolete_flag == 0 */ +	rc = lseek (fd, offset, SEEK_SET); +	if (rc < 0) { +		fprintf (stderr, "Cannot seek to set the flag on %s \n", +			 DEVNAME (dev)); +		return rc; +	} +	rc = write (fd, &obsolete_flag, sizeof (obsolete_flag)); +	if (rc < 0) +		perror ("Could not set obsolete flag"); + +	return rc; +} + +static int flash_write (int fd_current, int fd_target, int dev_target) +{ +	int rc; + +	switch (environment.flag_scheme) { +	case FLAG_NONE: +		break; +	case FLAG_INCREMENTAL: +		(*environment.flags)++; +		break; +	case FLAG_BOOLEAN: +		*environment.flags = active_flag; +		break; +	default: +		fprintf (stderr, "Unimplemented flash scheme %u \n", +			 environment.flag_scheme); +		return -1; +	} + +#ifdef DEBUG +	printf ("Writing new environment at 0x%lx on %s\n", +		DEVOFFSET (dev_target), DEVNAME (dev_target)); +#endif +	rc = flash_write_buf (dev_target, fd_target, environment.image, +			      CONFIG_ENV_SIZE, DEVOFFSET (dev_target), +			      DEVTYPE(dev_target)); +	if (rc < 0) +		return rc; + +	if (environment.flag_scheme == FLAG_BOOLEAN) { +		/* Have to set obsolete flag */ +		off_t offset = DEVOFFSET (dev_current) + +			offsetof (struct env_image_redundant, flags); +#ifdef DEBUG +		printf ("Setting obsolete flag in environment at 0x%lx on %s\n", +			DEVOFFSET (dev_current), DEVNAME (dev_current)); +#endif +		flash_flag_obsolete (dev_current, fd_current, offset); +	} + +	return 0; +} + +static int flash_read (int fd) +{ +	struct mtd_info_user mtdinfo; +	int rc; + +	rc = ioctl (fd, MEMGETINFO, &mtdinfo); +	if (rc < 0) { +		perror ("Cannot get MTD information"); +		return -1; +	} + +	if (mtdinfo.type != MTD_NORFLASH && mtdinfo.type != MTD_NANDFLASH) { +		fprintf (stderr, "Unsupported flash type %u\n", mtdinfo.type); +		return -1; +	} + +	DEVTYPE(dev_current) = mtdinfo.type; + +	rc = flash_read_buf (dev_current, fd, environment.image, CONFIG_ENV_SIZE, +			     DEVOFFSET (dev_current), mtdinfo.type); + +	return (rc != CONFIG_ENV_SIZE) ? -1 : 0; +} + +static int flash_io (int mode) +{ +	int fd_current, fd_target, rc, dev_target; + +	/* dev_current: fd_current, erase_current */ +	fd_current = open (DEVNAME (dev_current), mode); +	if (fd_current < 0) { +		fprintf (stderr, +			 "Can't open %s: %s\n", +			 DEVNAME (dev_current), strerror (errno)); +		return -1; +	} + +	if (mode == O_RDWR) {  		if (HaveRedundEnv) { -			/* change flag on current active env partition */ -			if (lseek (fd, DEVOFFSET (curdev) + sizeof (ulong), SEEK_SET) -				== -1) { -				fprintf (stderr, "seek error on %s: %s\n", -					DEVNAME (curdev), strerror (errno)); -				return (-1); -			} -			if (write (fd, &obsolete_flag, sizeof (obsolete_flag)) != -				sizeof (obsolete_flag)) { +			/* switch to next partition for writing */ +			dev_target = !dev_current; +			/* dev_target: fd_target, erase_target */ +			fd_target = open (DEVNAME (dev_target), mode); +			if (fd_target < 0) {  				fprintf (stderr, -					"Write error on %s: %s\n", -					DEVNAME (curdev), strerror (errno)); -				return (-1); +					 "Can't open %s: %s\n", +					 DEVNAME (dev_target), +					 strerror (errno)); +				rc = -1; +				goto exit;  			} +		} else { +			dev_target = dev_current; +			fd_target = fd_current;  		} -		printf ("Done\n"); -		printf ("Locking ...\n"); -		erase.length = DEVESIZE (otherdev); -		erase.start = DEVOFFSET (otherdev); -		ioctl (fdr, MEMLOCK, &erase); + +		rc = flash_write (fd_current, fd_target, dev_target); +  		if (HaveRedundEnv) { -			erase.length = DEVESIZE (curdev); -			erase.start = DEVOFFSET (curdev); -			ioctl (fd, MEMLOCK, &erase); -			if (close (fdr)) { +			if (close (fd_target)) {  				fprintf (stderr,  					"I/O error on %s: %s\n", -					DEVNAME (otherdev), +					DEVNAME (dev_target),  					strerror (errno)); -				return (-1); +				rc = -1;  			}  		} -		printf ("Done\n");  	} else { - -		if (lseek (fd, DEVOFFSET (curdev), SEEK_SET) == -1) { -			fprintf (stderr, -				"seek error on %s: %s\n", -				DEVNAME (curdev), strerror (errno)); -			return (-1); -		} -		if (read (fd, &environment, len) != len) { -			fprintf (stderr, -				"CRC read error on %s: %s\n", -				DEVNAME (curdev), strerror (errno)); -			return (-1); -		} -		if ((rc = read (fd, environment.data, ENV_SIZE)) != ENV_SIZE) { -			fprintf (stderr, -				"Read error on %s: %s\n", -				DEVNAME (curdev), strerror (errno)); -			return (-1); -		} +		rc = flash_read (fd_current);  	} -	if (close (fd)) { +exit: +	if (close (fd_current)) {  		fprintf (stderr, -			"I/O error on %s: %s\n", -			DEVNAME (curdev), strerror (errno)); -		return (-1); +			 "I/O error on %s: %s\n", +			 DEVNAME (dev_current), strerror (errno)); +		return -1;  	} -	/* everything ok */ -	return (0); +	return rc;  }  /* @@ -584,10 +871,10 @@ static char *envmatch (char * s1, char * s2)  	while (*s1 == *s2++)  		if (*s1++ == '=') -			return (s2); +			return s2;  	if (*s1 == '\0' && *(s2 - 1) == '=') -		return (s2); -	return (NULL); +		return s2; +	return NULL;  }  /* @@ -595,108 +882,156 @@ static char *envmatch (char * s1, char * s2)   */  static int env_init (void)  { +	int crc0, crc0_ok; +	char flag0; +	void *addr0; +  	int crc1, crc1_ok; -	char *addr1; +	char flag1; +	void *addr1; -	int crc2, crc2_ok; -	char flag1, flag2, *addr2; +	struct env_image_single *single; +	struct env_image_redundant *redundant;  	if (parse_config ())		/* should fill envdevices */ -		return 1; +		return -1; -	if ((addr1 = calloc (1, ENV_SIZE)) == NULL) { +	addr0 = calloc (1, CONFIG_ENV_SIZE); +	if (addr0 == NULL) {  		fprintf (stderr,  			"Not enough memory for environment (%ld bytes)\n", -			ENV_SIZE); -		return (errno); +			CONFIG_ENV_SIZE); +		return -1;  	}  	/* read environment from FLASH to local buffer */ -	environment.data = addr1; -	curdev = 0; -	if (flash_io (O_RDONLY)) { -		return (errno); +	environment.image = addr0; + +	if (HaveRedundEnv) { +		redundant = addr0; +		environment.crc		= &redundant->crc; +		environment.flags	= &redundant->flags; +		environment.data	= redundant->data; +	} else { +		single = addr0; +		environment.crc		= &single->crc; +		environment.flags	= NULL; +		environment.data	= single->data;  	} -	crc1_ok = ((crc1 = crc32 (0, (uint8_t *) environment.data, ENV_SIZE)) -			   == environment.crc); +	dev_current = 0; +	if (flash_io (O_RDONLY)) +		return -1; + +	crc0 = crc32 (0, (uint8_t *) environment.data, ENV_SIZE); +	crc0_ok = (crc0 == *environment.crc);  	if (!HaveRedundEnv) { -		if (!crc1_ok) { +		if (!crc0_ok) {  			fprintf (stderr,  				"Warning: Bad CRC, using default environment\n");  			memcpy(environment.data, default_environment, sizeof default_environment);  		}  	} else { -		flag1 = environment.flags; +		flag0 = *environment.flags; -		curdev = 1; -		if ((addr2 = calloc (1, ENV_SIZE)) == NULL) { +		dev_current = 1; +		addr1 = calloc (1, CONFIG_ENV_SIZE); +		if (addr1 == NULL) {  			fprintf (stderr,  				"Not enough memory for environment (%ld bytes)\n", -				ENV_SIZE); -			return (errno); +				CONFIG_ENV_SIZE); +			return -1;  		} -		environment.data = addr2; +		redundant = addr1; -		if (flash_io (O_RDONLY)) { -			return (errno); +		/* +		 * have to set environment.image for flash_read(), careful - +		 * other pointers in environment still point inside addr0 +		 */ +		environment.image = addr1; +		if (flash_io (O_RDONLY)) +			return -1; + +		/* Check flag scheme compatibility */ +		if (DEVTYPE(dev_current) == MTD_NORFLASH && +		    DEVTYPE(!dev_current) == MTD_NORFLASH) { +			environment.flag_scheme = FLAG_BOOLEAN; +		} else if (DEVTYPE(dev_current) == MTD_NANDFLASH && +			   DEVTYPE(!dev_current) == MTD_NANDFLASH) { +			environment.flag_scheme = FLAG_INCREMENTAL; +		} else { +			fprintf (stderr, "Incompatible flash types!\n"); +			return -1;  		} -		crc2_ok = ((crc2 = crc32 (0, (uint8_t *) environment.data, ENV_SIZE)) -				   == environment.crc); -		flag2 = environment.flags; +		crc1 = crc32 (0, (uint8_t *) redundant->data, ENV_SIZE); +		crc1_ok = (crc1 == redundant->crc); +		flag1 = redundant->flags; -		if (crc1_ok && !crc2_ok) { -			environment.data = addr1; -			environment.flags = flag1; -			environment.crc = crc1; -			curdev = 0; -			free (addr2); -		} else if (!crc1_ok && crc2_ok) { -			environment.data = addr2; -			environment.flags = flag2; -			environment.crc = crc2; -			curdev = 1; -			free (addr1); -		} else if (!crc1_ok && !crc2_ok) { +		if (crc0_ok && !crc1_ok) { +			dev_current = 0; +		} else if (!crc0_ok && crc1_ok) { +			dev_current = 1; +		} else if (!crc0_ok && !crc1_ok) {  			fprintf (stderr,  				"Warning: Bad CRC, using default environment\n"); -			memcpy(environment.data, default_environment, sizeof default_environment); -			curdev = 0; -			free (addr1); -		} else if (flag1 == active_flag && flag2 == obsolete_flag) { -			environment.data = addr1; -			environment.flags = flag1; -			environment.crc = crc1; -			curdev = 0; -			free (addr2); -		} else if (flag1 == obsolete_flag && flag2 == active_flag) { -			environment.data = addr2; -			environment.flags = flag2; -			environment.crc = crc2; -			curdev = 1; -			free (addr1); -		} else if (flag1 == flag2) { -			environment.data = addr1; -			environment.flags = flag1; -			environment.crc = crc1; -			curdev = 0; -			free (addr2); -		} else if (flag1 == 0xFF) { -			environment.data = addr1; -			environment.flags = flag1; -			environment.crc = crc1; -			curdev = 0; -			free (addr2); -		} else if (flag2 == 0xFF) { -			environment.data = addr2; -			environment.flags = flag2; -			environment.crc = crc2; -			curdev = 1; +			memcpy (environment.data, default_environment, +				sizeof default_environment); +			dev_current = 0; +		} else { +			switch (environment.flag_scheme) { +			case FLAG_BOOLEAN: +				if (flag0 == active_flag && +				    flag1 == obsolete_flag) { +					dev_current = 0; +				} else if (flag0 == obsolete_flag && +					   flag1 == active_flag) { +					dev_current = 1; +				} else if (flag0 == flag1) { +					dev_current = 0; +				} else if (flag0 == 0xFF) { +					dev_current = 0; +				} else if (flag1 == 0xFF) { +					dev_current = 1; +				} else { +					dev_current = 0; +				} +				break; +			case FLAG_INCREMENTAL: +				if ((flag0 == 255 && flag1 == 0) || +				    flag1 > flag0) +					dev_current = 1; +				else if ((flag1 == 255 && flag0 == 0) || +					 flag0 > flag1) +					dev_current = 0; +				else /* flags are equal - almost impossible */ +					dev_current = 0; +				break; +			default: +				fprintf (stderr, "Unknown flag scheme %u \n", +					 environment.flag_scheme); +				return -1; +			} +		} + +		/* +		 * If we are reading, we don't need the flag and the CRC any +		 * more, if we are writing, we will re-calculate CRC and update +		 * flags before writing out +		 */ +		if (dev_current) { +			environment.image	= addr1; +			environment.crc		= &redundant->crc; +			environment.flags	= &redundant->flags; +			environment.data	= redundant->data; +			free (addr0); +		} else { +			environment.image	= addr0; +			/* Other pointers are already set */  			free (addr1);  		}  	} -	return (0); +	return 0;  } @@ -709,18 +1044,20 @@ static int parse_config ()  	if (get_config (CONFIG_FILE)) {  		fprintf (stderr,  			"Cannot parse config file: %s\n", strerror (errno)); -		return 1; +		return -1;  	}  #else  	strcpy (DEVNAME (0), DEVICE1_NAME);  	DEVOFFSET (0) = DEVICE1_OFFSET;  	ENVSIZE (0) = ENV1_SIZE;  	DEVESIZE (0) = DEVICE1_ESIZE; +	ENVSECTORS (0) = DEVICE1_ENVSECTORS;  #ifdef HAVE_REDUND  	strcpy (DEVNAME (1), DEVICE2_NAME);  	DEVOFFSET (1) = DEVICE2_OFFSET;  	ENVSIZE (1) = ENV2_SIZE;  	DEVESIZE (1) = DEVICE2_ESIZE; +	ENVSECTORS (1) = DEVICE2_ENVSECTORS;  	HaveRedundEnv = 1;  #endif  #endif @@ -728,14 +1065,14 @@ static int parse_config ()  		fprintf (stderr,  			"Cannot access MTD device %s: %s\n",  			DEVNAME (0), strerror (errno)); -		return 1; +		return -1;  	}  	if (HaveRedundEnv && stat (DEVNAME (1), &st)) {  		fprintf (stderr,  			"Cannot access MTD device %s: %s\n",  			DEVNAME (1), strerror (errno)); -		return 1; +		return -1;  	}  	return 0;  } @@ -748,21 +1085,28 @@ static int get_config (char *fname)  	int rc;  	char dump[128]; -	if ((fp = fopen (fname, "r")) == NULL) { -		return 1; -	} - -	while ((i < 2) && ((rc = fscanf (fp, "%s %lx %lx %lx", -				  DEVNAME (i), -				  &DEVOFFSET (i), -				  &ENVSIZE (i), -				  &DEVESIZE (i)  )) != EOF)) { +	fp = fopen (fname, "r"); +	if (fp == NULL) +		return -1; +	while (i < 2 && fgets (dump, sizeof (dump), fp)) {  		/* Skip incomplete conversions and comment strings */ -		if ((rc < 3) || (*DEVNAME (i) == '#')) { -			fgets (dump, sizeof (dump), fp);	/* Consume till end */ +		if (dump[0] == '#')  			continue; -		} + +		rc = sscanf (dump, "%s %lx %lx %lx %lx", +			     DEVNAME (i), +			     &DEVOFFSET (i), +			     &ENVSIZE (i), +			     &DEVESIZE (i), +			     &ENVSECTORS (i)); + +		if (rc < 4) +			continue; + +		if (rc < 5) +			/* Default - 1 sector */ +			ENVSECTORS (i) = 1;  		i++;  	} @@ -771,7 +1115,7 @@ static int get_config (char *fname)  	HaveRedundEnv = i - 1;  	if (!i) {			/* No valid entries found */  		errno = EINVAL; -		return 1; +		return -1;  	} else  		return 0;  } |