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

login
register
mail settings
Submitter Mike Dunn
Date April 16, 2013, 8:15 p.m.
Message ID <1366143307-7055-1-git-send-email-mikedunn@newsguy.com>
Download mbox | patch
Permalink /patch/237075/
State Superseded
Delegated to: Albert ARIBAUD
Headers show

Comments

Mike Dunn - April 16, 2013, 8:15 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:
  v4:
    - use return instead of exit
    - use calloc() instead of malloc()
    - remove call to read() from within while loop test 
  v3: new patch; split off from patch 7 in v2 of patchset

I was wrong, Marek... the read() will return EOF while writing the last block,
unless the size of the image is such that it fills the last block.  Otherwise,
the last block is only partially written, and stops at EOF.  This is fine.

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

[...]

> +	if (argc != 3) {
> +		printf("usage: %s <image file> <mtd dev node>\n", argv[0]);

btw is this /dev/mtdX or /dev/mtdblockX ?

> +		return -1;

errno.h didn't work? Like ... return -EINVAL or such ?

[...]

> +	blockbuf = calloc(RELIABLE_BLOCKSIZE, 1);

calloc() semantics are misunderstood by so many :-(

[...]

Best regards,
Marek Vasut
Mike Dunn - April 18, 2013, 3:47 p.m.
On 04/18/2013 06:54 AM, Marek Vasut wrote:
> Dear Mike Dunn,
> 
> [...]
> 
>> +	if (argc != 3) {
>> +		printf("usage: %s <image file> <mtd dev node>\n", argv[0]);
> 
> btw is this /dev/mtdX or /dev/mtdblockX ?


/dev/mtdx  You must have defined a partition that starts at the offset at which
you are placing the u-boot image, and of course is big enough to hold the image.


> 
>> +		return -1;
> 
> errno.h didn't work? Like ... return -EINVAL or such ?


'return -EINVAL' works, sure.  Are you suggesting that the utility should return
an intelligent error code at all failure exit points?  Like if the call to
calloc() fails, it should return -ENOMEM?  I could do that if you prefer.  I
didn't worry about it because I always print the reason for the failure to
stderr, usually by way of perror().  Sorry, I guess I didn't understand your
initial suggestion.


> 
> [...]
> 
>> +	blockbuf = calloc(RELIABLE_BLOCKSIZE, 1);
> 
> calloc() semantics are misunderstood by so many :-(


calloc(RELIABLE_BLOCKSIZE, sizeof(char)) ?


Thanks,
Mike
Marek Vasut - April 18, 2013, 4:13 p.m.
Dear Mike Dunn,

> On 04/18/2013 06:54 AM, Marek Vasut wrote:
> > Dear Mike Dunn,
> > 
> > [...]
> > 
> >> +	if (argc != 3) {
> >> +		printf("usage: %s <image file> <mtd dev node>\n", argv[0]);
> > 
> > btw is this /dev/mtdX or /dev/mtdblockX ?
> 
> /dev/mtdx  You must have defined a partition that starts at the offset at
> which you are placing the u-boot image, and of course is big enough to
> hold the image.
> 
> >> +		return -1;
> > 
> > errno.h didn't work? Like ... return -EINVAL or such ?
> 
> 'return -EINVAL' works, sure.  Are you suggesting that the utility should
> return an intelligent error code at all failure exit points?  Like if the
> call to calloc() fails, it should return -ENOMEM?  I could do that if you
> prefer.  I didn't worry about it because I always print the reason for the
> failure to stderr, usually by way of perror().  Sorry, I guess I didn't
> understand your initial suggestion.

I think it's more "correct" to return proper error code, maybe try getting 
opinion from the others.

> > [...]
> > 
> >> +	blockbuf = calloc(RELIABLE_BLOCKSIZE, 1);
> > 
> > calloc() semantics are misunderstood by so many :-(
> 
> calloc(RELIABLE_BLOCKSIZE, sizeof(char)) ?

from malloc(3):

       void *calloc(size_t nmemb, size_t size);

[...]

The  calloc() function allocates memory for an array of nmemb elements of size
bytes each and returns a pointer to the allocated memory.  The memory is set to
zero.  If nmemb or size is 0, then calloc() returns either NULL, or  a  unique
pointer value that can later be successfully passed to free().

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..8f8dadf
--- /dev/null
+++ b/tools/palmtreo680/flash_u-boot.c
@@ -0,0 +1,168 @@ 
+/*
+ * 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]);
+		return -1;
+	}
+
+	mtd_desc = libmtd_open();
+	if (mtd_desc == NULL) {
+		fprintf(stderr, "can't initialize libmtd\n");
+		return -1;
+	}
+
+	/* open the spl image file and mtd device */
+	datafd = open(argv[1], O_RDONLY);
+	if (datafd == -1) {
+		perror(argv[1]);
+		return -1;
+	}
+	devfd = open(argv[2], O_WRONLY);
+	if (devfd == -1) {
+		perror(argv[2]);
+		return -1;
+	}
+	if (mtd_get_dev_info(mtd_desc, argv[2], &devinfo) < 0) {
+		fprintf(stderr, "mtd_get_dev_info failed\n");
+		return -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");
+		return -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");
+		return -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");
+		return -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);
+		return 0;
+	}
+
+	blockbuf = calloc(RELIABLE_BLOCKSIZE, 1);
+	if (blockbuf == NULL) {
+		perror("calloc");
+		return -1;
+	}
+
+	for (block = 0; block < num_blocks; block++) {
+		int ofs, page;
+		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) {
+			ssize_t read_ret = read(datafd, buf, len);
+			if (read_ret == -1) {
+				if (errno == EINTR)
+					continue;
+				perror("read");
+				return -1;
+			} else if (read_ret == 0)
+				break; /* EOF */
+			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);
+				return -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;
+}