Merge https://gitlab.denx.de/u-boot/custodians/u-boot-marvell
[oweals/u-boot.git] / tools / sunxi-spl-image-builder.c
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
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
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
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
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
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
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
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
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 }