uboot/cmd/mtd.c
<<
>>
Prefs
   1// SPDX-License-Identifier:  GPL-2.0+
   2/*
   3 * mtd.c
   4 *
   5 * Generic command to handle basic operations on any memory device.
   6 *
   7 * Copyright: Bootlin, 2018
   8 * Author: Miquèl Raynal <miquel.raynal@bootlin.com>
   9 */
  10
  11#include <command.h>
  12#include <common.h>
  13#include <console.h>
  14#include <malloc.h>
  15#include <mapmem.h>
  16#include <mtd.h>
  17#include <dm/devres.h>
  18#include <linux/err.h>
  19
  20#include <linux/ctype.h>
  21
  22static struct mtd_info *get_mtd_by_name(const char *name)
  23{
  24        struct mtd_info *mtd;
  25
  26        mtd_probe_devices();
  27
  28        mtd = get_mtd_device_nm(name);
  29        if (IS_ERR_OR_NULL(mtd))
  30                printf("MTD device %s not found, ret %ld\n", name,
  31                       PTR_ERR(mtd));
  32
  33        return mtd;
  34}
  35
  36static uint mtd_len_to_pages(struct mtd_info *mtd, u64 len)
  37{
  38        do_div(len, mtd->writesize);
  39
  40        return len;
  41}
  42
  43static bool mtd_is_aligned_with_min_io_size(struct mtd_info *mtd, u64 size)
  44{
  45        return !do_div(size, mtd->writesize);
  46}
  47
  48static bool mtd_is_aligned_with_block_size(struct mtd_info *mtd, u64 size)
  49{
  50        return !do_div(size, mtd->erasesize);
  51}
  52
  53static void mtd_dump_buf(const u8 *buf, uint len, uint offset)
  54{
  55        int i, j;
  56
  57        for (i = 0; i < len; ) {
  58                printf("0x%08x:\t", offset + i);
  59                for (j = 0; j < 8; j++)
  60                        printf("%02x ", buf[i + j]);
  61                printf(" ");
  62                i += 8;
  63                for (j = 0; j < 8; j++)
  64                        printf("%02x ", buf[i + j]);
  65                printf("\n");
  66                i += 8;
  67        }
  68}
  69
  70static void mtd_dump_device_buf(struct mtd_info *mtd, u64 start_off,
  71                                const u8 *buf, u64 len, bool woob)
  72{
  73        bool has_pages = mtd->type == MTD_NANDFLASH ||
  74                mtd->type == MTD_MLCNANDFLASH;
  75        int npages = mtd_len_to_pages(mtd, len);
  76        uint page;
  77
  78        if (has_pages) {
  79                for (page = 0; page < npages; page++) {
  80                        u64 data_off = page * mtd->writesize;
  81
  82                        printf("\nDump %d data bytes from 0x%08llx:\n",
  83                               mtd->writesize, start_off + data_off);
  84                        mtd_dump_buf(&buf[data_off],
  85                                     mtd->writesize, start_off + data_off);
  86
  87                        if (woob) {
  88                                u64 oob_off = page * mtd->oobsize;
  89
  90                                printf("Dump %d OOB bytes from page at 0x%08llx:\n",
  91                                       mtd->oobsize, start_off + data_off);
  92                                mtd_dump_buf(&buf[len + oob_off],
  93                                             mtd->oobsize, 0);
  94                        }
  95                }
  96        } else {
  97                printf("\nDump %lld data bytes from 0x%llx:\n",
  98                       len, start_off);
  99                mtd_dump_buf(buf, len, start_off);
 100        }
 101}
 102
 103static void mtd_show_parts(struct mtd_info *mtd, int level)
 104{
 105        struct mtd_info *part;
 106        int i;
 107
 108        list_for_each_entry(part, &mtd->partitions, node) {
 109                for (i = 0; i < level; i++)
 110                        printf("\t");
 111                printf("  - 0x%012llx-0x%012llx : \"%s\"\n",
 112                       part->offset, part->offset + part->size, part->name);
 113
 114                mtd_show_parts(part, level + 1);
 115        }
 116}
 117
 118static void mtd_show_device(struct mtd_info *mtd)
 119{
 120        /* Device */
 121        printf("* %s\n", mtd->name);
 122#if defined(CONFIG_DM)
 123        if (mtd->dev) {
 124                printf("  - device: %s\n", mtd->dev->name);
 125                printf("  - parent: %s\n", mtd->dev->parent->name);
 126                printf("  - driver: %s\n", mtd->dev->driver->name);
 127        }
 128#endif
 129        if (IS_ENABLED(CONFIG_OF_CONTROL) && mtd->dev) {
 130                char buf[256];
 131                int res;
 132
 133                res = ofnode_get_path(mtd_get_ofnode(mtd), buf, 256);
 134                printf("  - path: %s\n", res == 0 ? buf : "unavailable");
 135        }
 136
 137        /* MTD device information */
 138        printf("  - type: ");
 139        switch (mtd->type) {
 140        case MTD_RAM:
 141                printf("RAM\n");
 142                break;
 143        case MTD_ROM:
 144                printf("ROM\n");
 145                break;
 146        case MTD_NORFLASH:
 147                printf("NOR flash\n");
 148                break;
 149        case MTD_NANDFLASH:
 150                printf("NAND flash\n");
 151                break;
 152        case MTD_DATAFLASH:
 153                printf("Data flash\n");
 154                break;
 155        case MTD_UBIVOLUME:
 156                printf("UBI volume\n");
 157                break;
 158        case MTD_MLCNANDFLASH:
 159                printf("MLC NAND flash\n");
 160                break;
 161        case MTD_ABSENT:
 162        default:
 163                printf("Unknown\n");
 164                break;
 165        }
 166
 167        printf("  - block size: 0x%x bytes\n", mtd->erasesize);
 168        printf("  - min I/O: 0x%x bytes\n", mtd->writesize);
 169
 170        if (mtd->oobsize) {
 171                printf("  - OOB size: %u bytes\n", mtd->oobsize);
 172                printf("  - OOB available: %u bytes\n", mtd->oobavail);
 173        }
 174
 175        if (mtd->ecc_strength) {
 176                printf("  - ECC strength: %u bits\n", mtd->ecc_strength);
 177                printf("  - ECC step size: %u bytes\n", mtd->ecc_step_size);
 178                printf("  - bitflip threshold: %u bits\n",
 179                       mtd->bitflip_threshold);
 180        }
 181
 182        printf("  - 0x%012llx-0x%012llx : \"%s\"\n",
 183               mtd->offset, mtd->offset + mtd->size, mtd->name);
 184
 185        /* MTD partitions, if any */
 186        mtd_show_parts(mtd, 1);
 187}
 188
 189/* Logic taken from fs/ubifs/recovery.c:is_empty() */
 190static bool mtd_oob_write_is_empty(struct mtd_oob_ops *op)
 191{
 192        int i;
 193
 194        for (i = 0; i < op->len; i++)
 195                if (op->datbuf[i] != 0xff)
 196                        return false;
 197
 198        for (i = 0; i < op->ooblen; i++)
 199                if (op->oobbuf[i] != 0xff)
 200                        return false;
 201
 202        return true;
 203}
 204
 205static int do_mtd_list(struct cmd_tbl *cmdtp, int flag, int argc,
 206                       char *const argv[])
 207{
 208        struct mtd_info *mtd;
 209        int dev_nb = 0;
 210
 211        /* Ensure all devices (and their partitions) are probed */
 212        mtd_probe_devices();
 213
 214        printf("List of MTD devices:\n");
 215        mtd_for_each_device(mtd) {
 216                if (!mtd_is_partition(mtd))
 217                        mtd_show_device(mtd);
 218
 219                dev_nb++;
 220        }
 221
 222        if (!dev_nb) {
 223                printf("No MTD device found\n");
 224                return CMD_RET_FAILURE;
 225        }
 226
 227        return CMD_RET_SUCCESS;
 228}
 229
 230static int mtd_special_write_oob(struct mtd_info *mtd, u64 off,
 231                                 struct mtd_oob_ops *io_op,
 232                                 bool write_empty_pages, bool woob)
 233{
 234        int ret = 0;
 235
 236        /*
 237         * By default, do not write an empty page.
 238         * Skip it by simulating a successful write.
 239         */
 240        if (!write_empty_pages && mtd_oob_write_is_empty(io_op)) {
 241                io_op->retlen = mtd->writesize;
 242                io_op->oobretlen = woob ? mtd->oobsize : 0;
 243        } else {
 244                ret = mtd_write_oob(mtd, off, io_op);
 245        }
 246
 247        return ret;
 248}
 249
 250static int do_mtd_io(struct cmd_tbl *cmdtp, int flag, int argc,
 251                     char *const argv[])
 252{
 253        bool dump, read, raw, woob, write_empty_pages, has_pages = false;
 254        u64 start_off, off, len, remaining, default_len;
 255        struct mtd_oob_ops io_op = {};
 256        uint user_addr = 0, npages;
 257        const char *cmd = argv[0];
 258        struct mtd_info *mtd;
 259        u32 oob_len;
 260        u8 *buf;
 261        int ret;
 262
 263        if (argc < 2)
 264                return CMD_RET_USAGE;
 265
 266        mtd = get_mtd_by_name(argv[1]);
 267        if (IS_ERR_OR_NULL(mtd))
 268                return CMD_RET_FAILURE;
 269
 270        if (mtd->type == MTD_NANDFLASH || mtd->type == MTD_MLCNANDFLASH)
 271                has_pages = true;
 272
 273        dump = !strncmp(cmd, "dump", 4);
 274        read = dump || !strncmp(cmd, "read", 4);
 275        raw = strstr(cmd, ".raw");
 276        woob = strstr(cmd, ".oob");
 277        write_empty_pages = !has_pages || strstr(cmd, ".dontskipff");
 278
 279        argc -= 2;
 280        argv += 2;
 281
 282        if (!dump) {
 283                if (!argc) {
 284                        ret = CMD_RET_USAGE;
 285                        goto out_put_mtd;
 286                }
 287
 288                user_addr = hextoul(argv[0], NULL);
 289                argc--;
 290                argv++;
 291        }
 292
 293        start_off = argc > 0 ? hextoul(argv[0], NULL) : 0;
 294        if (!mtd_is_aligned_with_min_io_size(mtd, start_off)) {
 295                printf("Offset not aligned with a page (0x%x)\n",
 296                       mtd->writesize);
 297                ret = CMD_RET_FAILURE;
 298                goto out_put_mtd;
 299        }
 300
 301        default_len = dump ? mtd->writesize : mtd->size;
 302        len = argc > 1 ? hextoul(argv[1], NULL) : default_len;
 303        if (!mtd_is_aligned_with_min_io_size(mtd, len)) {
 304                len = round_up(len, mtd->writesize);
 305                printf("Size not on a page boundary (0x%x), rounding to 0x%llx\n",
 306                       mtd->writesize, len);
 307        }
 308
 309        remaining = len;
 310        npages = mtd_len_to_pages(mtd, len);
 311        oob_len = woob ? npages * mtd->oobsize : 0;
 312
 313        if (dump)
 314                buf = kmalloc(len + oob_len, GFP_KERNEL);
 315        else
 316                buf = map_sysmem(user_addr, 0);
 317
 318        if (!buf) {
 319                printf("Could not map/allocate the user buffer\n");
 320                ret = CMD_RET_FAILURE;
 321                goto out_put_mtd;
 322        }
 323
 324        if (has_pages)
 325                printf("%s %lld byte(s) (%d page(s)) at offset 0x%08llx%s%s%s\n",
 326                       read ? "Reading" : "Writing", len, npages, start_off,
 327                       raw ? " [raw]" : "", woob ? " [oob]" : "",
 328                       !read && write_empty_pages ? " [dontskipff]" : "");
 329        else
 330                printf("%s %lld byte(s) at offset 0x%08llx\n",
 331                       read ? "Reading" : "Writing", len, start_off);
 332
 333        io_op.mode = raw ? MTD_OPS_RAW : MTD_OPS_AUTO_OOB;
 334        io_op.len = has_pages ? mtd->writesize : len;
 335        io_op.ooblen = woob ? mtd->oobsize : 0;
 336        io_op.datbuf = buf;
 337        io_op.oobbuf = woob ? &buf[len] : NULL;
 338
 339        /* Search for the first good block after the given offset */
 340        off = start_off;
 341        while (mtd_block_isbad(mtd, off))
 342                off += mtd->erasesize;
 343
 344        /* Loop over the pages to do the actual read/write */
 345        while (remaining) {
 346                /* Skip the block if it is bad */
 347                if (mtd_is_aligned_with_block_size(mtd, off) &&
 348                    mtd_block_isbad(mtd, off)) {
 349                        off += mtd->erasesize;
 350                        continue;
 351                }
 352
 353                if (read)
 354                        ret = mtd_read_oob(mtd, off, &io_op);
 355                else
 356                        ret = mtd_special_write_oob(mtd, off, &io_op,
 357                                                    write_empty_pages, woob);
 358
 359                if (ret) {
 360                        printf("Failure while %s at offset 0x%llx\n",
 361                               read ? "reading" : "writing", off);
 362                        break;
 363                }
 364
 365                off += io_op.retlen;
 366                remaining -= io_op.retlen;
 367                io_op.datbuf += io_op.retlen;
 368                io_op.oobbuf += io_op.oobretlen;
 369        }
 370
 371        if (!ret && dump)
 372                mtd_dump_device_buf(mtd, start_off, buf, len, woob);
 373
 374        if (dump)
 375                kfree(buf);
 376        else
 377                unmap_sysmem(buf);
 378
 379        if (ret) {
 380                printf("%s on %s failed with error %d\n",
 381                       read ? "Read" : "Write", mtd->name, ret);
 382                ret = CMD_RET_FAILURE;
 383        } else {
 384                ret = CMD_RET_SUCCESS;
 385        }
 386
 387out_put_mtd:
 388        put_mtd_device(mtd);
 389
 390        return ret;
 391}
 392
 393static int do_mtd_erase(struct cmd_tbl *cmdtp, int flag, int argc,
 394                        char *const argv[])
 395{
 396        struct erase_info erase_op = {};
 397        struct mtd_info *mtd;
 398        u64 off, len;
 399        bool scrub;
 400        int ret = 0;
 401
 402        if (argc < 2)
 403                return CMD_RET_USAGE;
 404
 405        mtd = get_mtd_by_name(argv[1]);
 406        if (IS_ERR_OR_NULL(mtd))
 407                return CMD_RET_FAILURE;
 408
 409        scrub = strstr(argv[0], ".dontskipbad");
 410
 411        argc -= 2;
 412        argv += 2;
 413
 414        off = argc > 0 ? hextoul(argv[0], NULL) : 0;
 415        len = argc > 1 ? hextoul(argv[1], NULL) : mtd->size;
 416
 417        if (!mtd_is_aligned_with_block_size(mtd, off)) {
 418                printf("Offset not aligned with a block (0x%x)\n",
 419                       mtd->erasesize);
 420                ret = CMD_RET_FAILURE;
 421                goto out_put_mtd;
 422        }
 423
 424        if (!mtd_is_aligned_with_block_size(mtd, len)) {
 425                printf("Size not a multiple of a block (0x%x)\n",
 426                       mtd->erasesize);
 427                ret = CMD_RET_FAILURE;
 428                goto out_put_mtd;
 429        }
 430
 431        printf("Erasing 0x%08llx ... 0x%08llx (%d eraseblock(s))\n",
 432               off, off + len - 1, mtd_div_by_eb(len, mtd));
 433
 434        erase_op.mtd = mtd;
 435        erase_op.addr = off;
 436        erase_op.len = mtd->erasesize;
 437        erase_op.scrub = scrub;
 438
 439        while (len) {
 440                ret = mtd_erase(mtd, &erase_op);
 441
 442                if (ret) {
 443                        /* Abort if its not a bad block error */
 444                        if (ret != -EIO)
 445                                break;
 446                        printf("Skipping bad block at 0x%08llx\n",
 447                               erase_op.addr);
 448                }
 449
 450                len -= mtd->erasesize;
 451                erase_op.addr += mtd->erasesize;
 452        }
 453
 454        if (ret && ret != -EIO)
 455                ret = CMD_RET_FAILURE;
 456        else
 457                ret = CMD_RET_SUCCESS;
 458
 459out_put_mtd:
 460        put_mtd_device(mtd);
 461
 462        return ret;
 463}
 464
 465static int do_mtd_bad(struct cmd_tbl *cmdtp, int flag, int argc,
 466                      char *const argv[])
 467{
 468        struct mtd_info *mtd;
 469        loff_t off;
 470
 471        if (argc < 2)
 472                return CMD_RET_USAGE;
 473
 474        mtd = get_mtd_by_name(argv[1]);
 475        if (IS_ERR_OR_NULL(mtd))
 476                return CMD_RET_FAILURE;
 477
 478        if (!mtd_can_have_bb(mtd)) {
 479                printf("Only NAND-based devices can have bad blocks\n");
 480                goto out_put_mtd;
 481        }
 482
 483        printf("MTD device %s bad blocks list:\n", mtd->name);
 484        for (off = 0; off < mtd->size; off += mtd->erasesize) {
 485                if (mtd_block_isbad(mtd, off))
 486                        printf("\t0x%08llx\n", off);
 487        }
 488
 489out_put_mtd:
 490        put_mtd_device(mtd);
 491
 492        return CMD_RET_SUCCESS;
 493}
 494
 495#ifdef CONFIG_AUTO_COMPLETE
 496static int mtd_name_complete(int argc, char *const argv[], char last_char,
 497                             int maxv, char *cmdv[])
 498{
 499        int len = 0, n_found = 0;
 500        struct mtd_info *mtd;
 501
 502        argc--;
 503        argv++;
 504
 505        if (argc > 1 ||
 506            (argc == 1 && (last_char == '\0' || isblank(last_char))))
 507                return 0;
 508
 509        if (argc)
 510                len = strlen(argv[0]);
 511
 512        mtd_for_each_device(mtd) {
 513                if (argc &&
 514                    (len > strlen(mtd->name) ||
 515                     strncmp(argv[0], mtd->name, len)))
 516                        continue;
 517
 518                if (n_found >= maxv - 2) {
 519                        cmdv[n_found++] = "...";
 520                        break;
 521                }
 522
 523                cmdv[n_found++] = mtd->name;
 524        }
 525
 526        cmdv[n_found] = NULL;
 527
 528        return n_found;
 529}
 530#endif /* CONFIG_AUTO_COMPLETE */
 531
 532#ifdef CONFIG_SYS_LONGHELP
 533static char mtd_help_text[] =
 534        "- generic operations on memory technology devices\n\n"
 535        "mtd list\n"
 536        "mtd read[.raw][.oob]                  <name> <addr> [<off> [<size>]]\n"
 537        "mtd dump[.raw][.oob]                  <name>        [<off> [<size>]]\n"
 538        "mtd write[.raw][.oob][.dontskipff]    <name> <addr> [<off> [<size>]]\n"
 539        "mtd erase[.dontskipbad]               <name>        [<off> [<size>]]\n"
 540        "\n"
 541        "Specific functions:\n"
 542        "mtd bad                               <name>\n"
 543        "\n"
 544        "With:\n"
 545        "\t<name>: NAND partition/chip name (or corresponding DM device name or OF path)\n"
 546        "\t<addr>: user address from/to which data will be retrieved/stored\n"
 547        "\t<off>: offset in <name> in bytes (default: start of the part)\n"
 548        "\t\t* must be block-aligned for erase\n"
 549        "\t\t* must be page-aligned otherwise\n"
 550        "\t<size>: length of the operation in bytes (default: the entire device)\n"
 551        "\t\t* must be a multiple of a block for erase\n"
 552        "\t\t* must be a multiple of a page otherwise (special case: default is a page with dump)\n"
 553        "\n"
 554        "The .dontskipff option forces writing empty pages, don't use it if unsure.\n";
 555#endif
 556
 557U_BOOT_CMD_WITH_SUBCMDS(mtd, "MTD utils", mtd_help_text,
 558                U_BOOT_SUBCMD_MKENT(list, 1, 1, do_mtd_list),
 559                U_BOOT_SUBCMD_MKENT_COMPLETE(read, 5, 0, do_mtd_io,
 560                                             mtd_name_complete),
 561                U_BOOT_SUBCMD_MKENT_COMPLETE(write, 5, 0, do_mtd_io,
 562                                             mtd_name_complete),
 563                U_BOOT_SUBCMD_MKENT_COMPLETE(dump, 4, 0, do_mtd_io,
 564                                             mtd_name_complete),
 565                U_BOOT_SUBCMD_MKENT_COMPLETE(erase, 4, 0, do_mtd_erase,
 566                                             mtd_name_complete),
 567                U_BOOT_SUBCMD_MKENT_COMPLETE(bad, 2, 1, do_mtd_bad,
 568                                             mtd_name_complete));
 569