1/*
2 * Allwinner NAND randomizer and image builder implementation:
3 *
4 * Copyright �� 2016 NextThing Co.
5 * Copyright �� 2016 Free Electrons
6 *
7 * Author: Boris Brezillon <boris.brezillon@free-electrons.com>
8 *
9 */
10
11#include <linux/bch.h>
12
13#include <getopt.h>
14#include <version.h>
15
16#define BCH_PRIMITIVE_POLY	0x5803
17
18#define ARRAY_SIZE(arr)		(sizeof(arr) / sizeof((arr)[0]))
19#define DIV_ROUND_UP(n,d)	(((n) + (d) - 1) / (d))
20
21struct image_info {
22	int ecc_strength;
23	int ecc_step_size;
24	int page_size;
25	int oob_size;
26	int usable_page_size;
27	int eraseblock_size;
28	int scramble;
29	int boot0;
30	off_t offset;
31	const char *source;
32	const char *dest;
33};
34
35static void swap_bits(uint8_t *buf, int len)
36{
37	int i, j;
38
39	for (j = 0; j < len; j++) {
40		uint8_t byte = buf[j];
41
42		buf[j] = 0;
43		for (i = 0; i < 8; i++) {
44			if (byte & (1 << i))
45				buf[j] |= (1 << (7 - i));
46		}
47	}
48}
49
50static uint16_t lfsr_step(uint16_t state, int count)
51{
52	state &= 0x7fff;
53	while (count--)
54		state = ((state >> 1) |
55			 ((((state >> 0) ^ (state >> 1)) & 1) << 14)) & 0x7fff;
56
57	return state;
58}
59
60static uint16_t default_scrambler_seeds[] = {
61	0x2b75, 0x0bd0, 0x5ca3, 0x62d1, 0x1c93, 0x07e9, 0x2162, 0x3a72,
62	0x0d67, 0x67f9, 0x1be7, 0x077d, 0x032f, 0x0dac, 0x2716, 0x2436,
63	0x7922, 0x1510, 0x3860, 0x5287, 0x480f, 0x4252, 0x1789, 0x5a2d,
64	0x2a49, 0x5e10, 0x437f, 0x4b4e, 0x2f45, 0x216e, 0x5cb7, 0x7130,
65	0x2a3f, 0x60e4, 0x4dc9, 0x0ef0, 0x0f52, 0x1bb9, 0x6211, 0x7a56,
66	0x226d, 0x4ea7, 0x6f36, 0x3692, 0x38bf, 0x0c62, 0x05eb, 0x4c55,
67	0x60f4, 0x728c, 0x3b6f, 0x2037, 0x7f69, 0x0936, 0x651a, 0x4ceb,
68	0x6218, 0x79f3, 0x383f, 0x18d9, 0x4f05, 0x5c82, 0x2912, 0x6f17,
69	0x6856, 0x5938, 0x1007, 0x61ab, 0x3e7f, 0x57c2, 0x542f, 0x4f62,
70	0x7454, 0x2eac, 0x7739, 0x42d4, 0x2f90, 0x435a, 0x2e52, 0x2064,
71	0x637c, 0x66ad, 0x2c90, 0x0bad, 0x759c, 0x0029, 0x0986, 0x7126,
72	0x1ca7, 0x1605, 0x386a, 0x27f5, 0x1380, 0x6d75, 0x24c3, 0x0f8e,
73	0x2b7a, 0x1418, 0x1fd1, 0x7dc1, 0x2d8e, 0x43af, 0x2267, 0x7da3,
74	0x4e3d, 0x1338, 0x50db, 0x454d, 0x764d, 0x40a3, 0x42e6, 0x262b,
75	0x2d2e, 0x1aea, 0x2e17, 0x173d, 0x3a6e, 0x71bf, 0x25f9, 0x0a5d,
76	0x7c57, 0x0fbe, 0x46ce, 0x4939, 0x6b17, 0x37bb, 0x3e91, 0x76db,
77};
78
79static uint16_t brom_scrambler_seeds[] = { 0x4a80 };
80
81static void scramble(const struct image_info *info,
82		     int page, uint8_t *data, int datalen)
83{
84	uint16_t state;
85	int i;
86
87	/* Boot0 is always scrambled no matter the command line option. */
88	if (info->boot0) {
89		state = brom_scrambler_seeds[0];
90	} else {
91		unsigned seedmod = info->eraseblock_size / info->page_size;
92
93		/* Bail out earlier if the user didn't ask for scrambling. */
94		if (!info->scramble)
95			return;
96
97		if (seedmod > ARRAY_SIZE(default_scrambler_seeds))
98			seedmod = ARRAY_SIZE(default_scrambler_seeds);
99
100		state = default_scrambler_seeds[page % seedmod];
101	}
102
103	/* Prepare the initial state... */
104	state = lfsr_step(state, 15);
105
106	/* and start scrambling data. */
107	for (i = 0; i < datalen; i++) {
108		data[i] ^= state;
109		state = lfsr_step(state, 8);
110	}
111}
112
113static int write_page(const struct image_info *info, uint8_t *buffer,
114		      FILE *src, FILE *rnd, FILE *dst,
115		      struct bch_control *bch, int page)
116{
117	int steps = info->usable_page_size / info->ecc_step_size;
118	int eccbytes = DIV_ROUND_UP(info->ecc_strength * 14, 8);
119	off_t pos = ftell(dst);
120	size_t pad, cnt;
121	int i;
122
123	if (eccbytes % 2)
124		eccbytes++;
125
126	memset(buffer, 0xff, info->page_size + info->oob_size);
127	cnt = fread(buffer, 1, info->usable_page_size, src);
128	if (!cnt) {
129		if (!feof(src)) {
130			fprintf(stderr,
131				"Failed to read data from the source\n");
132			return -1;
133		} else {
134			return 0;
135		}
136	}
137
138	fwrite(buffer, info->page_size + info->oob_size, 1, dst);
139
140	for (i = 0; i < info->usable_page_size; i++) {
141		if (buffer[i] !=  0xff)
142			break;
143	}
144
145	/* We leave empty pages at 0xff. */
146	if (i == info->usable_page_size)
147		return 0;
148
149	/* Restore the source pointer to read it again. */
150	fseek(src, -cnt, SEEK_CUR);
151
152	/* Randomize unused space if scrambling is required. */
153	if (info->scramble) {
154		int offs;
155
156		if (info->boot0) {
157			size_t ret;
158
159			offs = steps * (info->ecc_step_size + eccbytes + 4);
160			cnt = info->page_size + info->oob_size - offs;
161			ret = fread(buffer + offs, 1, cnt, rnd);
162			if (!ret && !feof(rnd)) {
163				fprintf(stderr,
164					"Failed to read random data\n");
165				return -1;
166			}
167		} else {
168			offs = info->page_size + (steps * (eccbytes + 4));
169			cnt = info->page_size + info->oob_size - offs;
170			memset(buffer + offs, 0xff, cnt);
171			scramble(info, page, buffer + offs, cnt);
172		}
173		fseek(dst, pos + offs, SEEK_SET);
174		fwrite(buffer + offs, cnt, 1, dst);
175	}
176
177	for (i = 0; i < steps; i++) {
178		int ecc_offs, data_offs;
179		uint8_t *ecc;
180
181		memset(buffer, 0xff, info->ecc_step_size + eccbytes + 4);
182		ecc = buffer + info->ecc_step_size + 4;
183		if (info->boot0) {
184			data_offs = i * (info->ecc_step_size + eccbytes + 4);
185			ecc_offs = data_offs + info->ecc_step_size + 4;
186		} else {
187			data_offs = i * info->ecc_step_size;
188			ecc_offs = info->page_size + 4 + (i * (eccbytes + 4));
189		}
190
191		cnt = fread(buffer, 1, info->ecc_step_size, src);
192		if (!cnt && !feof(src)) {
193			fprintf(stderr,
194				"Failed to read data from the source\n");
195			return -1;
196		}
197
198		pad = info->ecc_step_size - cnt;
199		if (pad) {
200			if (info->scramble && info->boot0) {
201				size_t ret;
202
203				ret = fread(buffer + cnt, 1, pad, rnd);
204				if (!ret && !feof(rnd)) {
205					fprintf(stderr,
206						"Failed to read random data\n");
207					return -1;
208				}
209			} else {
210				memset(buffer + cnt, 0xff, pad);
211			}
212		}
213
214		memset(ecc, 0, eccbytes);
215		swap_bits(buffer, info->ecc_step_size + 4);
216		encode_bch(bch, buffer, info->ecc_step_size + 4, ecc);
217		swap_bits(buffer, info->ecc_step_size + 4);
218		swap_bits(ecc, eccbytes);
219		scramble(info, page, buffer, info->ecc_step_size + 4 + eccbytes);
220
221		fseek(dst, pos + data_offs, SEEK_SET);
222		fwrite(buffer, info->ecc_step_size, 1, dst);
223		fseek(dst, pos + ecc_offs - 4, SEEK_SET);
224		fwrite(ecc - 4, eccbytes + 4, 1, dst);
225	}
226
227	/* Fix BBM. */
228	fseek(dst, pos + info->page_size, SEEK_SET);
229	memset(buffer, 0xff, 2);
230	fwrite(buffer, 2, 1, dst);
231
232	/* Make dst pointer point to the next page. */
233	fseek(dst, pos + info->page_size + info->oob_size, SEEK_SET);
234
235	return 0;
236}
237
238static int create_image(const struct image_info *info)
239{
240	off_t page = info->offset / info->page_size;
241	struct bch_control *bch;
242	FILE *src, *dst, *rnd;
243	uint8_t *buffer;
244
245	bch = init_bch(14, info->ecc_strength, BCH_PRIMITIVE_POLY);
246	if (!bch) {
247		fprintf(stderr, "Failed to init the BCH engine\n");
248		return -1;
249	}
250
251	buffer = malloc(info->page_size + info->oob_size);
252	if (!buffer) {
253		fprintf(stderr, "Failed to allocate the NAND page buffer\n");
254		return -1;
255	}
256
257	memset(buffer, 0xff, info->page_size + info->oob_size);
258
259	src = fopen(info->source, "r");
260	if (!src) {
261		fprintf(stderr, "Failed to open source file (%s)\n",
262			info->source);
263		return -1;
264	}
265
266	dst = fopen(info->dest, "w");
267	if (!dst) {
268		fprintf(stderr, "Failed to open dest file (%s)\n", info->dest);
269		return -1;
270	}
271
272	rnd = fopen("/dev/urandom", "r");
273	if (!rnd) {
274		fprintf(stderr, "Failed to open /dev/urandom\n");
275		return -1;
276	}
277
278	while (!feof(src)) {
279		int ret;
280
281		ret = write_page(info, buffer, src, rnd, dst, bch, page++);
282		if (ret)
283			return ret;
284	}
285
286	return 0;
287}
288
289static void display_help(int status)
290{
291	fprintf(status == EXIT_SUCCESS ? stdout : stderr,
292		"sunxi-nand-image-builder %s\n"
293		"\n"
294		"Usage: sunxi-nand-image-builder [OPTIONS] source-image output-image\n"
295		"\n"
296		"Creates a raw NAND image that can be read by the sunxi NAND controller.\n"
297		"\n"
298		"-h               --help               Display this help and exit\n"
299		"-c <str>/<step>  --ecc=<str>/<step>   ECC config (strength/step-size)\n"
300		"-p <size>        --page=<size>        Page size\n"
301		"-o <size>        --oob=<size>         OOB size\n"
302		"-u <size>        --usable=<size>      Usable page size\n"
303		"-e <size>        --eraseblock=<size>  Erase block size\n"
304		"-b               --boot0              Build a boot0 image.\n"
305		"-s               --scramble           Scramble data\n"
306		"-a <offset>      --address=<offset>   Where the image will be programmed.\n"
307		"\n"
308		"Notes:\n"
309		"All the information you need to pass to this tool should be part of\n"
310		"the NAND datasheet.\n"
311		"\n"
312		"The NAND controller only supports the following ECC configs\n"
313		"  Valid ECC strengths: 16, 24, 28, 32, 40, 48, 56, 60 and 64\n"
314		"  Valid ECC step size: 512 and 1024\n"
315		"\n"
316		"If you are building a boot0 image, you'll have specify extra options.\n"
317		"These options should be chosen based on the layouts described here:\n"
318		"  http://linux-sunxi.org/NAND#More_information_on_BROM_NAND\n"
319		"\n"
320		"  --usable should be assigned the 'Hardware page' value\n"
321		"  --ecc should be assigned the 'ECC capacity'/'ECC page' values\n"
322		"  --usable should be smaller than --page\n"
323		"\n"
324		"The --address option is only required for non-boot0 images that are \n"
325		"meant to be programmed at a non eraseblock aligned offset.\n"
326		"\n"
327		"Examples:\n"
328		"  The H27UCG8T2BTR-BC NAND exposes\n"
329		"  * 16k pages\n"
330		"  * 1280 OOB bytes per page\n"
331		"  * 4M eraseblocks\n"
332		"  * requires data scrambling\n"
333		"  * expects a minimum ECC of 40bits/1024bytes\n"
334		"\n"
335		"  A normal image can be generated with\n"
336		"    sunxi-nand-image-builder -p 16384 -o 1280 -e 0x400000 -s -c 40/1024\n"
337		"  A boot0 image can be generated with\n"
338		"    sunxi-nand-image-builder -p 16384 -o 1280 -e 0x400000 -s -b -u 4096 -c 64/1024\n",
339		PLAIN_VERSION);
340	exit(status);
341}
342
343static int check_image_info(struct image_info *info)
344{
345	static int valid_ecc_strengths[] = { 16, 24, 28, 32, 40, 48, 56, 60, 64 };
346	int eccbytes, eccsteps;
347	unsigned i;
348
349	if (!info->page_size) {
350		fprintf(stderr, "--page is missing\n");
351		return -EINVAL;
352	}
353
354	if (!info->page_size) {
355		fprintf(stderr, "--oob is missing\n");
356		return -EINVAL;
357	}
358
359	if (!info->eraseblock_size) {
360		fprintf(stderr, "--eraseblock is missing\n");
361		return -EINVAL;
362	}
363
364	if (info->ecc_step_size != 512 && info->ecc_step_size != 1024) {
365		fprintf(stderr, "Invalid ECC step argument: %d\n",
366			info->ecc_step_size);
367		return -EINVAL;
368	}
369
370	for (i = 0; i < ARRAY_SIZE(valid_ecc_strengths); i++) {
371		if (valid_ecc_strengths[i] == info->ecc_strength)
372			break;
373	}
374
375	if (i == ARRAY_SIZE(valid_ecc_strengths)) {
376		fprintf(stderr, "Invalid ECC strength argument: %d\n",
377			info->ecc_strength);
378		return -EINVAL;
379	}
380
381	eccbytes = DIV_ROUND_UP(info->ecc_strength * 14, 8);
382	if (eccbytes % 2)
383		eccbytes++;
384	eccbytes += 4;
385
386	eccsteps = info->usable_page_size / info->ecc_step_size;
387
388	if (info->page_size + info->oob_size <
389	    info->usable_page_size + (eccsteps * eccbytes)) {
390		fprintf(stderr,
391			"ECC bytes do not fit in the NAND page, choose a weaker ECC\n");
392		return -EINVAL;
393	}
394
395	return 0;
396}
397
398int main(int argc, char **argv)
399{
400	struct image_info info;
401
402	memset(&info, 0, sizeof(info));
403	/*
404	 * Process user arguments
405	 */
406	for (;;) {
407		int option_index = 0;
408		char *endptr = NULL;
409		static const struct option long_options[] = {
410			{"help", no_argument, 0, 'h'},
411			{"ecc", required_argument, 0, 'c'},
412			{"page", required_argument, 0, 'p'},
413			{"oob", required_argument, 0, 'o'},
414			{"usable", required_argument, 0, 'u'},
415			{"eraseblock", required_argument, 0, 'e'},
416			{"boot0", no_argument, 0, 'b'},
417			{"scramble", no_argument, 0, 's'},
418			{"address", required_argument, 0, 'a'},
419			{0, 0, 0, 0},
420		};
421
422		int c = getopt_long(argc, argv, "c:p:o:u:e:ba:sh",
423				long_options, &option_index);
424		if (c == EOF)
425			break;
426
427		switch (c) {
428		case 'h':
429			display_help(0);
430			break;
431		case 's':
432			info.scramble = 1;
433			break;
434		case 'c':
435			info.ecc_strength = strtol(optarg, &endptr, 0);
436			if (*endptr == '/')
437				info.ecc_step_size = strtol(endptr + 1, NULL, 0);
438			break;
439		case 'p':
440			info.page_size = strtol(optarg, NULL, 0);
441			break;
442		case 'o':
443			info.oob_size = strtol(optarg, NULL, 0);
444			break;
445		case 'u':
446			info.usable_page_size = strtol(optarg, NULL, 0);
447			break;
448		case 'e':
449			info.eraseblock_size = strtol(optarg, NULL, 0);
450			break;
451		case 'b':
452			info.boot0 = 1;
453			break;
454		case 'a':
455			info.offset = strtoull(optarg, NULL, 0);
456			break;
457		case '?':
458			display_help(-1);
459			break;
460		}
461	}
462
463	if ((argc - optind) != 2)
464		display_help(-1);
465
466	info.source = argv[optind];
467	info.dest = argv[optind + 1];
468
469	if (!info.boot0) {
470		info.usable_page_size = info.page_size;
471	} else if (!info.usable_page_size) {
472		if (info.page_size > 8192)
473			info.usable_page_size = 8192;
474		else if (info.page_size > 4096)
475			info.usable_page_size = 4096;
476		else
477			info.usable_page_size = 1024;
478	}
479
480	if (check_image_info(&info))
481		display_help(-1);
482
483	return create_image(&info);
484}
485