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