Patchwork [U-Boot,v3,8/8] palmtreo680: add utility that writes u-boot to flash

login
register
mail settings
Submitter Mike Dunn
Date April 12, 2013, 6:59 p.m.
Message ID <1365793160-18247-9-git-send-email-mikedunn@newsguy.com>
Download mbox | patch
Permalink /patch/236179/
State Superseded
Delegated to: Marek Vasut
Headers show

Comments

Mike Dunn - April 12, 2013, 6:59 p.m.
This adds a userspace linux utility that writes the u-boot image to an mtd
partition on the docg4 nand flash.

A special utility is required to do this because u-boot is partially loaded by
an initial program loader (IPL) that is permanently programmed to the boot
region of the flash.  This IPL expects the image to be written in a unique
format. The characteristics of this format can be summarized as follows:
  - Flash blocks to be loaded must have a magic number in the oob bytes of the
    first page of the block.
  - Each page must be written redundantly in the subsequent page.
  - The integrated flash controller's "reliable mode" is used, requiring that
    alternate 2k regions (4 pages) are skipped when writing.
For these reasons, a u-boot image can not be written using nandwrite from
mtd-utils.

Signed-off-by: Mike Dunn <mikedunn@newsguy.com>
---
Changelog:
  v3: new patch; split off from patch 7 in v2 of patchset

 tools/palmtreo680/flash_u-boot.c |  167 ++++++++++++++++++++++++++++++++++++++
 1 files changed, 167 insertions(+), 0 deletions(-)
 create mode 100644 tools/palmtreo680/flash_u-boot.c
Marek Vasut - April 14, 2013, 5:38 p.m.
Dear Mike Dunn,

> This adds a userspace linux utility that writes the u-boot image to an mtd
> partition on the docg4 nand flash.
> 
> A special utility is required to do this because u-boot is partially loaded
> by an initial program loader (IPL) that is permanently programmed to the
> boot region of the flash.  This IPL expects the image to be written in a
> unique format. The characteristics of this format can be summarized as
> follows: - Flash blocks to be loaded must have a magic number in the oob
> bytes of the first page of the block.
>   - Each page must be written redundantly in the subsequent page.
>   - The integrated flash controller's "reliable mode" is used, requiring
> that alternate 2k regions (4 pages) are skipped when writing.
> For these reasons, a u-boot image can not be written using nandwrite from
> mtd-utils.
> 
> Signed-off-by: Mike Dunn <mikedunn@newsguy.com>
> ---
> Changelog:
>   v3: new patch; split off from patch 7 in v2 of patchset
> 
>  tools/palmtreo680/flash_u-boot.c |  167
> ++++++++++++++++++++++++++++++++++++++ 1 files changed, 167 insertions(+),
> 0 deletions(-)
>  create mode 100644 tools/palmtreo680/flash_u-boot.c
> 
> diff --git a/tools/palmtreo680/flash_u-boot.c
> b/tools/palmtreo680/flash_u-boot.c new file mode 100644
> index 0000000..97b336f
> --- /dev/null
> +++ b/tools/palmtreo680/flash_u-boot.c
> @@ -0,0 +1,167 @@
> +/*
> + * Copyright (C) 2013 Mike Dunn <mikedunn@newsguy.com>
> + *
> + * This file is released under the terms of GPL v2 and any later version.
> + * See the file COPYING in the root directory of the source tree for
> details. + *
> + *
> + * This is a userspace Linux utility that, when run on the Treo 680, will
> + * program u-boot to flash.  The docg4 driver *must* be loaded with the
> + * reliable_mode and ignore_badblocks parameters enabled:
> + *
> + *        modprobe docg4 ignore_badblocks=1 reliable_mode=1
> + *
> + * This utility writes the concatenated spl + u-boot image to the start of
> the + * mtd device in the format expected by the IPL/SPL.  The image file
> and mtd + * device node are passed to the utility as arguments.  The
> blocks must have + * been erased beforehand.
> + *
> + * When you compile this, note that it links to libmtd from mtd-utils, so
> ensure + * that your include and lib paths include this.
> + */
> +
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <sys/stat.h>
> +#include <fcntl.h>
> +#include <string.h>
> +#include <sys/types.h>
> +#include <unistd.h>
> +#include <errno.h>
> +#include <mtd/mtd-user.h>
> +#include "libmtd.h"
> +
> +#define RELIABLE_BLOCKSIZE  0x10000 /* block capacity in reliable mode */
> +#define STANDARD_BLOCKSIZE  0x40000 /* block capacity in normal mode */
> +#define PAGESIZE 512
> +#define PAGES_PER_BLOCK 512
> +#define OOBSIZE 7		/* available to user (16 total) */
> +
> +uint8_t ff_oob[OOBSIZE] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
> +
> +/* this is the magic number the IPL looks for (ASCII "BIPO") */
> +uint8_t page0_oob[OOBSIZE] = {'B', 'I', 'P', 'O', 0xff, 0xff, 0xff};
> +
> +int main(int argc, char * const argv[])
> +{
> +	int devfd, datafd, num_blocks, block;
> +	off_t file_size;
> +	libmtd_t mtd_desc;
> +	struct mtd_dev_info devinfo;
> +	uint8_t *blockbuf;
> +	char response[8];
> +
> +	if (argc != 3) {
> +		printf("usage: %s <image file> <mtd dev node>\n", argv[0]);
> +		exit(-1);

Use proper errno and "return" as you're returning from main() anyway.

> +	}
> +
> +	mtd_desc = libmtd_open();
> +	if (mtd_desc == NULL) {
> +		fprintf(stderr, "can't initialize libmtd\n");
> +		exit(-1);
> +	}
> +
> +	/* open the spl image file and mtd device */
> +	datafd = open(argv[1], O_RDONLY);
> +	if (datafd == -1) {
> +		perror(argv[1]);
> +		exit(-1);
> +	}
> +	devfd = open(argv[2], O_WRONLY);
> +	if (devfd == -1) {
> +		perror(argv[2]);
> +		exit(-1);
> +	}
> +	if (mtd_get_dev_info(mtd_desc, argv[2], &devinfo) < 0) {
> +		fprintf(stderr, "mtd_get_dev_info failed\n");
> +		exit(-1);
> +	}
> +
> +	/* determine the number of blocks needed by the image */
> +	file_size = lseek(datafd, 0, SEEK_END);
> +	if (file_size == (off_t)-1) {
> +		perror("lseek");
> +		exit(-1);
> +	}
> +	num_blocks = (file_size + RELIABLE_BLOCKSIZE - 1) / RELIABLE_BLOCKSIZE;
> +	file_size = lseek(datafd, 0, SEEK_SET);
> +	if (file_size == (off_t)-1) {
> +		perror("lseek");
> +		exit(-1);
> +	}
> +	printf("The mtd partition contains %d blocks\n", devinfo.eb_cnt);
> +	printf("U-boot will occupy %d blocks\n", num_blocks);
> +	if (num_blocks > devinfo.eb_cnt) {
> +		fprintf(stderr, "Insufficient blocks on partition\n");
> +		exit(-1);
> +	}
> +
> +	printf("IMPORTANT: These blocks must be in an erased state!\n");
> +	printf("Do you want to proceed?\n");
> +	scanf("%s", response);
> +	if ((response[0] != 'y') && (response[0] != 'Y')) {
> +		printf("Exiting\n");
> +		close(devfd);
> +		close(datafd);
> +		exit(-1);
> +	}
> +
> +	blockbuf = malloc(RELIABLE_BLOCKSIZE);

Do you not want to use some calloc() here to make sure the "blockbuf" is zeroed?

> +	if (blockbuf == NULL) {
> +		perror("malloc");
> +		exit(-1);
> +	}
> +
> +	for (block = 0; block < num_blocks; block++) {
> +		int ofs, page;
> +		ssize_t read_ret;
> +		uint8_t *pagebuf = blockbuf, *buf = blockbuf;
> +		uint8_t *oobbuf = page0_oob; /* magic num in oob of 1st page */
> +		size_t len = RELIABLE_BLOCKSIZE;
> +		int ret;
> +
> +		/* read data for one block from file */
> +		while (len != 0 && (read_ret = read(datafd, buf, len)) != 0) {

Uh, this really might be a candidate for IOCCC, split this please ...

> +			if (read_ret == -1) {
> +				if (errno == EINTR)
> +					continue;
> +				perror("read");
> +				exit(-1);
> +			}
> +			len -= read_ret;
> +			buf += read_ret;
> +		}
> +
> +		printf("Block %d: writing\r", block + 1);
> +		fflush(stdout);
> +
> +		for (page = 0, ofs = 0;
> +		     page < PAGES_PER_BLOCK;
> +		     page++, ofs += PAGESIZE) {
> +			if (page & 0x04)  /* Odd-numbered 2k page */
> +				continue; /* skipped in reliable mode */
> +
> +			ret = mtd_write(mtd_desc, &devinfo, devfd, block, ofs,
> +					pagebuf, PAGESIZE, oobbuf, OOBSIZE,
> +					MTD_OPS_PLACE_OOB);
> +			if (ret) {
> +				fprintf(stderr,
> +					"\nmtd_write returned %d on block %d, 
ofs %x\n",
> +					ret, block + 1, ofs);
> +				exit(-1);
> +			}
> +			oobbuf = ff_oob;  /* oob for subsequent pages */
> +
> +			if (page & 0x01)  /* odd-numbered subpage */
> +				pagebuf += PAGESIZE;
> +		}
> +	}
> +
> +	printf("\nDone\n");
> +
> +	close(devfd);
> +	close(datafd);
> +	free(blockbuf);
> +	return 0;
> +}
Mike Dunn - April 16, 2013, 5:50 p.m.
Thanks again Marek.  A question below...


On 04/14/2013 10:38 AM, Marek Vasut wrote:


[...]


>> +
>> +	if (argc != 3) {
>> +		printf("usage: %s <image file> <mtd dev node>\n", argv[0]);
>> +		exit(-1);
> 
> Use proper errno and "return" as you're returning from main() anyway.


Agreed regarding 'return'.  But should I be concerned with setting or preserving
errno before all 'return -1' lines?  Is it normal practice for a common utility
to set errno?  errno will have to be saved in many places, since perror() itself
can change it.  This will add many more lines of code.


[...]


>> +
>> +	blockbuf = malloc(RELIABLE_BLOCKSIZE);
> 
> Do you not want to use some calloc() here to make sure the "blockbuf" is zeroed?


Not necessary here; the buffer is always filled or the utility exits with error.
 But will change to calloc() anyway.

[...]


>> +
>> +		/* read data for one block from file */
>> +		while (len != 0 && (read_ret = read(datafd, buf, len)) != 0) {
> 
> Uh, this really might be a candidate for IOCCC, split this please ...


Well, OK, but... I normally don't embed calls in tests, but I do it here because
the read is performed at the start of each loop iteration, and I thought this
made it clearer and more concise.  Basically it means "loop while there's still
more data to write, and read() does not return EOF".

Actually, read() should never return EOF, because earlier I check the file
length, so if I'm going to do the sanity check anyway, maybe it should be separate.

Thanks,
Mike
Marek Vasut - April 16, 2013, 6:06 p.m.
Dear Mike Dunn,

> Thanks again Marek.  A question below...
> 
> 
> On 04/14/2013 10:38 AM, Marek Vasut wrote:
> 
> 
> [...]
> 
> >> +
> >> +	if (argc != 3) {
> >> +		printf("usage: %s <image file> <mtd dev node>\n", argv[0]);
> >> +		exit(-1);
> > 
> > Use proper errno and "return" as you're returning from main() anyway.
> 
> Agreed regarding 'return'.  But should I be concerned with setting or
> preserving errno before all 'return -1' lines?  Is it normal practice for
> a common utility to set errno?  errno will have to be saved in many
> places, since perror() itself can change it.  This will add many more
> lines of code.

Ooops! errno.h, sorry for the confusion :-(

> [...]
> 
> >> +
> >> +	blockbuf = malloc(RELIABLE_BLOCKSIZE);
> > 
> > Do you not want to use some calloc() here to make sure the "blockbuf" is
> > zeroed?
> 
> Not necessary here; the buffer is always filled or the utility exits with
> error. But will change to calloc() anyway.

If you're sure it's filled, then it's no problem.

> [...]
> 
> >> +
> >> +		/* read data for one block from file */
> >> +		while (len != 0 && (read_ret = read(datafd, buf, len)) != 0) {
> > 
> > Uh, this really might be a candidate for IOCCC, split this please ...
> 
> Well, OK, but... I normally don't embed calls in tests, but I do it here
> because the read is performed at the start of each loop iteration, and I
> thought this made it clearer and more concise.  Basically it means "loop
> while there's still more data to write, and read() does not return EOF".
> 
> Actually, read() should never return EOF, because earlier I check the file
> length, so if I'm going to do the sanity check anyway, maybe it should be
> separate.

I'd say you can loop and break; out if needed, no ?

while (cond.) {
 if (x)
  break;
 if (y)
  break;

 do_useful_stuff here;
}

Best regards,
Marek Vasut

Patch

diff --git a/tools/palmtreo680/flash_u-boot.c b/tools/palmtreo680/flash_u-boot.c
new file mode 100644
index 0000000..97b336f
--- /dev/null
+++ b/tools/palmtreo680/flash_u-boot.c
@@ -0,0 +1,167 @@ 
+/*
+ * Copyright (C) 2013 Mike Dunn <mikedunn@newsguy.com>
+ *
+ * This file is released under the terms of GPL v2 and any later version.
+ * See the file COPYING in the root directory of the source tree for details.
+ *
+ *
+ * This is a userspace Linux utility that, when run on the Treo 680, will
+ * program u-boot to flash.  The docg4 driver *must* be loaded with the
+ * reliable_mode and ignore_badblocks parameters enabled:
+ *
+ *        modprobe docg4 ignore_badblocks=1 reliable_mode=1
+ *
+ * This utility writes the concatenated spl + u-boot image to the start of the
+ * mtd device in the format expected by the IPL/SPL.  The image file and mtd
+ * device node are passed to the utility as arguments.  The blocks must have
+ * been erased beforehand.
+ *
+ * When you compile this, note that it links to libmtd from mtd-utils, so ensure
+ * that your include and lib paths include this.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+#include <mtd/mtd-user.h>
+#include "libmtd.h"
+
+#define RELIABLE_BLOCKSIZE  0x10000 /* block capacity in reliable mode */
+#define STANDARD_BLOCKSIZE  0x40000 /* block capacity in normal mode */
+#define PAGESIZE 512
+#define PAGES_PER_BLOCK 512
+#define OOBSIZE 7		/* available to user (16 total) */
+
+uint8_t ff_oob[OOBSIZE] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+/* this is the magic number the IPL looks for (ASCII "BIPO") */
+uint8_t page0_oob[OOBSIZE] = {'B', 'I', 'P', 'O', 0xff, 0xff, 0xff};
+
+int main(int argc, char * const argv[])
+{
+	int devfd, datafd, num_blocks, block;
+	off_t file_size;
+	libmtd_t mtd_desc;
+	struct mtd_dev_info devinfo;
+	uint8_t *blockbuf;
+	char response[8];
+
+	if (argc != 3) {
+		printf("usage: %s <image file> <mtd dev node>\n", argv[0]);
+		exit(-1);
+	}
+
+	mtd_desc = libmtd_open();
+	if (mtd_desc == NULL) {
+		fprintf(stderr, "can't initialize libmtd\n");
+		exit(-1);
+	}
+
+	/* open the spl image file and mtd device */
+	datafd = open(argv[1], O_RDONLY);
+	if (datafd == -1) {
+		perror(argv[1]);
+		exit(-1);
+	}
+	devfd = open(argv[2], O_WRONLY);
+	if (devfd == -1) {
+		perror(argv[2]);
+		exit(-1);
+	}
+	if (mtd_get_dev_info(mtd_desc, argv[2], &devinfo) < 0) {
+		fprintf(stderr, "mtd_get_dev_info failed\n");
+		exit(-1);
+	}
+
+	/* determine the number of blocks needed by the image */
+	file_size = lseek(datafd, 0, SEEK_END);
+	if (file_size == (off_t)-1) {
+		perror("lseek");
+		exit(-1);
+	}
+	num_blocks = (file_size + RELIABLE_BLOCKSIZE - 1) / RELIABLE_BLOCKSIZE;
+	file_size = lseek(datafd, 0, SEEK_SET);
+	if (file_size == (off_t)-1) {
+		perror("lseek");
+		exit(-1);
+	}
+	printf("The mtd partition contains %d blocks\n", devinfo.eb_cnt);
+	printf("U-boot will occupy %d blocks\n", num_blocks);
+	if (num_blocks > devinfo.eb_cnt) {
+		fprintf(stderr, "Insufficient blocks on partition\n");
+		exit(-1);
+	}
+
+	printf("IMPORTANT: These blocks must be in an erased state!\n");
+	printf("Do you want to proceed?\n");
+	scanf("%s", response);
+	if ((response[0] != 'y') && (response[0] != 'Y')) {
+		printf("Exiting\n");
+		close(devfd);
+		close(datafd);
+		exit(-1);
+	}
+
+	blockbuf = malloc(RELIABLE_BLOCKSIZE);
+	if (blockbuf == NULL) {
+		perror("malloc");
+		exit(-1);
+	}
+
+	for (block = 0; block < num_blocks; block++) {
+		int ofs, page;
+		ssize_t read_ret;
+		uint8_t *pagebuf = blockbuf, *buf = blockbuf;
+		uint8_t *oobbuf = page0_oob; /* magic num in oob of 1st page */
+		size_t len = RELIABLE_BLOCKSIZE;
+		int ret;
+
+		/* read data for one block from file */
+		while (len != 0 && (read_ret = read(datafd, buf, len)) != 0) {
+			if (read_ret == -1) {
+				if (errno == EINTR)
+					continue;
+				perror("read");
+				exit(-1);
+			}
+			len -= read_ret;
+			buf += read_ret;
+		}
+
+		printf("Block %d: writing\r", block + 1);
+		fflush(stdout);
+
+		for (page = 0, ofs = 0;
+		     page < PAGES_PER_BLOCK;
+		     page++, ofs += PAGESIZE) {
+			if (page & 0x04)  /* Odd-numbered 2k page */
+				continue; /* skipped in reliable mode */
+
+			ret = mtd_write(mtd_desc, &devinfo, devfd, block, ofs,
+					pagebuf, PAGESIZE, oobbuf, OOBSIZE,
+					MTD_OPS_PLACE_OOB);
+			if (ret) {
+				fprintf(stderr,
+					"\nmtd_write returned %d on block %d, ofs %x\n",
+					ret, block + 1, ofs);
+				exit(-1);
+			}
+			oobbuf = ff_oob;  /* oob for subsequent pages */
+
+			if (page & 0x01)  /* odd-numbered subpage */
+				pagebuf += PAGESIZE;
+		}
+	}
+
+	printf("\nDone\n");
+
+	close(devfd);
+	close(datafd);
+	free(blockbuf);
+	return 0;
+}