[U-Boot] [PATCH 6/6] Support environment in NAND

Wolfgang Denk wd at denx.de
Sun Aug 31 20:57:38 CEST 2008


Dear Guennadi Liakhovetski,

In message <Pine.LNX.4.64.0808271748520.6718 at axis700.grange> you wrote:
>
> --- a/tools/env/fw_env.c
> +++ b/tools/env/fw_env.c
> @@ -44,6 +44,12 @@
>  #define	CMD_GETENV	"fw_printenv"
>  #define	CMD_SETENV	"fw_setenv"
>  
> +#define min(x, y) ({				\
> +	typeof(x) _min1 = (x);			\
> +	typeof(y) _min2 = (y);			\
> +	(void) (&_min1 == &_min2);		\

What does this do?

> +	_min1 < _min2 ? _min1 : _min2; })


>  typedef struct envdev_s {
>  	char devname[16];		/* Device name */
>  	ulong devoff;			/* Device offset */
> @@ -413,179 +419,290 @@ int fw_setenv (int argc, char *argv[])
>  	return 0;
>  }
>  
> +static int flash_bad_block (int dev, int fd, struct mtd_info_user *mtdinfo,
> +			    loff_t *blockstart, size_t blocklen)
> +{
> +	if (mtdinfo->type == MTD_NANDFLASH) {
> +		int badblock = ioctl (fd, MEMGETBADBLOCK, blockstart);
> +
> +		if (badblock < 0) {
> +			perror ("Cannot read bad block mark");

It would be probably helpful to print the block address.

> +			return badblock;
> +		}
> +
> +		if (badblock) {
> +			fprintf (stderr, "Bad block at 0x%llx, "
> +				 "skipping\n", *blockstart);
> +			*blockstart += blocklen;
> +			return badblock;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * We are called with count == 0 for backing up as much data from the
> + * range as possible
> + */

Backing up?

>  static int flash_read_buf (int dev, int fd, void *buf, size_t count,
> -			   off_t offset)
> +			   off_t offset, size_t range)
>  {
> +	struct mtd_info_user mtdinfo;
> +	size_t blocklen, processed = 0;
> +	size_t readlen = count ? : range;
> +	off_t erase_offset, block_seek;
> +	loff_t blockstart;
>  	int rc;
> +	int backup_mode = !count;

backup_mode ?

I think there should be an explanation what exactly you are trying to
do.

> -	rc = lseek (fd, offset, SEEK_SET);
> -	if (rc == -1) {
> -		fprintf (stderr,
> -			 "seek error on %s: %s\n",
> -			 DEVNAME (dev), strerror (errno));
> +	if (!count)
> +		count = range;
> +
> +	rc = ioctl (fd, MEMGETINFO, &mtdinfo);
> +	if (rc < 0) {
> +		perror ("Cannot get MTD information");
>  		return rc;
>  	}

Did you verify that the code still builds when MTD_OLD is set?

>  
> -	rc = read (fd, buf, count);
> -	if (rc != count) {
> -		fprintf (stderr,
> -			 "Read error on %s: %s\n",
> -			 DEVNAME (dev), strerror (errno));
> -		return -1;
> +	/* Erase sector size is always a power of 2 */
> +	erase_offset = offset & ~(mtdinfo.erasesize - 1);

Please explain this logic.

> +	blockstart = erase_offset;
> +	/* Offset inside a block */
> +	block_seek = offset - erase_offset;
> +
> +	if (mtdinfo.type == MTD_NANDFLASH) {
> +		/*
> +		 * NAND: calculate which blocks we are reading. We have
> +		 * to read one block at a time to skip bad blocks.
> +		 */
> +		blocklen = mtdinfo.erasesize;
> +		/* Limit to one block for the first read */
> +		if (readlen > blocklen - block_seek)
> +			readlen = blocklen - block_seek;
> +	} else {
> +		blocklen = 0;
>  	}
>  
> -	return rc;
> +	/* This only runs once for NOR flash */
> +	while (processed < count) {
> +		rc = flash_bad_block (dev, fd, &mtdinfo, &blockstart, blocklen);

But - NOR flash does not have bad block, so all of this is not needed
at all?

> +		if (rc < 0)
> +			return -1;
> +		else if (blockstart + block_seek + readlen > offset + range) {

I do not understand what you are doing here. Comment?

> +			/* End of range is reached */
> +			if (backup_mode) {
> +				return processed;
> +			} else {
> +				fprintf (stderr,
> +					 "Too few good blocks within range\n");
> +				return -1;
> +			}
> +		} else if (rc)
> +			continue;
> +
> +		/*
> +		 * 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);

I don't see that you remember which blocks were bad. Does that mean
that you will attemopt to write the environment to known bad blocks?
Sonds not like a good idea to me.

> +		rc = read (fd, buf + processed, readlen);
> +		if (rc != readlen) {
> +			fprintf (stderr,
> +				 "Read error on %s: %s\n",
> +				 DEVNAME (dev), strerror (errno));
> +			return -1;
> +		}
> +		processed += readlen;
> +		readlen = min(blocklen, count - processed);
> +		block_seek = 0;
> +		blockstart += blocklen;
> +	}
> +
> +	return processed;
>  }
>  
> -static int flash_write (void)
> +static int flash_write_buf (int dev, int fd, void *buf, size_t count,
> +			    off_t offset)
>  {
> -	int fd_current, fd_target, rc, dev_target;
> -	erase_info_t erase_current = {}, erase_target;
>  	char *data = NULL;
> -	off_t erase_offset;
> -	struct mtd_info_user mtdinfo_target;
> +	erase_info_t erase;
> +	struct mtd_info_user mtdinfo;
> +	size_t blocklen, erase_len, processed = 0;
> +	size_t writelen, write_total = DEVESIZE (dev);
> +	off_t erase_offset, block_seek;
> +	loff_t blockstart;
> +	int rc;
>  
> -	/* dev_current: fd_current, erase_current */
> -	if ((fd_current = open (DEVNAME (dev_current), O_RDWR)) < 0) {
> -		fprintf (stderr,
> -			 "Can't open %s: %s\n",
> -			 DEVNAME (dev_current), strerror (errno));
> +	rc = ioctl (fd, MEMGETINFO, &mtdinfo);
> +	if (rc < 0) {
> +		perror ("Cannot get MTD information");
>  		return -1;
>  	}
>  
> -	if (HaveRedundEnv) {
> -		/* switch to next partition for writing */
> -		dev_target = !dev_current;
> -		/* dev_target: fd_target, erase_target */
> -		if ((fd_target = open (DEVNAME (dev_target), O_RDWR)) < 0) {
> -			fprintf (stderr,
> -				 "Can't open %s: %s\n",
> -				 DEVNAME (dev_target),
> -				 strerror (errno));
> -			return -1;
> -		}
> -	} else {
> -		dev_target = dev_current;
> -		fd_target = fd_current;
> -	}
> +	/* Erase sector size is always a power of 2 */
> +	erase_offset = offset & ~(mtdinfo.erasesize - 1);
> +	/* Maximum area we may use */
> +	erase_len = (offset - erase_offset + DEVESIZE (dev) +
> +		     mtdinfo.erasesize - 1) & ~(mtdinfo.erasesize - 1);
> +
> +	blockstart = erase_offset;
> +	/* Offset inside a block */
> +	block_seek = offset - erase_offset;
>  
>  	/*
>  	 * Support environment anywhere within erase sectors: read out the
>  	 * complete area to be erased, replace the environment image, write
>  	 * the whole block back again.
>  	 */
> -	if (DEVESIZE (dev_target) > CFG_ENV_SIZE) {
> -		data = malloc (DEVESIZE (dev_target));
> +	if (erase_len > DEVESIZE (dev)) {
> +		data = malloc (erase_len);
>  		if (!data) {
>  			fprintf (stderr,
> -				 "Cannot malloc %lu bytes: %s\n",
> -				 DEVESIZE (dev_target),
> -				 strerror (errno));
> +				 "Cannot malloc %u bytes: %s\n",
> +				 erase_len, strerror (errno));
>  			return -1;
>  		}
>  
> -		rc = ioctl (fd_target, MEMGETINFO, &mtdinfo_target);
> -		if (rc < 0) {
> -			perror ("Cannot get MTD information");
> +		/*
> +		 * This is different from a normal read. We have to read as much
> +		 * as we can from a certain area, and it should be at least X
> +		 * bytes, instead of having to read a fixed number of bytes as
> +		 * usual. This also tells us how much data "fits" in the good
> +		 * blocks in the area.
> +		 */
> +		write_total = flash_read_buf (dev, fd, data, 0,
> +					      erase_offset, erase_len);
> +		if (write_total < block_seek + CFG_ENV_SIZE)

Ummm...this is flash_write_buf(), and we start reading data?

Please explain your code.

Best regards,

Wolfgang Denk

-- 
DENX Software Engineering GmbH,     MD: Wolfgang Denk & Detlev Zundel
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd at denx.de
It would seem that evil retreats when forcibly confronted
	-- Yarnek of Excalbia, "The Savage Curtain", stardate 5906.5


More information about the U-Boot mailing list