uboot/tools/sunxi-spl-image-builder.c
<<
>>
Prefs
   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