• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
21 struct 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 
swap_bits(uint8_t * buf,int len)35 static 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 
lfsr_step(uint16_t state,int count)50 static 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 
60 static 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 
79 static uint16_t brom_scrambler_seeds[] = { 0x4a80 };
80 
scramble(const struct image_info * info,int page,uint8_t * data,int datalen)81 static 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 
write_page(const struct image_info * info,uint8_t * buffer,FILE * src,FILE * rnd,FILE * dst,struct bch_control * bch,int page)113 static 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 
create_image(const struct image_info * info)238 static 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 
display_help(int status)289 static 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 
check_image_info(struct image_info * info)343 static 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 
main(int argc,char ** argv)398 int 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