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
  18static uint mtd_len_to_pages(struct mtd_info *mtd, u64 len)
  19{
  20        do_div(len, mtd->writesize);
  21
  22        return len;
  23}
  24
  25static bool mtd_is_aligned_with_min_io_size(struct mtd_info *mtd, u64 size)
  26{
  27        return !do_div(size, mtd->writesize);
  28}
  29
  30static bool mtd_is_aligned_with_block_size(struct mtd_info *mtd, u64 size)
  31{
  32        return !do_div(size, mtd->erasesize);
  33}
  34
  35static void mtd_dump_buf(const u8 *buf, uint len, uint offset)
  36{
  37        int i, j;
  38
  39        for (i = 0; i < len; ) {
  40                printf("0x%08x:\t", offset + i);
  41                for (j = 0; j < 8; j++)
  42                        printf("%02x ", buf[i + j]);
  43                printf(" ");
  44                i += 8;
  45                for (j = 0; j < 8; j++)
  46                        printf("%02x ", buf[i + j]);
  47                printf("\n");
  48                i += 8;
  49        }
  50}
  51
  52static void mtd_dump_device_buf(struct mtd_info *mtd, u64 start_off,
  53                                const u8 *buf, u64 len, bool woob)
  54{
  55        bool has_pages = mtd->type == MTD_NANDFLASH ||
  56                mtd->type == MTD_MLCNANDFLASH;
  57        int npages = mtd_len_to_pages(mtd, len);
  58        uint page;
  59
  60        if (has_pages) {
  61                for (page = 0; page < npages; page++) {
  62                        u64 data_off = page * mtd->writesize;
  63
  64                        printf("\nDump %d data bytes from 0x%08llx:\n",
  65                               mtd->writesize, start_off + data_off);
  66                        mtd_dump_buf(&buf[data_off],
  67                                     mtd->writesize, start_off + data_off);
  68
  69                        if (woob) {
  70                                u64 oob_off = page * mtd->oobsize;
  71
  72                                printf("Dump %d OOB bytes from page at 0x%08llx:\n",
  73                                       mtd->oobsize, start_off + data_off);
  74                                mtd_dump_buf(&buf[len + oob_off],
  75                                             mtd->oobsize, 0);
  76                        }
  77                }
  78        } else {
  79                printf("\nDump %lld data bytes from 0x%llx:\n",
  80                       len, start_off);
  81                mtd_dump_buf(buf, len, start_off);
  82        }
  83}
  84
  85static void mtd_show_parts(struct mtd_info *mtd, int level)
  86{
  87        struct mtd_info *part;
  88        int i;
  89
  90        list_for_each_entry(part, &mtd->partitions, node) {
  91                for (i = 0; i < level; i++)
  92                        printf("\t");
  93                printf("  - 0x%012llx-0x%012llx : \"%s\"\n",
  94                       part->offset, part->offset + part->size, part->name);
  95
  96                mtd_show_parts(part, level + 1);
  97        }
  98}
  99
 100static void mtd_show_device(struct mtd_info *mtd)
 101{
 102        /* Device */
 103        printf("* %s\n", mtd->name);
 104#if defined(CONFIG_DM)
 105        if (mtd->dev) {
 106                printf("  - device: %s\n", mtd->dev->name);
 107                printf("  - parent: %s\n", mtd->dev->parent->name);
 108                printf("  - driver: %s\n", mtd->dev->driver->name);
 109        }
 110#endif
 111
 112        /* MTD device information */
 113        printf("  - type: ");
 114        switch (mtd->type) {
 115        case MTD_RAM:
 116                printf("RAM\n");
 117                break;
 118        case MTD_ROM:
 119                printf("ROM\n");
 120                break;
 121        case MTD_NORFLASH:
 122                printf("NOR flash\n");
 123                break;
 124        case MTD_NANDFLASH:
 125                printf("NAND flash\n");
 126                break;
 127        case MTD_DATAFLASH:
 128                printf("Data flash\n");
 129                break;
 130        case MTD_UBIVOLUME:
 131                printf("UBI volume\n");
 132                break;
 133        case MTD_MLCNANDFLASH:
 134                printf("MLC NAND flash\n");
 135                break;
 136        case MTD_ABSENT:
 137        default:
 138                printf("Unknown\n");
 139                break;
 140        }
 141
 142        printf("  - block size: 0x%x bytes\n", mtd->erasesize);
 143        printf("  - min I/O: 0x%x bytes\n", mtd->writesize);
 144
 145        if (mtd->oobsize) {
 146                printf("  - OOB size: %u bytes\n", mtd->oobsize);
 147                printf("  - OOB available: %u bytes\n", mtd->oobavail);
 148        }
 149
 150        if (mtd->ecc_strength) {
 151                printf("  - ECC strength: %u bits\n", mtd->ecc_strength);
 152                printf("  - ECC step size: %u bytes\n", mtd->ecc_step_size);
 153                printf("  - bitflip threshold: %u bits\n",
 154                       mtd->bitflip_threshold);
 155        }
 156
 157        printf("  - 0x%012llx-0x%012llx : \"%s\"\n",
 158               mtd->offset, mtd->offset + mtd->size, mtd->name);
 159
 160        /* MTD partitions, if any */
 161        mtd_show_parts(mtd, 1);
 162}
 163
 164/* Logic taken from fs/ubifs/recovery.c:is_empty() */
 165static bool mtd_oob_write_is_empty(struct mtd_oob_ops *op)
 166{
 167        int i;
 168
 169        for (i = 0; i < op->len; i++)
 170                if (op->datbuf[i] != 0xff)
 171                        return false;
 172
 173        for (i = 0; i < op->ooblen; i++)
 174                if (op->oobbuf[i] != 0xff)
 175                        return false;
 176
 177        return true;
 178}
 179
 180static int do_mtd_list(void)
 181{
 182        struct mtd_info *mtd;
 183        int dev_nb = 0;
 184
 185        /* Ensure all devices (and their partitions) are probed */
 186        mtd_probe_devices();
 187
 188        printf("List of MTD devices:\n");
 189        mtd_for_each_device(mtd) {
 190                if (!mtd_is_partition(mtd))
 191                        mtd_show_device(mtd);
 192
 193                dev_nb++;
 194        }
 195
 196        if (!dev_nb) {
 197                printf("No MTD device found\n");
 198                return CMD_RET_FAILURE;
 199        }
 200
 201        return CMD_RET_SUCCESS;
 202}
 203
 204static int mtd_special_write_oob(struct mtd_info *mtd, u64 off,
 205                                 struct mtd_oob_ops *io_op,
 206                                 bool write_empty_pages, bool woob)
 207{
 208        int ret = 0;
 209
 210        /*
 211         * By default, do not write an empty page.
 212         * Skip it by simulating a successful write.
 213         */
 214        if (!write_empty_pages && mtd_oob_write_is_empty(io_op)) {
 215                io_op->retlen = mtd->writesize;
 216                io_op->oobretlen = woob ? mtd->oobsize : 0;
 217        } else {
 218                ret = mtd_write_oob(mtd, off, io_op);
 219        }
 220
 221        return ret;
 222}
 223
 224static int do_mtd(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 225{
 226        struct mtd_info *mtd;
 227        const char *cmd;
 228        char *mtd_name;
 229
 230        /* All MTD commands need at least two arguments */
 231        if (argc < 2)
 232                return CMD_RET_USAGE;
 233
 234        /* Parse the command name and its optional suffixes */
 235        cmd = argv[1];
 236
 237        /* List the MTD devices if that is what the user wants */
 238        if (strcmp(cmd, "list") == 0)
 239                return do_mtd_list();
 240
 241        /*
 242         * The remaining commands require also at least a device ID.
 243         * Check the selected device is valid. Ensure it is probed.
 244         */
 245        if (argc < 3)
 246                return CMD_RET_USAGE;
 247
 248        mtd_name = argv[2];
 249        mtd_probe_devices();
 250        mtd = get_mtd_device_nm(mtd_name);
 251        if (IS_ERR_OR_NULL(mtd)) {
 252                printf("MTD device %s not found, ret %ld\n",
 253                       mtd_name, PTR_ERR(mtd));
 254                return CMD_RET_FAILURE;
 255        }
 256        put_mtd_device(mtd);
 257
 258        argc -= 3;
 259        argv += 3;
 260
 261        /* Do the parsing */
 262        if (!strncmp(cmd, "read", 4) || !strncmp(cmd, "dump", 4) ||
 263            !strncmp(cmd, "write", 5)) {
 264                bool has_pages = mtd->type == MTD_NANDFLASH ||
 265                                 mtd->type == MTD_MLCNANDFLASH;
 266                bool dump, read, raw, woob, write_empty_pages;
 267                struct mtd_oob_ops io_op = {};
 268                uint user_addr = 0, npages;
 269                u64 start_off, off, len, remaining, default_len;
 270                u32 oob_len;
 271                u8 *buf;
 272                int ret;
 273
 274                dump = !strncmp(cmd, "dump", 4);
 275                read = dump || !strncmp(cmd, "read", 4);
 276                raw = strstr(cmd, ".raw");
 277                woob = strstr(cmd, ".oob");
 278                write_empty_pages = !has_pages || strstr(cmd, ".dontskipff");
 279
 280                if (!dump) {
 281                        if (!argc)
 282                                return CMD_RET_USAGE;
 283
 284                        user_addr = simple_strtoul(argv[0], NULL, 16);
 285                        argc--;
 286                        argv++;
 287                }
 288
 289                start_off = argc > 0 ? simple_strtoul(argv[0], NULL, 16) : 0;
 290                if (!mtd_is_aligned_with_min_io_size(mtd, start_off)) {
 291                        printf("Offset not aligned with a page (0x%x)\n",
 292                               mtd->writesize);
 293                        return CMD_RET_FAILURE;
 294                }
 295
 296                default_len = dump ? mtd->writesize : mtd->size;
 297                len = argc > 1 ? simple_strtoul(argv[1], NULL, 16) :
 298                                 default_len;
 299                if (!mtd_is_aligned_with_min_io_size(mtd, len)) {
 300                        len = round_up(len, mtd->writesize);
 301                        printf("Size not on a page boundary (0x%x), rounding to 0x%llx\n",
 302                               mtd->writesize, len);
 303                }
 304
 305                remaining = len;
 306                npages = mtd_len_to_pages(mtd, len);
 307                oob_len = woob ? npages * mtd->oobsize : 0;
 308
 309                if (dump)
 310                        buf = kmalloc(len + oob_len, GFP_KERNEL);
 311                else
 312                        buf = map_sysmem(user_addr, 0);
 313
 314                if (!buf) {
 315                        printf("Could not map/allocate the user buffer\n");
 316                        return CMD_RET_FAILURE;
 317                }
 318
 319                if (has_pages)
 320                        printf("%s %lld byte(s) (%d page(s)) at offset 0x%08llx%s%s%s\n",
 321                               read ? "Reading" : "Writing", len, npages, start_off,
 322                               raw ? " [raw]" : "", woob ? " [oob]" : "",
 323                               !read && write_empty_pages ? " [dontskipff]" : "");
 324                else
 325                        printf("%s %lld byte(s) at offset 0x%08llx\n",
 326                               read ? "Reading" : "Writing", len, start_off);
 327
 328                io_op.mode = raw ? MTD_OPS_RAW : MTD_OPS_AUTO_OOB;
 329                io_op.len = has_pages ? mtd->writesize : len;
 330                io_op.ooblen = woob ? mtd->oobsize : 0;
 331                io_op.datbuf = buf;
 332                io_op.oobbuf = woob ? &buf[len] : NULL;
 333
 334                /* Search for the first good block after the given offset */
 335                off = start_off;
 336                while (mtd_block_isbad(mtd, off))
 337                        off += mtd->erasesize;
 338
 339                /* Loop over the pages to do the actual read/write */
 340                while (remaining) {
 341                        /* Skip the block if it is bad */
 342                        if (mtd_is_aligned_with_block_size(mtd, off) &&
 343                            mtd_block_isbad(mtd, off)) {
 344                                off += mtd->erasesize;
 345                                continue;
 346                        }
 347
 348                        if (read)
 349                                ret = mtd_read_oob(mtd, off, &io_op);
 350                        else
 351                                ret = mtd_special_write_oob(mtd, off, &io_op,
 352                                                            write_empty_pages,
 353                                                            woob);
 354
 355                        if (ret) {
 356                                printf("Failure while %s at offset 0x%llx\n",
 357                                       read ? "reading" : "writing", off);
 358                                return CMD_RET_FAILURE;
 359                        }
 360
 361                        off += io_op.retlen;
 362                        remaining -= io_op.retlen;
 363                        io_op.datbuf += io_op.retlen;
 364                        io_op.oobbuf += io_op.oobretlen;
 365                }
 366
 367                if (!ret && dump)
 368                        mtd_dump_device_buf(mtd, start_off, buf, len, woob);
 369
 370                if (dump)
 371                        kfree(buf);
 372                else
 373                        unmap_sysmem(buf);
 374
 375                if (ret) {
 376                        printf("%s on %s failed with error %d\n",
 377                               read ? "Read" : "Write", mtd->name, ret);
 378                        return CMD_RET_FAILURE;
 379                }
 380
 381        } else if (!strcmp(cmd, "erase")) {
 382                bool scrub = strstr(cmd, ".dontskipbad");
 383                struct erase_info erase_op = {};
 384                u64 off, len;
 385                int ret;
 386
 387                off = argc > 0 ? simple_strtoul(argv[0], NULL, 16) : 0;
 388                len = argc > 1 ? simple_strtoul(argv[1], NULL, 16) : mtd->size;
 389
 390                if (!mtd_is_aligned_with_block_size(mtd, off)) {
 391                        printf("Offset not aligned with a block (0x%x)\n",
 392                               mtd->erasesize);
 393                        return CMD_RET_FAILURE;
 394                }
 395
 396                if (!mtd_is_aligned_with_block_size(mtd, len)) {
 397                        printf("Size not a multiple of a block (0x%x)\n",
 398                               mtd->erasesize);
 399                        return CMD_RET_FAILURE;
 400                }
 401
 402                printf("Erasing 0x%08llx ... 0x%08llx (%d eraseblock(s))\n",
 403                       off, off + len - 1, mtd_div_by_eb(len, mtd));
 404
 405                erase_op.mtd = mtd;
 406                erase_op.addr = off;
 407                erase_op.len = len;
 408                erase_op.scrub = scrub;
 409
 410                while (erase_op.len) {
 411                        ret = mtd_erase(mtd, &erase_op);
 412
 413                        /* Abort if its not a bad block error */
 414                        if (ret != -EIO)
 415                                break;
 416
 417                        printf("Skipping bad block at 0x%08llx\n",
 418                               erase_op.fail_addr);
 419
 420                        /* Skip bad block and continue behind it */
 421                        erase_op.len -= erase_op.fail_addr - erase_op.addr;
 422                        erase_op.len -= mtd->erasesize;
 423                        erase_op.addr = erase_op.fail_addr + mtd->erasesize;
 424                }
 425
 426                if (ret && ret != -EIO)
 427                        return CMD_RET_FAILURE;
 428        } else if (!strcmp(cmd, "bad")) {
 429                loff_t off;
 430
 431                if (!mtd_can_have_bb(mtd)) {
 432                        printf("Only NAND-based devices can have bad blocks\n");
 433                        return CMD_RET_SUCCESS;
 434                }
 435
 436                printf("MTD device %s bad blocks list:\n", mtd->name);
 437                for (off = 0; off < mtd->size; off += mtd->erasesize)
 438                        if (mtd_block_isbad(mtd, off))
 439                                printf("\t0x%08llx\n", off);
 440        } else {
 441                return CMD_RET_USAGE;
 442        }
 443
 444        return CMD_RET_SUCCESS;
 445}
 446
 447static char mtd_help_text[] =
 448#ifdef CONFIG_SYS_LONGHELP
 449        "- generic operations on memory technology devices\n\n"
 450        "mtd list\n"
 451        "mtd read[.raw][.oob]                  <name> <addr> [<off> [<size>]]\n"
 452        "mtd dump[.raw][.oob]                  <name>        [<off> [<size>]]\n"
 453        "mtd write[.raw][.oob][.dontskipff]    <name> <addr> [<off> [<size>]]\n"
 454        "mtd erase[.dontskipbad]               <name>        [<off> [<size>]]\n"
 455        "\n"
 456        "Specific functions:\n"
 457        "mtd bad                               <name>\n"
 458        "\n"
 459        "With:\n"
 460        "\t<name>: NAND partition/chip name\n"
 461        "\t<addr>: user address from/to which data will be retrieved/stored\n"
 462        "\t<off>: offset in <name> in bytes (default: start of the part)\n"
 463        "\t\t* must be block-aligned for erase\n"
 464        "\t\t* must be page-aligned otherwise\n"
 465        "\t<size>: length of the operation in bytes (default: the entire device)\n"
 466        "\t\t* must be a multiple of a block for erase\n"
 467        "\t\t* must be a multiple of a page otherwise (special case: default is a page with dump)\n"
 468        "\n"
 469        "The .dontskipff option forces writing empty pages, don't use it if unsure.\n"
 470#endif
 471        "";
 472
 473U_BOOT_CMD(mtd, 10, 1, do_mtd, "MTD utils", mtd_help_text);
 474