summaryrefslogtreecommitdiff
path: root/tools/palmtreo680/flash_u-boot.c
blob: 3d8296fc6b0ca81a71d9cd92544ffda2ea9e3ee7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
/*
 * 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 -EINVAL;
	}

	mtd_desc = libmtd_open();
	if (mtd_desc == NULL) {
		int errsv = errno;
		fprintf(stderr, "can't initialize libmtd\n");
		return -errsv;
	}

	/* open the spl image file and mtd device */
	datafd = open(argv[1], O_RDONLY);
	if (datafd == -1) {
		int errsv = errno;
		perror(argv[1]);
		return -errsv;
	}
	devfd = open(argv[2], O_WRONLY);
	if (devfd == -1) {
		int errsv = errno;
		perror(argv[2]);
		return -errsv;
	}
	if (mtd_get_dev_info(mtd_desc, argv[2], &devinfo) < 0) {
		int errsv = errno;
		perror(argv[2]);
		return -errsv;
	}

	/* determine the number of blocks needed by the image */
	file_size = lseek(datafd, 0, SEEK_END);
	if (file_size == (off_t)-1) {
		int errsv = errno;
		perror("lseek");
		return -errsv;
	}
	num_blocks = (file_size + RELIABLE_BLOCKSIZE - 1) / RELIABLE_BLOCKSIZE;
	file_size = lseek(datafd, 0, SEEK_SET);
	if (file_size == (off_t)-1) {
		int errsv = errno;
		perror("lseek");
		return -errsv;
	}
	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 -EINVAL;
	}

	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) {
		int errsv = errno;
		perror("calloc");
		return -errsv;
	}

	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) {
				int errsv = errno;
				if (errno == EINTR)
					continue;
				perror("read");
				return -errsv;
			} 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 -EIO;
			}
			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;
}