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

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
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; +}

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

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

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
participants (2)
-
Marek Vasut
-
Mike Dunn