uboot/tools/mxsboot.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Freescale i.MX28 image generator
   4 *
   5 * Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com>
   6 * on behalf of DENX Software Engineering GmbH
   7 */
   8
   9#include <fcntl.h>
  10#include <sys/stat.h>
  11#include <sys/types.h>
  12#include <unistd.h>
  13
  14#include "compiler.h"
  15
  16/* Taken from <linux/kernel.h> */
  17#define __round_mask(x, y) ((__typeof__(x))((y)-1))
  18#define round_down(x, y) ((x) & ~__round_mask(x, y))
  19
  20/*
  21 * Default BCB layout.
  22 *
  23 * TWEAK this if you have blown any OCOTP fuses.
  24 */
  25#define STRIDE_PAGES            64
  26#define STRIDE_COUNT            4
  27
  28/*
  29 * Layout for 256Mb big NAND with 2048b page size, 64b OOB size and
  30 * 128kb erase size.
  31 *
  32 * TWEAK this if you have different kind of NAND chip.
  33 */
  34static uint32_t nand_writesize = 2048;
  35static uint32_t nand_oobsize = 64;
  36static uint32_t nand_erasesize = 128 * 1024;
  37
  38/*
  39 * Sector on which the SigmaTel boot partition (0x53) starts.
  40 */
  41static uint32_t sd_sector = 2048;
  42
  43/*
  44 * Each of the U-Boot bootstreams is at maximum 1MB big.
  45 *
  46 * TWEAK this if, for some wild reason, you need to boot bigger image.
  47 */
  48#define MAX_BOOTSTREAM_SIZE     (1 * 1024 * 1024)
  49
  50/* i.MX28 NAND controller-specific constants. DO NOT TWEAK! */
  51#define MXS_NAND_DMA_DESCRIPTOR_COUNT           4
  52#define MXS_NAND_CHUNK_DATA_CHUNK_SIZE          512
  53#define MXS_NAND_METADATA_SIZE                  10
  54#define MXS_NAND_BITS_PER_ECC_LEVEL             13
  55#define MXS_NAND_COMMAND_BUFFER_SIZE            32
  56
  57struct mx28_nand_fcb {
  58        uint32_t                checksum;
  59        uint32_t                fingerprint;
  60        uint32_t                version;
  61        struct {
  62                uint8_t                 data_setup;
  63                uint8_t                 data_hold;
  64                uint8_t                 address_setup;
  65                uint8_t                 dsample_time;
  66                uint8_t                 nand_timing_state;
  67                uint8_t                 rea;
  68                uint8_t                 rloh;
  69                uint8_t                 rhoh;
  70        }                       timing;
  71        uint32_t                page_data_size;
  72        uint32_t                total_page_size;
  73        uint32_t                sectors_per_block;
  74        uint32_t                number_of_nands;                /* Ignored */
  75        uint32_t                total_internal_die;             /* Ignored */
  76        uint32_t                cell_type;                      /* Ignored */
  77        uint32_t                ecc_block_n_ecc_type;
  78        uint32_t                ecc_block_0_size;
  79        uint32_t                ecc_block_n_size;
  80        uint32_t                ecc_block_0_ecc_type;
  81        uint32_t                metadata_bytes;
  82        uint32_t                num_ecc_blocks_per_page;
  83        uint32_t                ecc_block_n_ecc_level_sdk;      /* Ignored */
  84        uint32_t                ecc_block_0_size_sdk;           /* Ignored */
  85        uint32_t                ecc_block_n_size_sdk;           /* Ignored */
  86        uint32_t                ecc_block_0_ecc_level_sdk;      /* Ignored */
  87        uint32_t                num_ecc_blocks_per_page_sdk;    /* Ignored */
  88        uint32_t                metadata_bytes_sdk;             /* Ignored */
  89        uint32_t                erase_threshold;
  90        uint32_t                boot_patch;
  91        uint32_t                patch_sectors;
  92        uint32_t                firmware1_starting_sector;
  93        uint32_t                firmware2_starting_sector;
  94        uint32_t                sectors_in_firmware1;
  95        uint32_t                sectors_in_firmware2;
  96        uint32_t                dbbt_search_area_start_address;
  97        uint32_t                badblock_marker_byte;
  98        uint32_t                badblock_marker_start_bit;
  99        uint32_t                bb_marker_physical_offset;
 100};
 101
 102struct mx28_nand_dbbt {
 103        uint32_t                checksum;
 104        uint32_t                fingerprint;
 105        uint32_t                version;
 106        uint32_t                number_bb;
 107        uint32_t                number_2k_pages_bb;
 108};
 109
 110struct mx28_nand_bbt {
 111        uint32_t                nand;
 112        uint32_t                number_bb;
 113        uint32_t                badblock[510];
 114};
 115
 116struct mx28_sd_drive_info {
 117        uint32_t                chip_num;
 118        uint32_t                drive_type;
 119        uint32_t                tag;
 120        uint32_t                first_sector_number;
 121        uint32_t                sector_count;
 122};
 123
 124struct mx28_sd_config_block {
 125        uint32_t                        signature;
 126        uint32_t                        primary_boot_tag;
 127        uint32_t                        secondary_boot_tag;
 128        uint32_t                        num_copies;
 129        struct mx28_sd_drive_info       drv_info[1];
 130};
 131
 132static inline uint32_t mx28_nand_ecc_chunk_cnt(uint32_t page_data_size)
 133{
 134        return page_data_size / MXS_NAND_CHUNK_DATA_CHUNK_SIZE;
 135}
 136
 137static inline uint32_t mx28_nand_ecc_size_in_bits(uint32_t ecc_strength)
 138{
 139        return ecc_strength * MXS_NAND_BITS_PER_ECC_LEVEL;
 140}
 141
 142static inline uint32_t mx28_nand_get_ecc_strength(uint32_t page_data_size,
 143                                                uint32_t page_oob_size)
 144{
 145        int ecc_strength;
 146
 147        /*
 148         * Determine the ECC layout with the formula:
 149         *      ECC bits per chunk = (total page spare data bits) /
 150         *              (bits per ECC level) / (chunks per page)
 151         * where:
 152         *      total page spare data bits =
 153         *              (page oob size - meta data size) * (bits per byte)
 154         */
 155        ecc_strength = ((page_oob_size - MXS_NAND_METADATA_SIZE) * 8)
 156                        / (MXS_NAND_BITS_PER_ECC_LEVEL *
 157                                mx28_nand_ecc_chunk_cnt(page_data_size));
 158
 159        return round_down(ecc_strength, 2);
 160}
 161
 162static inline uint32_t mx28_nand_get_mark_offset(uint32_t page_data_size,
 163                                                uint32_t ecc_strength)
 164{
 165        uint32_t chunk_data_size_in_bits;
 166        uint32_t chunk_ecc_size_in_bits;
 167        uint32_t chunk_total_size_in_bits;
 168        uint32_t block_mark_chunk_number;
 169        uint32_t block_mark_chunk_bit_offset;
 170        uint32_t block_mark_bit_offset;
 171
 172        chunk_data_size_in_bits = MXS_NAND_CHUNK_DATA_CHUNK_SIZE * 8;
 173        chunk_ecc_size_in_bits  = mx28_nand_ecc_size_in_bits(ecc_strength);
 174
 175        chunk_total_size_in_bits =
 176                        chunk_data_size_in_bits + chunk_ecc_size_in_bits;
 177
 178        /* Compute the bit offset of the block mark within the physical page. */
 179        block_mark_bit_offset = page_data_size * 8;
 180
 181        /* Subtract the metadata bits. */
 182        block_mark_bit_offset -= MXS_NAND_METADATA_SIZE * 8;
 183
 184        /*
 185         * Compute the chunk number (starting at zero) in which the block mark
 186         * appears.
 187         */
 188        block_mark_chunk_number =
 189                        block_mark_bit_offset / chunk_total_size_in_bits;
 190
 191        /*
 192         * Compute the bit offset of the block mark within its chunk, and
 193         * validate it.
 194         */
 195        block_mark_chunk_bit_offset = block_mark_bit_offset -
 196                        (block_mark_chunk_number * chunk_total_size_in_bits);
 197
 198        if (block_mark_chunk_bit_offset > chunk_data_size_in_bits)
 199                return 1;
 200
 201        /*
 202         * Now that we know the chunk number in which the block mark appears,
 203         * we can subtract all the ECC bits that appear before it.
 204         */
 205        block_mark_bit_offset -=
 206                block_mark_chunk_number * chunk_ecc_size_in_bits;
 207
 208        return block_mark_bit_offset;
 209}
 210
 211static inline uint32_t mx28_nand_mark_byte_offset(void)
 212{
 213        uint32_t ecc_strength;
 214        ecc_strength = mx28_nand_get_ecc_strength(nand_writesize, nand_oobsize);
 215        return mx28_nand_get_mark_offset(nand_writesize, ecc_strength) >> 3;
 216}
 217
 218static inline uint32_t mx28_nand_mark_bit_offset(void)
 219{
 220        uint32_t ecc_strength;
 221        ecc_strength = mx28_nand_get_ecc_strength(nand_writesize, nand_oobsize);
 222        return mx28_nand_get_mark_offset(nand_writesize, ecc_strength) & 0x7;
 223}
 224
 225static uint32_t mx28_nand_block_csum(uint8_t *block, uint32_t size)
 226{
 227        uint32_t csum = 0;
 228        int i;
 229
 230        for (i = 0; i < size; i++)
 231                csum += block[i];
 232
 233        return csum ^ 0xffffffff;
 234}
 235
 236static struct mx28_nand_fcb *mx28_nand_get_fcb(uint32_t size)
 237{
 238        struct mx28_nand_fcb *fcb;
 239        uint32_t bcb_size_bytes;
 240        uint32_t stride_size_bytes;
 241        uint32_t bootstream_size_pages;
 242        uint32_t fw1_start_page;
 243        uint32_t fw2_start_page;
 244
 245        fcb = malloc(nand_writesize);
 246        if (!fcb) {
 247                printf("MX28 NAND: Unable to allocate FCB\n");
 248                return NULL;
 249        }
 250
 251        memset(fcb, 0, nand_writesize);
 252
 253        fcb->fingerprint =                      0x20424346;
 254        fcb->version =                          0x01000000;
 255
 256        /*
 257         * FIXME: These here are default values as found in kobs-ng. We should
 258         * probably retrieve the data from NAND or something.
 259         */
 260        fcb->timing.data_setup =                80;
 261        fcb->timing.data_hold =                 60;
 262        fcb->timing.address_setup =             25;
 263        fcb->timing.dsample_time =              6;
 264
 265        fcb->page_data_size =           nand_writesize;
 266        fcb->total_page_size =          nand_writesize + nand_oobsize;
 267        fcb->sectors_per_block =        nand_erasesize / nand_writesize;
 268
 269        fcb->num_ecc_blocks_per_page =  (nand_writesize / 512) - 1;
 270        fcb->ecc_block_0_size =         512;
 271        fcb->ecc_block_n_size =         512;
 272        fcb->metadata_bytes =           10;
 273        fcb->ecc_block_n_ecc_type = mx28_nand_get_ecc_strength(
 274                                        nand_writesize, nand_oobsize) >> 1;
 275        fcb->ecc_block_0_ecc_type = mx28_nand_get_ecc_strength(
 276                                        nand_writesize, nand_oobsize) >> 1;
 277        if (fcb->ecc_block_n_ecc_type == 0) {
 278                printf("MX28 NAND: Unsupported NAND geometry\n");
 279                goto err;
 280        }
 281
 282        fcb->boot_patch =                       0;
 283        fcb->patch_sectors =                    0;
 284
 285        fcb->badblock_marker_byte =     mx28_nand_mark_byte_offset();
 286        fcb->badblock_marker_start_bit = mx28_nand_mark_bit_offset();
 287        fcb->bb_marker_physical_offset = nand_writesize;
 288
 289        stride_size_bytes = STRIDE_PAGES * nand_writesize;
 290        bcb_size_bytes = stride_size_bytes * STRIDE_COUNT;
 291
 292        bootstream_size_pages = (size + (nand_writesize - 1)) /
 293                                        nand_writesize;
 294
 295        fw1_start_page = 2 * bcb_size_bytes / nand_writesize;
 296        fw2_start_page = (2 * bcb_size_bytes + MAX_BOOTSTREAM_SIZE) /
 297                                nand_writesize;
 298
 299        fcb->firmware1_starting_sector =        fw1_start_page;
 300        fcb->firmware2_starting_sector =        fw2_start_page;
 301        fcb->sectors_in_firmware1 =             bootstream_size_pages;
 302        fcb->sectors_in_firmware2 =             bootstream_size_pages;
 303
 304        fcb->dbbt_search_area_start_address =   STRIDE_PAGES * STRIDE_COUNT;
 305
 306        return fcb;
 307
 308err:
 309        free(fcb);
 310        return NULL;
 311}
 312
 313static struct mx28_nand_dbbt *mx28_nand_get_dbbt(void)
 314{
 315        struct mx28_nand_dbbt *dbbt;
 316
 317        dbbt = malloc(nand_writesize);
 318        if (!dbbt) {
 319                printf("MX28 NAND: Unable to allocate DBBT\n");
 320                return NULL;
 321        }
 322
 323        memset(dbbt, 0, nand_writesize);
 324
 325        dbbt->fingerprint       = 0x54424244;
 326        dbbt->version           = 0x1;
 327
 328        return dbbt;
 329}
 330
 331static inline uint8_t mx28_nand_parity_13_8(const uint8_t b)
 332{
 333        uint32_t parity = 0, tmp;
 334
 335        tmp = ((b >> 6) ^ (b >> 5) ^ (b >> 3) ^ (b >> 2)) & 1;
 336        parity |= tmp << 0;
 337
 338        tmp = ((b >> 7) ^ (b >> 5) ^ (b >> 4) ^ (b >> 2) ^ (b >> 1)) & 1;
 339        parity |= tmp << 1;
 340
 341        tmp = ((b >> 7) ^ (b >> 6) ^ (b >> 5) ^ (b >> 1) ^ (b >> 0)) & 1;
 342        parity |= tmp << 2;
 343
 344        tmp = ((b >> 7) ^ (b >> 4) ^ (b >> 3) ^ (b >> 0)) & 1;
 345        parity |= tmp << 3;
 346
 347        tmp = ((b >> 6) ^ (b >> 4) ^ (b >> 3) ^
 348                (b >> 2) ^ (b >> 1) ^ (b >> 0)) & 1;
 349        parity |= tmp << 4;
 350
 351        return parity;
 352}
 353
 354static uint8_t *mx28_nand_fcb_block(struct mx28_nand_fcb *fcb)
 355{
 356        uint8_t *block;
 357        uint8_t *ecc;
 358        int i;
 359
 360        block = malloc(nand_writesize + nand_oobsize);
 361        if (!block) {
 362                printf("MX28 NAND: Unable to allocate FCB block\n");
 363                return NULL;
 364        }
 365
 366        memset(block, 0, nand_writesize + nand_oobsize);
 367
 368        /* Update the FCB checksum */
 369        fcb->checksum = mx28_nand_block_csum(((uint8_t *)fcb) + 4, 508);
 370
 371        /* Figure 12-11. in iMX28RM, rev. 1, says FCB is at offset 12 */
 372        memcpy(block + 12, fcb, sizeof(struct mx28_nand_fcb));
 373
 374        /* ECC is at offset 12 + 512 */
 375        ecc = block + 12 + 512;
 376
 377        /* Compute the ECC parity */
 378        for (i = 0; i < sizeof(struct mx28_nand_fcb); i++)
 379                ecc[i] = mx28_nand_parity_13_8(block[i + 12]);
 380
 381        return block;
 382}
 383
 384static int mx28_nand_write_fcb(struct mx28_nand_fcb *fcb, uint8_t *buf)
 385{
 386        uint32_t offset;
 387        uint8_t *fcbblock;
 388        int ret = 0;
 389        int i;
 390
 391        fcbblock = mx28_nand_fcb_block(fcb);
 392        if (!fcbblock)
 393                return -1;
 394
 395        for (i = 0; i < STRIDE_PAGES * STRIDE_COUNT; i += STRIDE_PAGES) {
 396                offset = i * nand_writesize;
 397                memcpy(buf + offset, fcbblock, nand_writesize + nand_oobsize);
 398                /* Mark the NAND page is OK. */
 399                buf[offset + nand_writesize] = 0xff;
 400        }
 401
 402        free(fcbblock);
 403        return ret;
 404}
 405
 406static int mx28_nand_write_dbbt(struct mx28_nand_dbbt *dbbt, uint8_t *buf)
 407{
 408        uint32_t offset;
 409        int i = STRIDE_PAGES * STRIDE_COUNT;
 410
 411        for (; i < 2 * STRIDE_PAGES * STRIDE_COUNT; i += STRIDE_PAGES) {
 412                offset = i * nand_writesize;
 413                memcpy(buf + offset, dbbt, sizeof(struct mx28_nand_dbbt));
 414        }
 415
 416        return 0;
 417}
 418
 419static int mx28_nand_write_firmware(struct mx28_nand_fcb *fcb, int infd,
 420                                    uint8_t *buf)
 421{
 422        int ret;
 423        off_t size;
 424        uint32_t offset1, offset2;
 425
 426        size = lseek(infd, 0, SEEK_END);
 427        lseek(infd, 0, SEEK_SET);
 428
 429        offset1 = fcb->firmware1_starting_sector * nand_writesize;
 430        offset2 = fcb->firmware2_starting_sector * nand_writesize;
 431
 432        ret = read(infd, buf + offset1, size);
 433        if (ret != size)
 434                return -1;
 435
 436        memcpy(buf + offset2, buf + offset1, size);
 437
 438        return 0;
 439}
 440
 441static void usage(void)
 442{
 443        printf(
 444                "Usage: mxsboot [ops] <type> <infile> <outfile>\n"
 445                "Augment BootStream file with a proper header for i.MX28 boot\n"
 446                "\n"
 447                "  <type>       type of image:\n"
 448                "                 \"nand\" for NAND image\n"
 449                "                 \"sd\" for SD image\n"
 450                "  <infile>     input file, the u-boot.sb bootstream\n"
 451                "  <outfile>    output file, the bootable image\n"
 452                "\n");
 453        printf(
 454                "For NAND boot, these options are accepted:\n"
 455                "  -w <size>    NAND page size\n"
 456                "  -o <size>    NAND OOB size\n"
 457                "  -e <size>    NAND erase size\n"
 458                "\n"
 459                "For SD boot, these options are accepted:\n"
 460                "  -p <sector>  Sector where the SGTL partition starts\n"
 461        );
 462}
 463
 464static int mx28_create_nand_image(int infd, int outfd)
 465{
 466        struct mx28_nand_fcb *fcb;
 467        struct mx28_nand_dbbt *dbbt;
 468        int ret = -1;
 469        uint8_t *buf;
 470        int size;
 471        ssize_t wr_size;
 472
 473        size = nand_writesize * 512 + 2 * MAX_BOOTSTREAM_SIZE;
 474
 475        buf = malloc(size);
 476        if (!buf) {
 477                printf("Can not allocate output buffer of %d bytes\n", size);
 478                goto err0;
 479        }
 480
 481        memset(buf, 0, size);
 482
 483        fcb = mx28_nand_get_fcb(MAX_BOOTSTREAM_SIZE);
 484        if (!fcb) {
 485                printf("Unable to compile FCB\n");
 486                goto err1;
 487        }
 488
 489        dbbt = mx28_nand_get_dbbt();
 490        if (!dbbt) {
 491                printf("Unable to compile DBBT\n");
 492                goto err2;
 493        }
 494
 495        ret = mx28_nand_write_fcb(fcb, buf);
 496        if (ret) {
 497                printf("Unable to write FCB to buffer\n");
 498                goto err3;
 499        }
 500
 501        ret = mx28_nand_write_dbbt(dbbt, buf);
 502        if (ret) {
 503                printf("Unable to write DBBT to buffer\n");
 504                goto err3;
 505        }
 506
 507        ret = mx28_nand_write_firmware(fcb, infd, buf);
 508        if (ret) {
 509                printf("Unable to write firmware to buffer\n");
 510                goto err3;
 511        }
 512
 513        wr_size = write(outfd, buf, size);
 514        if (wr_size != size) {
 515                ret = -1;
 516                goto err3;
 517        }
 518
 519        ret = 0;
 520
 521err3:
 522        free(dbbt);
 523err2:
 524        free(fcb);
 525err1:
 526        free(buf);
 527err0:
 528        return ret;
 529}
 530
 531static int mx28_create_sd_image(int infd, int outfd)
 532{
 533        int ret = -1;
 534        uint32_t *buf;
 535        int size;
 536        off_t fsize;
 537        ssize_t wr_size;
 538        struct mx28_sd_config_block *cb;
 539
 540        fsize = lseek(infd, 0, SEEK_END);
 541        lseek(infd, 0, SEEK_SET);
 542        size = fsize + 4 * 512;
 543
 544        buf = malloc(size);
 545        if (!buf) {
 546                printf("Can not allocate output buffer of %d bytes\n", size);
 547                goto err0;
 548        }
 549
 550        ret = read(infd, (uint8_t *)buf + 4 * 512, fsize);
 551        if (ret != fsize) {
 552                ret = -1;
 553                goto err1;
 554        }
 555
 556        cb = (struct mx28_sd_config_block *)buf;
 557
 558        cb->signature = cpu_to_le32(0x00112233);
 559        cb->primary_boot_tag = cpu_to_le32(0x1);
 560        cb->secondary_boot_tag = cpu_to_le32(0x1);
 561        cb->num_copies = cpu_to_le32(1);
 562        cb->drv_info[0].chip_num = cpu_to_le32(0x0);
 563        cb->drv_info[0].drive_type = cpu_to_le32(0x0);
 564        cb->drv_info[0].tag = cpu_to_le32(0x1);
 565        cb->drv_info[0].first_sector_number = cpu_to_le32(sd_sector + 4);
 566        cb->drv_info[0].sector_count = cpu_to_le32((size - 4) / 512);
 567
 568        wr_size = write(outfd, buf, size);
 569        if (wr_size != size) {
 570                ret = -1;
 571                goto err1;
 572        }
 573
 574        ret = 0;
 575
 576err1:
 577        free(buf);
 578err0:
 579        return ret;
 580}
 581
 582static int parse_ops(int argc, char **argv)
 583{
 584        int i;
 585        int tmp;
 586        char *end;
 587        enum param {
 588                PARAM_WRITE,
 589                PARAM_OOB,
 590                PARAM_ERASE,
 591                PARAM_PART,
 592                PARAM_SD,
 593                PARAM_NAND
 594        };
 595        int type;
 596
 597        if (argc < 4)
 598                return -1;
 599
 600        for (i = 1; i < argc; i++) {
 601                if (!strncmp(argv[i], "-w", 2))
 602                        type = PARAM_WRITE;
 603                else if (!strncmp(argv[i], "-o", 2))
 604                        type = PARAM_OOB;
 605                else if (!strncmp(argv[i], "-e", 2))
 606                        type = PARAM_ERASE;
 607                else if (!strncmp(argv[i], "-p", 2))
 608                        type = PARAM_PART;
 609                else    /* SD/MMC */
 610                        break;
 611
 612                tmp = strtol(argv[++i], &end, 10);
 613                if (tmp % 2)
 614                        return -1;
 615                if (tmp <= 0)
 616                        return -1;
 617
 618                if (type == PARAM_WRITE)
 619                        nand_writesize = tmp;
 620                if (type == PARAM_OOB)
 621                        nand_oobsize = tmp;
 622                if (type == PARAM_ERASE)
 623                        nand_erasesize = tmp;
 624                if (type == PARAM_PART)
 625                        sd_sector = tmp;
 626        }
 627
 628        if (strcmp(argv[i], "sd") && strcmp(argv[i], "nand"))
 629                return -1;
 630
 631        if (i + 3 != argc)
 632                return -1;
 633
 634        return i;
 635}
 636
 637int main(int argc, char **argv)
 638{
 639        int infd, outfd;
 640        int ret = 0;
 641        int offset;
 642
 643        offset = parse_ops(argc, argv);
 644        if (offset < 0) {
 645                usage();
 646                ret = 1;
 647                goto err1;
 648        }
 649
 650        infd = open(argv[offset + 1], O_RDONLY);
 651        if (infd < 0) {
 652                printf("Input BootStream file can not be opened\n");
 653                ret = 2;
 654                goto err1;
 655        }
 656
 657        outfd = open(argv[offset + 2], O_CREAT | O_TRUNC | O_WRONLY,
 658                                        S_IRUSR | S_IWUSR);
 659        if (outfd < 0) {
 660                printf("Output file can not be created\n");
 661                ret = 3;
 662                goto err2;
 663        }
 664
 665        if (!strcmp(argv[offset], "sd"))
 666                ret = mx28_create_sd_image(infd, outfd);
 667        else if (!strcmp(argv[offset], "nand"))
 668                ret = mx28_create_nand_image(infd, outfd);
 669
 670        close(outfd);
 671err2:
 672        close(infd);
 673err1:
 674        return ret;
 675}
 676