xref: /openbmc/u-boot/tools/sunxi-spl-image-builder.c (revision 4f66e09bb9fbc47b73f67c3cc08ee2663e8fcdb1)
1594b4cc7SMaxime Ripard /*
2594b4cc7SMaxime Ripard  * Allwinner NAND randomizer and image builder implementation:
3594b4cc7SMaxime Ripard  *
4594b4cc7SMaxime Ripard  * Copyright © 2016 NextThing Co.
5594b4cc7SMaxime Ripard  * Copyright © 2016 Free Electrons
6594b4cc7SMaxime Ripard  *
7594b4cc7SMaxime Ripard  * Author: Boris Brezillon <boris.brezillon@free-electrons.com>
8594b4cc7SMaxime Ripard  *
9594b4cc7SMaxime Ripard  */
10594b4cc7SMaxime Ripard 
11594b4cc7SMaxime Ripard #include <linux/bch.h>
12594b4cc7SMaxime Ripard 
13594b4cc7SMaxime Ripard #include <getopt.h>
14594b4cc7SMaxime Ripard #include <version.h>
15594b4cc7SMaxime Ripard 
16594b4cc7SMaxime Ripard #define BCH_PRIMITIVE_POLY	0x5803
17594b4cc7SMaxime Ripard 
18594b4cc7SMaxime Ripard #define ARRAY_SIZE(arr)		(sizeof(arr) / sizeof((arr)[0]))
19594b4cc7SMaxime Ripard #define DIV_ROUND_UP(n,d)	(((n) + (d) - 1) / (d))
20594b4cc7SMaxime Ripard 
21594b4cc7SMaxime Ripard struct image_info {
22594b4cc7SMaxime Ripard 	int ecc_strength;
23594b4cc7SMaxime Ripard 	int ecc_step_size;
24594b4cc7SMaxime Ripard 	int page_size;
25594b4cc7SMaxime Ripard 	int oob_size;
26594b4cc7SMaxime Ripard 	int usable_page_size;
27594b4cc7SMaxime Ripard 	int eraseblock_size;
28594b4cc7SMaxime Ripard 	int scramble;
29594b4cc7SMaxime Ripard 	int boot0;
30594b4cc7SMaxime Ripard 	off_t offset;
31594b4cc7SMaxime Ripard 	const char *source;
32594b4cc7SMaxime Ripard 	const char *dest;
33594b4cc7SMaxime Ripard };
34594b4cc7SMaxime Ripard 
swap_bits(uint8_t * buf,int len)35594b4cc7SMaxime Ripard static void swap_bits(uint8_t *buf, int len)
36594b4cc7SMaxime Ripard {
37594b4cc7SMaxime Ripard 	int i, j;
38594b4cc7SMaxime Ripard 
39594b4cc7SMaxime Ripard 	for (j = 0; j < len; j++) {
40594b4cc7SMaxime Ripard 		uint8_t byte = buf[j];
41594b4cc7SMaxime Ripard 
42594b4cc7SMaxime Ripard 		buf[j] = 0;
43594b4cc7SMaxime Ripard 		for (i = 0; i < 8; i++) {
44594b4cc7SMaxime Ripard 			if (byte & (1 << i))
45594b4cc7SMaxime Ripard 				buf[j] |= (1 << (7 - i));
46594b4cc7SMaxime Ripard 		}
47594b4cc7SMaxime Ripard 	}
48594b4cc7SMaxime Ripard }
49594b4cc7SMaxime Ripard 
lfsr_step(uint16_t state,int count)50594b4cc7SMaxime Ripard static uint16_t lfsr_step(uint16_t state, int count)
51594b4cc7SMaxime Ripard {
52594b4cc7SMaxime Ripard 	state &= 0x7fff;
53594b4cc7SMaxime Ripard 	while (count--)
54594b4cc7SMaxime Ripard 		state = ((state >> 1) |
55594b4cc7SMaxime Ripard 			 ((((state >> 0) ^ (state >> 1)) & 1) << 14)) & 0x7fff;
56594b4cc7SMaxime Ripard 
57594b4cc7SMaxime Ripard 	return state;
58594b4cc7SMaxime Ripard }
59594b4cc7SMaxime Ripard 
60594b4cc7SMaxime Ripard static uint16_t default_scrambler_seeds[] = {
61594b4cc7SMaxime Ripard 	0x2b75, 0x0bd0, 0x5ca3, 0x62d1, 0x1c93, 0x07e9, 0x2162, 0x3a72,
62594b4cc7SMaxime Ripard 	0x0d67, 0x67f9, 0x1be7, 0x077d, 0x032f, 0x0dac, 0x2716, 0x2436,
63594b4cc7SMaxime Ripard 	0x7922, 0x1510, 0x3860, 0x5287, 0x480f, 0x4252, 0x1789, 0x5a2d,
64594b4cc7SMaxime Ripard 	0x2a49, 0x5e10, 0x437f, 0x4b4e, 0x2f45, 0x216e, 0x5cb7, 0x7130,
65594b4cc7SMaxime Ripard 	0x2a3f, 0x60e4, 0x4dc9, 0x0ef0, 0x0f52, 0x1bb9, 0x6211, 0x7a56,
66594b4cc7SMaxime Ripard 	0x226d, 0x4ea7, 0x6f36, 0x3692, 0x38bf, 0x0c62, 0x05eb, 0x4c55,
67594b4cc7SMaxime Ripard 	0x60f4, 0x728c, 0x3b6f, 0x2037, 0x7f69, 0x0936, 0x651a, 0x4ceb,
68594b4cc7SMaxime Ripard 	0x6218, 0x79f3, 0x383f, 0x18d9, 0x4f05, 0x5c82, 0x2912, 0x6f17,
69594b4cc7SMaxime Ripard 	0x6856, 0x5938, 0x1007, 0x61ab, 0x3e7f, 0x57c2, 0x542f, 0x4f62,
70594b4cc7SMaxime Ripard 	0x7454, 0x2eac, 0x7739, 0x42d4, 0x2f90, 0x435a, 0x2e52, 0x2064,
71594b4cc7SMaxime Ripard 	0x637c, 0x66ad, 0x2c90, 0x0bad, 0x759c, 0x0029, 0x0986, 0x7126,
72594b4cc7SMaxime Ripard 	0x1ca7, 0x1605, 0x386a, 0x27f5, 0x1380, 0x6d75, 0x24c3, 0x0f8e,
73594b4cc7SMaxime Ripard 	0x2b7a, 0x1418, 0x1fd1, 0x7dc1, 0x2d8e, 0x43af, 0x2267, 0x7da3,
74594b4cc7SMaxime Ripard 	0x4e3d, 0x1338, 0x50db, 0x454d, 0x764d, 0x40a3, 0x42e6, 0x262b,
75594b4cc7SMaxime Ripard 	0x2d2e, 0x1aea, 0x2e17, 0x173d, 0x3a6e, 0x71bf, 0x25f9, 0x0a5d,
76594b4cc7SMaxime Ripard 	0x7c57, 0x0fbe, 0x46ce, 0x4939, 0x6b17, 0x37bb, 0x3e91, 0x76db,
77594b4cc7SMaxime Ripard };
78594b4cc7SMaxime Ripard 
79594b4cc7SMaxime Ripard static uint16_t brom_scrambler_seeds[] = { 0x4a80 };
80594b4cc7SMaxime Ripard 
scramble(const struct image_info * info,int page,uint8_t * data,int datalen)81594b4cc7SMaxime Ripard static void scramble(const struct image_info *info,
82594b4cc7SMaxime Ripard 		     int page, uint8_t *data, int datalen)
83594b4cc7SMaxime Ripard {
84594b4cc7SMaxime Ripard 	uint16_t state;
85594b4cc7SMaxime Ripard 	int i;
86594b4cc7SMaxime Ripard 
87594b4cc7SMaxime Ripard 	/* Boot0 is always scrambled no matter the command line option. */
88594b4cc7SMaxime Ripard 	if (info->boot0) {
89594b4cc7SMaxime Ripard 		state = brom_scrambler_seeds[0];
90594b4cc7SMaxime Ripard 	} else {
91594b4cc7SMaxime Ripard 		unsigned seedmod = info->eraseblock_size / info->page_size;
92594b4cc7SMaxime Ripard 
93594b4cc7SMaxime Ripard 		/* Bail out earlier if the user didn't ask for scrambling. */
94594b4cc7SMaxime Ripard 		if (!info->scramble)
95594b4cc7SMaxime Ripard 			return;
96594b4cc7SMaxime Ripard 
97594b4cc7SMaxime Ripard 		if (seedmod > ARRAY_SIZE(default_scrambler_seeds))
98594b4cc7SMaxime Ripard 			seedmod = ARRAY_SIZE(default_scrambler_seeds);
99594b4cc7SMaxime Ripard 
100594b4cc7SMaxime Ripard 		state = default_scrambler_seeds[page % seedmod];
101594b4cc7SMaxime Ripard 	}
102594b4cc7SMaxime Ripard 
103594b4cc7SMaxime Ripard 	/* Prepare the initial state... */
104594b4cc7SMaxime Ripard 	state = lfsr_step(state, 15);
105594b4cc7SMaxime Ripard 
106594b4cc7SMaxime Ripard 	/* and start scrambling data. */
107594b4cc7SMaxime Ripard 	for (i = 0; i < datalen; i++) {
108594b4cc7SMaxime Ripard 		data[i] ^= state;
109594b4cc7SMaxime Ripard 		state = lfsr_step(state, 8);
110594b4cc7SMaxime Ripard 	}
111594b4cc7SMaxime Ripard }
112594b4cc7SMaxime Ripard 
write_page(const struct image_info * info,uint8_t * buffer,FILE * src,FILE * rnd,FILE * dst,struct bch_control * bch,int page)113594b4cc7SMaxime Ripard static int write_page(const struct image_info *info, uint8_t *buffer,
114594b4cc7SMaxime Ripard 		      FILE *src, FILE *rnd, FILE *dst,
115594b4cc7SMaxime Ripard 		      struct bch_control *bch, int page)
116594b4cc7SMaxime Ripard {
117594b4cc7SMaxime Ripard 	int steps = info->usable_page_size / info->ecc_step_size;
118594b4cc7SMaxime Ripard 	int eccbytes = DIV_ROUND_UP(info->ecc_strength * 14, 8);
119594b4cc7SMaxime Ripard 	off_t pos = ftell(dst);
120594b4cc7SMaxime Ripard 	size_t pad, cnt;
121594b4cc7SMaxime Ripard 	int i;
122594b4cc7SMaxime Ripard 
123594b4cc7SMaxime Ripard 	if (eccbytes % 2)
124594b4cc7SMaxime Ripard 		eccbytes++;
125594b4cc7SMaxime Ripard 
126594b4cc7SMaxime Ripard 	memset(buffer, 0xff, info->page_size + info->oob_size);
127594b4cc7SMaxime Ripard 	cnt = fread(buffer, 1, info->usable_page_size, src);
128594b4cc7SMaxime Ripard 	if (!cnt) {
129594b4cc7SMaxime Ripard 		if (!feof(src)) {
130594b4cc7SMaxime Ripard 			fprintf(stderr,
131594b4cc7SMaxime Ripard 				"Failed to read data from the source\n");
132594b4cc7SMaxime Ripard 			return -1;
133594b4cc7SMaxime Ripard 		} else {
134594b4cc7SMaxime Ripard 			return 0;
135594b4cc7SMaxime Ripard 		}
136594b4cc7SMaxime Ripard 	}
137594b4cc7SMaxime Ripard 
138594b4cc7SMaxime Ripard 	fwrite(buffer, info->page_size + info->oob_size, 1, dst);
139594b4cc7SMaxime Ripard 
140594b4cc7SMaxime Ripard 	for (i = 0; i < info->usable_page_size; i++) {
141594b4cc7SMaxime Ripard 		if (buffer[i] !=  0xff)
142594b4cc7SMaxime Ripard 			break;
143594b4cc7SMaxime Ripard 	}
144594b4cc7SMaxime Ripard 
145594b4cc7SMaxime Ripard 	/* We leave empty pages at 0xff. */
146594b4cc7SMaxime Ripard 	if (i == info->usable_page_size)
147594b4cc7SMaxime Ripard 		return 0;
148594b4cc7SMaxime Ripard 
149594b4cc7SMaxime Ripard 	/* Restore the source pointer to read it again. */
150594b4cc7SMaxime Ripard 	fseek(src, -cnt, SEEK_CUR);
151594b4cc7SMaxime Ripard 
152594b4cc7SMaxime Ripard 	/* Randomize unused space if scrambling is required. */
153594b4cc7SMaxime Ripard 	if (info->scramble) {
154594b4cc7SMaxime Ripard 		int offs;
155594b4cc7SMaxime Ripard 
156594b4cc7SMaxime Ripard 		if (info->boot0) {
157594b4cc7SMaxime Ripard 			size_t ret;
158594b4cc7SMaxime Ripard 
159594b4cc7SMaxime Ripard 			offs = steps * (info->ecc_step_size + eccbytes + 4);
160594b4cc7SMaxime Ripard 			cnt = info->page_size + info->oob_size - offs;
161594b4cc7SMaxime Ripard 			ret = fread(buffer + offs, 1, cnt, rnd);
162594b4cc7SMaxime Ripard 			if (!ret && !feof(rnd)) {
163594b4cc7SMaxime Ripard 				fprintf(stderr,
164594b4cc7SMaxime Ripard 					"Failed to read random data\n");
165594b4cc7SMaxime Ripard 				return -1;
166594b4cc7SMaxime Ripard 			}
167594b4cc7SMaxime Ripard 		} else {
168594b4cc7SMaxime Ripard 			offs = info->page_size + (steps * (eccbytes + 4));
169594b4cc7SMaxime Ripard 			cnt = info->page_size + info->oob_size - offs;
170594b4cc7SMaxime Ripard 			memset(buffer + offs, 0xff, cnt);
171594b4cc7SMaxime Ripard 			scramble(info, page, buffer + offs, cnt);
172594b4cc7SMaxime Ripard 		}
173594b4cc7SMaxime Ripard 		fseek(dst, pos + offs, SEEK_SET);
174594b4cc7SMaxime Ripard 		fwrite(buffer + offs, cnt, 1, dst);
175594b4cc7SMaxime Ripard 	}
176594b4cc7SMaxime Ripard 
177594b4cc7SMaxime Ripard 	for (i = 0; i < steps; i++) {
178594b4cc7SMaxime Ripard 		int ecc_offs, data_offs;
179594b4cc7SMaxime Ripard 		uint8_t *ecc;
180594b4cc7SMaxime Ripard 
181594b4cc7SMaxime Ripard 		memset(buffer, 0xff, info->ecc_step_size + eccbytes + 4);
182594b4cc7SMaxime Ripard 		ecc = buffer + info->ecc_step_size + 4;
183594b4cc7SMaxime Ripard 		if (info->boot0) {
184594b4cc7SMaxime Ripard 			data_offs = i * (info->ecc_step_size + eccbytes + 4);
185594b4cc7SMaxime Ripard 			ecc_offs = data_offs + info->ecc_step_size + 4;
186594b4cc7SMaxime Ripard 		} else {
187594b4cc7SMaxime Ripard 			data_offs = i * info->ecc_step_size;
188594b4cc7SMaxime Ripard 			ecc_offs = info->page_size + 4 + (i * (eccbytes + 4));
189594b4cc7SMaxime Ripard 		}
190594b4cc7SMaxime Ripard 
191594b4cc7SMaxime Ripard 		cnt = fread(buffer, 1, info->ecc_step_size, src);
192594b4cc7SMaxime Ripard 		if (!cnt && !feof(src)) {
193594b4cc7SMaxime Ripard 			fprintf(stderr,
194594b4cc7SMaxime Ripard 				"Failed to read data from the source\n");
195594b4cc7SMaxime Ripard 			return -1;
196594b4cc7SMaxime Ripard 		}
197594b4cc7SMaxime Ripard 
198594b4cc7SMaxime Ripard 		pad = info->ecc_step_size - cnt;
199594b4cc7SMaxime Ripard 		if (pad) {
200594b4cc7SMaxime Ripard 			if (info->scramble && info->boot0) {
201594b4cc7SMaxime Ripard 				size_t ret;
202594b4cc7SMaxime Ripard 
203594b4cc7SMaxime Ripard 				ret = fread(buffer + cnt, 1, pad, rnd);
204594b4cc7SMaxime Ripard 				if (!ret && !feof(rnd)) {
205594b4cc7SMaxime Ripard 					fprintf(stderr,
206594b4cc7SMaxime Ripard 						"Failed to read random data\n");
207594b4cc7SMaxime Ripard 					return -1;
208594b4cc7SMaxime Ripard 				}
209594b4cc7SMaxime Ripard 			} else {
210594b4cc7SMaxime Ripard 				memset(buffer + cnt, 0xff, pad);
211594b4cc7SMaxime Ripard 			}
212594b4cc7SMaxime Ripard 		}
213594b4cc7SMaxime Ripard 
214594b4cc7SMaxime Ripard 		memset(ecc, 0, eccbytes);
215594b4cc7SMaxime Ripard 		swap_bits(buffer, info->ecc_step_size + 4);
216594b4cc7SMaxime Ripard 		encode_bch(bch, buffer, info->ecc_step_size + 4, ecc);
217594b4cc7SMaxime Ripard 		swap_bits(buffer, info->ecc_step_size + 4);
218594b4cc7SMaxime Ripard 		swap_bits(ecc, eccbytes);
219594b4cc7SMaxime Ripard 		scramble(info, page, buffer, info->ecc_step_size + 4 + eccbytes);
220594b4cc7SMaxime Ripard 
221594b4cc7SMaxime Ripard 		fseek(dst, pos + data_offs, SEEK_SET);
222594b4cc7SMaxime Ripard 		fwrite(buffer, info->ecc_step_size, 1, dst);
223594b4cc7SMaxime Ripard 		fseek(dst, pos + ecc_offs - 4, SEEK_SET);
224594b4cc7SMaxime Ripard 		fwrite(ecc - 4, eccbytes + 4, 1, dst);
225594b4cc7SMaxime Ripard 	}
226594b4cc7SMaxime Ripard 
227594b4cc7SMaxime Ripard 	/* Fix BBM. */
228594b4cc7SMaxime Ripard 	fseek(dst, pos + info->page_size, SEEK_SET);
229594b4cc7SMaxime Ripard 	memset(buffer, 0xff, 2);
230594b4cc7SMaxime Ripard 	fwrite(buffer, 2, 1, dst);
231594b4cc7SMaxime Ripard 
232594b4cc7SMaxime Ripard 	/* Make dst pointer point to the next page. */
233594b4cc7SMaxime Ripard 	fseek(dst, pos + info->page_size + info->oob_size, SEEK_SET);
234594b4cc7SMaxime Ripard 
235594b4cc7SMaxime Ripard 	return 0;
236594b4cc7SMaxime Ripard }
237594b4cc7SMaxime Ripard 
create_image(const struct image_info * info)238594b4cc7SMaxime Ripard static int create_image(const struct image_info *info)
239594b4cc7SMaxime Ripard {
240594b4cc7SMaxime Ripard 	off_t page = info->offset / info->page_size;
241594b4cc7SMaxime Ripard 	struct bch_control *bch;
242594b4cc7SMaxime Ripard 	FILE *src, *dst, *rnd;
243594b4cc7SMaxime Ripard 	uint8_t *buffer;
244594b4cc7SMaxime Ripard 
245594b4cc7SMaxime Ripard 	bch = init_bch(14, info->ecc_strength, BCH_PRIMITIVE_POLY);
246594b4cc7SMaxime Ripard 	if (!bch) {
247594b4cc7SMaxime Ripard 		fprintf(stderr, "Failed to init the BCH engine\n");
248594b4cc7SMaxime Ripard 		return -1;
249594b4cc7SMaxime Ripard 	}
250594b4cc7SMaxime Ripard 
251594b4cc7SMaxime Ripard 	buffer = malloc(info->page_size + info->oob_size);
252594b4cc7SMaxime Ripard 	if (!buffer) {
253594b4cc7SMaxime Ripard 		fprintf(stderr, "Failed to allocate the NAND page buffer\n");
254594b4cc7SMaxime Ripard 		return -1;
255594b4cc7SMaxime Ripard 	}
256594b4cc7SMaxime Ripard 
257594b4cc7SMaxime Ripard 	memset(buffer, 0xff, info->page_size + info->oob_size);
258594b4cc7SMaxime Ripard 
259594b4cc7SMaxime Ripard 	src = fopen(info->source, "r");
260594b4cc7SMaxime Ripard 	if (!src) {
261594b4cc7SMaxime Ripard 		fprintf(stderr, "Failed to open source file (%s)\n",
262594b4cc7SMaxime Ripard 			info->source);
263594b4cc7SMaxime Ripard 		return -1;
264594b4cc7SMaxime Ripard 	}
265594b4cc7SMaxime Ripard 
266594b4cc7SMaxime Ripard 	dst = fopen(info->dest, "w");
267594b4cc7SMaxime Ripard 	if (!dst) {
268594b4cc7SMaxime Ripard 		fprintf(stderr, "Failed to open dest file (%s)\n", info->dest);
269594b4cc7SMaxime Ripard 		return -1;
270594b4cc7SMaxime Ripard 	}
271594b4cc7SMaxime Ripard 
272594b4cc7SMaxime Ripard 	rnd = fopen("/dev/urandom", "r");
273594b4cc7SMaxime Ripard 	if (!rnd) {
274594b4cc7SMaxime Ripard 		fprintf(stderr, "Failed to open /dev/urandom\n");
275594b4cc7SMaxime Ripard 		return -1;
276594b4cc7SMaxime Ripard 	}
277594b4cc7SMaxime Ripard 
278594b4cc7SMaxime Ripard 	while (!feof(src)) {
279594b4cc7SMaxime Ripard 		int ret;
280594b4cc7SMaxime Ripard 
281594b4cc7SMaxime Ripard 		ret = write_page(info, buffer, src, rnd, dst, bch, page++);
282594b4cc7SMaxime Ripard 		if (ret)
283594b4cc7SMaxime Ripard 			return ret;
284594b4cc7SMaxime Ripard 	}
285594b4cc7SMaxime Ripard 
286594b4cc7SMaxime Ripard 	return 0;
287594b4cc7SMaxime Ripard }
288594b4cc7SMaxime Ripard 
display_help(int status)289594b4cc7SMaxime Ripard static void display_help(int status)
290594b4cc7SMaxime Ripard {
291594b4cc7SMaxime Ripard 	fprintf(status == EXIT_SUCCESS ? stdout : stderr,
292594b4cc7SMaxime Ripard 		"sunxi-nand-image-builder %s\n"
293594b4cc7SMaxime Ripard 		"\n"
294594b4cc7SMaxime Ripard 		"Usage: sunxi-nand-image-builder [OPTIONS] source-image output-image\n"
295594b4cc7SMaxime Ripard 		"\n"
296594b4cc7SMaxime Ripard 		"Creates a raw NAND image that can be read by the sunxi NAND controller.\n"
297594b4cc7SMaxime Ripard 		"\n"
298594b4cc7SMaxime Ripard 		"-h               --help               Display this help and exit\n"
299594b4cc7SMaxime Ripard 		"-c <str>/<step>  --ecc=<str>/<step>   ECC config (strength/step-size)\n"
300594b4cc7SMaxime Ripard 		"-p <size>        --page=<size>        Page size\n"
301594b4cc7SMaxime Ripard 		"-o <size>        --oob=<size>         OOB size\n"
302594b4cc7SMaxime Ripard 		"-u <size>        --usable=<size>      Usable page size\n"
303594b4cc7SMaxime Ripard 		"-e <size>        --eraseblock=<size>  Erase block size\n"
304594b4cc7SMaxime Ripard 		"-b               --boot0              Build a boot0 image.\n"
305594b4cc7SMaxime Ripard 		"-s               --scramble           Scramble data\n"
306594b4cc7SMaxime Ripard 		"-a <offset>      --address=<offset>   Where the image will be programmed.\n"
307594b4cc7SMaxime Ripard 		"\n"
308594b4cc7SMaxime Ripard 		"Notes:\n"
309594b4cc7SMaxime Ripard 		"All the information you need to pass to this tool should be part of\n"
310594b4cc7SMaxime Ripard 		"the NAND datasheet.\n"
311594b4cc7SMaxime Ripard 		"\n"
312594b4cc7SMaxime Ripard 		"The NAND controller only supports the following ECC configs\n"
313594b4cc7SMaxime Ripard 		"  Valid ECC strengths: 16, 24, 28, 32, 40, 48, 56, 60 and 64\n"
314594b4cc7SMaxime Ripard 		"  Valid ECC step size: 512 and 1024\n"
315594b4cc7SMaxime Ripard 		"\n"
316594b4cc7SMaxime Ripard 		"If you are building a boot0 image, you'll have specify extra options.\n"
317594b4cc7SMaxime Ripard 		"These options should be chosen based on the layouts described here:\n"
318594b4cc7SMaxime Ripard 		"  http://linux-sunxi.org/NAND#More_information_on_BROM_NAND\n"
319594b4cc7SMaxime Ripard 		"\n"
320594b4cc7SMaxime Ripard 		"  --usable should be assigned the 'Hardware page' value\n"
321594b4cc7SMaxime Ripard 		"  --ecc should be assigned the 'ECC capacity'/'ECC page' values\n"
322594b4cc7SMaxime Ripard 		"  --usable should be smaller than --page\n"
323594b4cc7SMaxime Ripard 		"\n"
324594b4cc7SMaxime Ripard 		"The --address option is only required for non-boot0 images that are \n"
325594b4cc7SMaxime Ripard 		"meant to be programmed at a non eraseblock aligned offset.\n"
326594b4cc7SMaxime Ripard 		"\n"
327594b4cc7SMaxime Ripard 		"Examples:\n"
328594b4cc7SMaxime Ripard 		"  The H27UCG8T2BTR-BC NAND exposes\n"
329594b4cc7SMaxime Ripard 		"  * 16k pages\n"
330594b4cc7SMaxime Ripard 		"  * 1280 OOB bytes per page\n"
331594b4cc7SMaxime Ripard 		"  * 4M eraseblocks\n"
332594b4cc7SMaxime Ripard 		"  * requires data scrambling\n"
333594b4cc7SMaxime Ripard 		"  * expects a minimum ECC of 40bits/1024bytes\n"
334594b4cc7SMaxime Ripard 		"\n"
335594b4cc7SMaxime Ripard 		"  A normal image can be generated with\n"
336594b4cc7SMaxime Ripard 		"    sunxi-nand-image-builder -p 16384 -o 1280 -e 0x400000 -s -c 40/1024\n"
337594b4cc7SMaxime Ripard 		"  A boot0 image can be generated with\n"
338594b4cc7SMaxime Ripard 		"    sunxi-nand-image-builder -p 16384 -o 1280 -e 0x400000 -s -b -u 4096 -c 64/1024\n",
339594b4cc7SMaxime Ripard 		PLAIN_VERSION);
340594b4cc7SMaxime Ripard 	exit(status);
341594b4cc7SMaxime Ripard }
342594b4cc7SMaxime Ripard 
check_image_info(struct image_info * info)343594b4cc7SMaxime Ripard static int check_image_info(struct image_info *info)
344594b4cc7SMaxime Ripard {
345594b4cc7SMaxime Ripard 	static int valid_ecc_strengths[] = { 16, 24, 28, 32, 40, 48, 56, 60, 64 };
346594b4cc7SMaxime Ripard 	int eccbytes, eccsteps;
347594b4cc7SMaxime Ripard 	unsigned i;
348594b4cc7SMaxime Ripard 
349594b4cc7SMaxime Ripard 	if (!info->page_size) {
350594b4cc7SMaxime Ripard 		fprintf(stderr, "--page is missing\n");
351594b4cc7SMaxime Ripard 		return -EINVAL;
352594b4cc7SMaxime Ripard 	}
353594b4cc7SMaxime Ripard 
354594b4cc7SMaxime Ripard 	if (!info->page_size) {
355594b4cc7SMaxime Ripard 		fprintf(stderr, "--oob is missing\n");
356594b4cc7SMaxime Ripard 		return -EINVAL;
357594b4cc7SMaxime Ripard 	}
358594b4cc7SMaxime Ripard 
359594b4cc7SMaxime Ripard 	if (!info->eraseblock_size) {
360594b4cc7SMaxime Ripard 		fprintf(stderr, "--eraseblock is missing\n");
361594b4cc7SMaxime Ripard 		return -EINVAL;
362594b4cc7SMaxime Ripard 	}
363594b4cc7SMaxime Ripard 
364594b4cc7SMaxime Ripard 	if (info->ecc_step_size != 512 && info->ecc_step_size != 1024) {
365594b4cc7SMaxime Ripard 		fprintf(stderr, "Invalid ECC step argument: %d\n",
366594b4cc7SMaxime Ripard 			info->ecc_step_size);
367594b4cc7SMaxime Ripard 		return -EINVAL;
368594b4cc7SMaxime Ripard 	}
369594b4cc7SMaxime Ripard 
370594b4cc7SMaxime Ripard 	for (i = 0; i < ARRAY_SIZE(valid_ecc_strengths); i++) {
371594b4cc7SMaxime Ripard 		if (valid_ecc_strengths[i] == info->ecc_strength)
372594b4cc7SMaxime Ripard 			break;
373594b4cc7SMaxime Ripard 	}
374594b4cc7SMaxime Ripard 
375594b4cc7SMaxime Ripard 	if (i == ARRAY_SIZE(valid_ecc_strengths)) {
376594b4cc7SMaxime Ripard 		fprintf(stderr, "Invalid ECC strength argument: %d\n",
377594b4cc7SMaxime Ripard 			info->ecc_strength);
378594b4cc7SMaxime Ripard 		return -EINVAL;
379594b4cc7SMaxime Ripard 	}
380594b4cc7SMaxime Ripard 
381594b4cc7SMaxime Ripard 	eccbytes = DIV_ROUND_UP(info->ecc_strength * 14, 8);
382594b4cc7SMaxime Ripard 	if (eccbytes % 2)
383594b4cc7SMaxime Ripard 		eccbytes++;
384594b4cc7SMaxime Ripard 	eccbytes += 4;
385594b4cc7SMaxime Ripard 
386594b4cc7SMaxime Ripard 	eccsteps = info->usable_page_size / info->ecc_step_size;
387594b4cc7SMaxime Ripard 
388594b4cc7SMaxime Ripard 	if (info->page_size + info->oob_size <
389594b4cc7SMaxime Ripard 	    info->usable_page_size + (eccsteps * eccbytes)) {
390594b4cc7SMaxime Ripard 		fprintf(stderr,
391594b4cc7SMaxime Ripard 			"ECC bytes do not fit in the NAND page, choose a weaker ECC\n");
392594b4cc7SMaxime Ripard 		return -EINVAL;
393594b4cc7SMaxime Ripard 	}
394594b4cc7SMaxime Ripard 
395594b4cc7SMaxime Ripard 	return 0;
396594b4cc7SMaxime Ripard }
397594b4cc7SMaxime Ripard 
main(int argc,char ** argv)398594b4cc7SMaxime Ripard int main(int argc, char **argv)
399594b4cc7SMaxime Ripard {
400594b4cc7SMaxime Ripard 	struct image_info info;
401594b4cc7SMaxime Ripard 
402594b4cc7SMaxime Ripard 	memset(&info, 0, sizeof(info));
403594b4cc7SMaxime Ripard 	/*
404594b4cc7SMaxime Ripard 	 * Process user arguments
405594b4cc7SMaxime Ripard 	 */
406594b4cc7SMaxime Ripard 	for (;;) {
407594b4cc7SMaxime Ripard 		int option_index = 0;
408594b4cc7SMaxime Ripard 		char *endptr = NULL;
409594b4cc7SMaxime Ripard 		static const struct option long_options[] = {
410594b4cc7SMaxime Ripard 			{"help", no_argument, 0, 'h'},
411594b4cc7SMaxime Ripard 			{"ecc", required_argument, 0, 'c'},
412594b4cc7SMaxime Ripard 			{"page", required_argument, 0, 'p'},
413594b4cc7SMaxime Ripard 			{"oob", required_argument, 0, 'o'},
414594b4cc7SMaxime Ripard 			{"usable", required_argument, 0, 'u'},
415594b4cc7SMaxime Ripard 			{"eraseblock", required_argument, 0, 'e'},
416594b4cc7SMaxime Ripard 			{"boot0", no_argument, 0, 'b'},
417594b4cc7SMaxime Ripard 			{"scramble", no_argument, 0, 's'},
418594b4cc7SMaxime Ripard 			{"address", required_argument, 0, 'a'},
419594b4cc7SMaxime Ripard 			{0, 0, 0, 0},
420594b4cc7SMaxime Ripard 		};
421594b4cc7SMaxime Ripard 
422594b4cc7SMaxime Ripard 		int c = getopt_long(argc, argv, "c:p:o:u:e:ba:sh",
423594b4cc7SMaxime Ripard 				long_options, &option_index);
424594b4cc7SMaxime Ripard 		if (c == EOF)
425594b4cc7SMaxime Ripard 			break;
426594b4cc7SMaxime Ripard 
427594b4cc7SMaxime Ripard 		switch (c) {
428594b4cc7SMaxime Ripard 		case 'h':
429594b4cc7SMaxime Ripard 			display_help(0);
430594b4cc7SMaxime Ripard 			break;
431594b4cc7SMaxime Ripard 		case 's':
432594b4cc7SMaxime Ripard 			info.scramble = 1;
433594b4cc7SMaxime Ripard 			break;
434594b4cc7SMaxime Ripard 		case 'c':
435594b4cc7SMaxime Ripard 			info.ecc_strength = strtol(optarg, &endptr, 0);
436*f59a3b21Sxypron.glpk@gmx.de 			if (*endptr == '/')
437594b4cc7SMaxime Ripard 				info.ecc_step_size = strtol(endptr + 1, NULL, 0);
438594b4cc7SMaxime Ripard 			break;
439594b4cc7SMaxime Ripard 		case 'p':
440594b4cc7SMaxime Ripard 			info.page_size = strtol(optarg, NULL, 0);
441594b4cc7SMaxime Ripard 			break;
442594b4cc7SMaxime Ripard 		case 'o':
443594b4cc7SMaxime Ripard 			info.oob_size = strtol(optarg, NULL, 0);
444594b4cc7SMaxime Ripard 			break;
445594b4cc7SMaxime Ripard 		case 'u':
446594b4cc7SMaxime Ripard 			info.usable_page_size = strtol(optarg, NULL, 0);
447594b4cc7SMaxime Ripard 			break;
448594b4cc7SMaxime Ripard 		case 'e':
449594b4cc7SMaxime Ripard 			info.eraseblock_size = strtol(optarg, NULL, 0);
450594b4cc7SMaxime Ripard 			break;
451594b4cc7SMaxime Ripard 		case 'b':
452594b4cc7SMaxime Ripard 			info.boot0 = 1;
453594b4cc7SMaxime Ripard 			break;
454594b4cc7SMaxime Ripard 		case 'a':
455594b4cc7SMaxime Ripard 			info.offset = strtoull(optarg, NULL, 0);
456594b4cc7SMaxime Ripard 			break;
457594b4cc7SMaxime Ripard 		case '?':
458594b4cc7SMaxime Ripard 			display_help(-1);
459594b4cc7SMaxime Ripard 			break;
460594b4cc7SMaxime Ripard 		}
461594b4cc7SMaxime Ripard 	}
462594b4cc7SMaxime Ripard 
463594b4cc7SMaxime Ripard 	if ((argc - optind) != 2)
464594b4cc7SMaxime Ripard 		display_help(-1);
465594b4cc7SMaxime Ripard 
466594b4cc7SMaxime Ripard 	info.source = argv[optind];
467594b4cc7SMaxime Ripard 	info.dest = argv[optind + 1];
468594b4cc7SMaxime Ripard 
469594b4cc7SMaxime Ripard 	if (!info.boot0) {
470594b4cc7SMaxime Ripard 		info.usable_page_size = info.page_size;
471594b4cc7SMaxime Ripard 	} else if (!info.usable_page_size) {
472594b4cc7SMaxime Ripard 		if (info.page_size > 8192)
473594b4cc7SMaxime Ripard 			info.usable_page_size = 8192;
474594b4cc7SMaxime Ripard 		else if (info.page_size > 4096)
475594b4cc7SMaxime Ripard 			info.usable_page_size = 4096;
476594b4cc7SMaxime Ripard 		else
477594b4cc7SMaxime Ripard 			info.usable_page_size = 1024;
478594b4cc7SMaxime Ripard 	}
479594b4cc7SMaxime Ripard 
480594b4cc7SMaxime Ripard 	if (check_image_info(&info))
481594b4cc7SMaxime Ripard 		display_help(-1);
482594b4cc7SMaxime Ripard 
483594b4cc7SMaxime Ripard 	return create_image(&info);
484594b4cc7SMaxime Ripard }
485