mtd.c (592cd5defd4f71d34ffcbd8dd3326bc10f662e20) mtd.c (9671243e8d10defb06f2ea24fac138c87697d7fc)
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
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
18#include <linux/ctype.h>
19
20static struct mtd_info *get_mtd_by_name(const char *name)
21{
22 struct mtd_info *mtd;
23
24 mtd_probe_devices();
25
26 mtd = get_mtd_device_nm(name);
27 if (IS_ERR_OR_NULL(mtd))
28 printf("MTD device %s not found, ret %ld\n", name,
29 PTR_ERR(mtd));
30
31 return mtd;
32}
33
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)

--- 146 unchanged lines hidden (view full) ---

172
173 for (i = 0; i < op->ooblen; i++)
174 if (op->oobbuf[i] != 0xff)
175 return false;
176
177 return true;
178}
179
34static uint mtd_len_to_pages(struct mtd_info *mtd, u64 len)
35{
36 do_div(len, mtd->writesize);
37
38 return len;
39}
40
41static bool mtd_is_aligned_with_min_io_size(struct mtd_info *mtd, u64 size)

--- 146 unchanged lines hidden (view full) ---

188
189 for (i = 0; i < op->ooblen; i++)
190 if (op->oobbuf[i] != 0xff)
191 return false;
192
193 return true;
194}
195
180static int do_mtd_list(void)
196static int do_mtd_list(cmd_tbl_t *cmdtp, int flag, int argc,
197 char * const argv[])
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");

--- 27 unchanged lines hidden (view full) ---

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
198{
199 struct mtd_info *mtd;
200 int dev_nb = 0;
201
202 /* Ensure all devices (and their partitions) are probed */
203 mtd_probe_devices();
204
205 printf("List of MTD devices:\n");

--- 27 unchanged lines hidden (view full) ---

233 io_op->oobretlen = woob ? mtd->oobsize : 0;
234 } else {
235 ret = mtd_write_oob(mtd, off, io_op);
236 }
237
238 return ret;
239}
240
224static int do_mtd(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
241static int do_mtd_io(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
225{
242{
243 bool dump, read, raw, woob, write_empty_pages, has_pages = false;
244 u64 start_off, off, len, remaining, default_len;
245 struct mtd_oob_ops io_op = {};
246 uint user_addr = 0, npages;
247 const char *cmd = argv[0];
226 struct mtd_info *mtd;
248 struct mtd_info *mtd;
227 const char *cmd;
228 char *mtd_name;
249 u32 oob_len;
250 u8 *buf;
251 int ret;
229
252
230 /* All MTD commands need at least two arguments */
231 if (argc < 2)
232 return CMD_RET_USAGE;
233
253 if (argc < 2)
254 return CMD_RET_USAGE;
255
234 /* Parse the command name and its optional suffixes */
235 cmd = argv[1];
256 mtd = get_mtd_by_name(argv[1]);
257 if (IS_ERR_OR_NULL(mtd))
258 return CMD_RET_FAILURE;
236
259
237 /* List the MTD devices if that is what the user wants */
238 if (strcmp(cmd, "list") == 0)
239 return do_mtd_list();
260 if (mtd->type == MTD_NANDFLASH || mtd->type == MTD_MLCNANDFLASH)
261 has_pages = true;
240
262
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;
263 dump = !strncmp(cmd, "dump", 4);
264 read = dump || !strncmp(cmd, "read", 4);
265 raw = strstr(cmd, ".raw");
266 woob = strstr(cmd, ".oob");
267 write_empty_pages = !has_pages || strstr(cmd, ".dontskipff");
247
268
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;
269 argc -= 2;
270 argv += 2;
271
272 if (!dump) {
273 if (!argc) {
274 ret = CMD_RET_USAGE;
275 goto out_put_mtd;
276 }
277
278 user_addr = simple_strtoul(argv[0], NULL, 16);
279 argc--;
280 argv++;
255 }
281 }
256 put_mtd_device(mtd);
257
282
258 argc -= 3;
259 argv += 3;
283 start_off = argc > 0 ? simple_strtoul(argv[0], NULL, 16) : 0;
284 if (!mtd_is_aligned_with_min_io_size(mtd, start_off)) {
285 printf("Offset not aligned with a page (0x%x)\n",
286 mtd->writesize);
287 ret = CMD_RET_FAILURE;
288 goto out_put_mtd;
289 }
260
290
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;
291 default_len = dump ? mtd->writesize : mtd->size;
292 len = argc > 1 ? simple_strtoul(argv[1], NULL, 16) : default_len;
293 if (!mtd_is_aligned_with_min_io_size(mtd, len)) {
294 len = round_up(len, mtd->writesize);
295 printf("Size not on a page boundary (0x%x), rounding to 0x%llx\n",
296 mtd->writesize, len);
297 }
273
298
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");
299 remaining = len;
300 npages = mtd_len_to_pages(mtd, len);
301 oob_len = woob ? npages * mtd->oobsize : 0;
279
302
280 if (!dump) {
281 if (!argc)
282 return CMD_RET_USAGE;
303 if (dump)
304 buf = kmalloc(len + oob_len, GFP_KERNEL);
305 else
306 buf = map_sysmem(user_addr, 0);
283
307
284 user_addr = simple_strtoul(argv[0], NULL, 16);
285 argc--;
286 argv++;
287 }
308 if (!buf) {
309 printf("Could not map/allocate the user buffer\n");
310 ret = CMD_RET_FAILURE;
311 goto out_put_mtd;
312 }
288
313
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 }
314 if (has_pages)
315 printf("%s %lld byte(s) (%d page(s)) at offset 0x%08llx%s%s%s\n",
316 read ? "Reading" : "Writing", len, npages, start_off,
317 raw ? " [raw]" : "", woob ? " [oob]" : "",
318 !read && write_empty_pages ? " [dontskipff]" : "");
319 else
320 printf("%s %lld byte(s) at offset 0x%08llx\n",
321 read ? "Reading" : "Writing", len, start_off);
295
322
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 }
323 io_op.mode = raw ? MTD_OPS_RAW : MTD_OPS_AUTO_OOB;
324 io_op.len = has_pages ? mtd->writesize : len;
325 io_op.ooblen = woob ? mtd->oobsize : 0;
326 io_op.datbuf = buf;
327 io_op.oobbuf = woob ? &buf[len] : NULL;
304
328
305 remaining = len;
306 npages = mtd_len_to_pages(mtd, len);
307 oob_len = woob ? npages * mtd->oobsize : 0;
329 /* Search for the first good block after the given offset */
330 off = start_off;
331 while (mtd_block_isbad(mtd, off))
332 off += mtd->erasesize;
308
333
309 if (dump)
310 buf = kmalloc(len + oob_len, GFP_KERNEL);
334 /* Loop over the pages to do the actual read/write */
335 while (remaining) {
336 /* Skip the block if it is bad */
337 if (mtd_is_aligned_with_block_size(mtd, off) &&
338 mtd_block_isbad(mtd, off)) {
339 off += mtd->erasesize;
340 continue;
341 }
342
343 if (read)
344 ret = mtd_read_oob(mtd, off, &io_op);
311 else
345 else
312 buf = map_sysmem(user_addr, 0);
346 ret = mtd_special_write_oob(mtd, off, &io_op,
347 write_empty_pages, woob);
313
348
314 if (!buf) {
315 printf("Could not map/allocate the user buffer\n");
316 return CMD_RET_FAILURE;
349 if (ret) {
350 printf("Failure while %s at offset 0x%llx\n",
351 read ? "reading" : "writing", off);
352 break;
317 }
318
353 }
354
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);
355 off += io_op.retlen;
356 remaining -= io_op.retlen;
357 io_op.datbuf += io_op.retlen;
358 io_op.oobbuf += io_op.oobretlen;
359 }
327
360
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;
361 if (!ret && dump)
362 mtd_dump_device_buf(mtd, start_off, buf, len, woob);
333
363
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;
364 if (dump)
365 kfree(buf);
366 else
367 unmap_sysmem(buf);
338
368
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 }
369 if (ret) {
370 printf("%s on %s failed with error %d\n",
371 read ? "Read" : "Write", mtd->name, ret);
372 ret = CMD_RET_FAILURE;
373 } else {
374 ret = CMD_RET_SUCCESS;
375 }
347
376
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);
377out_put_mtd:
378 put_mtd_device(mtd);
354
379
355 if (ret) {
356 printf("Failure while %s at offset 0x%llx\n",
357 read ? "reading" : "writing", off);
358 return CMD_RET_FAILURE;
359 }
380 return ret;
381}
360
382
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 }
383static int do_mtd_erase(cmd_tbl_t *cmdtp, int flag, int argc,
384 char * const argv[])
385{
386 struct erase_info erase_op = {};
387 struct mtd_info *mtd;
388 u64 off, len;
389 bool scrub;
390 int ret;
366
391
367 if (!ret && dump)
368 mtd_dump_device_buf(mtd, start_off, buf, len, woob);
392 if (argc < 2)
393 return CMD_RET_USAGE;
369
394
370 if (dump)
371 kfree(buf);
372 else
373 unmap_sysmem(buf);
395 mtd = get_mtd_by_name(argv[1]);
396 if (IS_ERR_OR_NULL(mtd))
397 return CMD_RET_FAILURE;
374
398
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 }
399 scrub = strstr(argv[0], ".dontskipbad");
380
400
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;
401 argc -= 2;
402 argv += 2;
386
403
387 off = argc > 0 ? simple_strtoul(argv[0], NULL, 16) : 0;
388 len = argc > 1 ? simple_strtoul(argv[1], NULL, 16) : mtd->size;
404 off = argc > 0 ? simple_strtoul(argv[0], NULL, 16) : 0;
405 len = argc > 1 ? simple_strtoul(argv[1], NULL, 16) : mtd->size;
389
406
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 }
407 if (!mtd_is_aligned_with_block_size(mtd, off)) {
408 printf("Offset not aligned with a block (0x%x)\n",
409 mtd->erasesize);
410 ret = CMD_RET_FAILURE;
411 goto out_put_mtd;
412 }
395
413
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 }
414 if (!mtd_is_aligned_with_block_size(mtd, len)) {
415 printf("Size not a multiple of a block (0x%x)\n",
416 mtd->erasesize);
417 ret = CMD_RET_FAILURE;
418 goto out_put_mtd;
419 }
401
420
402 printf("Erasing 0x%08llx ... 0x%08llx (%d eraseblock(s))\n",
403 off, off + len - 1, mtd_div_by_eb(len, mtd));
421 printf("Erasing 0x%08llx ... 0x%08llx (%d eraseblock(s))\n",
422 off, off + len - 1, mtd_div_by_eb(len, mtd));
404
423
405 erase_op.mtd = mtd;
406 erase_op.addr = off;
407 erase_op.len = len;
408 erase_op.scrub = scrub;
424 erase_op.mtd = mtd;
425 erase_op.addr = off;
426 erase_op.len = len;
427 erase_op.scrub = scrub;
409
428
410 while (erase_op.len) {
411 ret = mtd_erase(mtd, &erase_op);
429 while (erase_op.len) {
430 ret = mtd_erase(mtd, &erase_op);
412
431
413 /* Abort if its not a bad block error */
414 if (ret != -EIO)
415 break;
432 /* Abort if its not a bad block error */
433 if (ret != -EIO)
434 break;
416
435
417 printf("Skipping bad block at 0x%08llx\n",
418 erase_op.fail_addr);
436 printf("Skipping bad block at 0x%08llx\n", erase_op.fail_addr);
419
437
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 }
438 /* Skip bad block and continue behind it */
439 erase_op.len -= erase_op.fail_addr - erase_op.addr;
440 erase_op.len -= mtd->erasesize;
441 erase_op.addr = erase_op.fail_addr + mtd->erasesize;
442 }
425
443
426 if (ret && ret != -EIO)
427 return CMD_RET_FAILURE;
428 } else if (!strcmp(cmd, "bad")) {
429 loff_t off;
444 if (ret && ret != -EIO)
445 ret = CMD_RET_FAILURE;
446 else
447 ret = CMD_RET_SUCCESS;
430
448
431 if (!mtd_can_have_bb(mtd)) {
432 printf("Only NAND-based devices can have bad blocks\n");
433 return CMD_RET_SUCCESS;
434 }
449out_put_mtd:
450 put_mtd_device(mtd);
435
451
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 {
452 return ret;
453}
454
455static int do_mtd_bad(cmd_tbl_t *cmdtp, int flag, int argc,
456 char * const argv[])
457{
458 struct mtd_info *mtd;
459 loff_t off;
460
461 if (argc < 2)
441 return CMD_RET_USAGE;
462 return CMD_RET_USAGE;
463
464 mtd = get_mtd_by_name(argv[1]);
465 if (IS_ERR_OR_NULL(mtd))
466 return CMD_RET_FAILURE;
467
468 if (!mtd_can_have_bb(mtd)) {
469 printf("Only NAND-based devices can have bad blocks\n");
470 goto out_put_mtd;
442 }
443
471 }
472
473 printf("MTD device %s bad blocks list:\n", mtd->name);
474 for (off = 0; off < mtd->size; off += mtd->erasesize) {
475 if (mtd_block_isbad(mtd, off))
476 printf("\t0x%08llx\n", off);
477 }
478
479out_put_mtd:
480 put_mtd_device(mtd);
481
444 return CMD_RET_SUCCESS;
445}
446
482 return CMD_RET_SUCCESS;
483}
484
485#ifdef CONFIG_AUTO_COMPLETE
486static int mtd_name_complete(int argc, char * const argv[], char last_char,
487 int maxv, char *cmdv[])
488{
489 int len = 0, n_found = 0;
490 struct mtd_info *mtd;
491
492 argc--;
493 argv++;
494
495 if (argc > 1 ||
496 (argc == 1 && (last_char == '\0' || isblank(last_char))))
497 return 0;
498
499 if (argc)
500 len = strlen(argv[0]);
501
502 mtd_for_each_device(mtd) {
503 if (argc &&
504 (len > strlen(mtd->name) ||
505 strncmp(argv[0], mtd->name, len)))
506 continue;
507
508 if (n_found >= maxv - 2) {
509 cmdv[n_found++] = "...";
510 break;
511 }
512
513 cmdv[n_found++] = mtd->name;
514 }
515
516 cmdv[n_found] = NULL;
517
518 return n_found;
519}
520#endif /* CONFIG_AUTO_COMPLETE */
521
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"

--- 10 unchanged lines hidden (view full) ---

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
522static char mtd_help_text[] =
523#ifdef CONFIG_SYS_LONGHELP
524 "- generic operations on memory technology devices\n\n"
525 "mtd list\n"
526 "mtd read[.raw][.oob] <name> <addr> [<off> [<size>]]\n"
527 "mtd dump[.raw][.oob] <name> [<off> [<size>]]\n"
528 "mtd write[.raw][.oob][.dontskipff] <name> <addr> [<off> [<size>]]\n"
529 "mtd erase[.dontskipbad] <name> [<off> [<size>]]\n"

--- 10 unchanged lines hidden (view full) ---

540 "\t<size>: length of the operation in bytes (default: the entire device)\n"
541 "\t\t* must be a multiple of a block for erase\n"
542 "\t\t* must be a multiple of a page otherwise (special case: default is a page with dump)\n"
543 "\n"
544 "The .dontskipff option forces writing empty pages, don't use it if unsure.\n"
545#endif
546 "";
547
473U_BOOT_CMD(mtd, 10, 1, do_mtd, "MTD utils", mtd_help_text);
548U_BOOT_CMD_WITH_SUBCMDS(mtd, "MTD utils", mtd_help_text,
549 U_BOOT_SUBCMD_MKENT(list, 1, 1, do_mtd_list),
550 U_BOOT_SUBCMD_MKENT_COMPLETE(read, 5, 0, do_mtd_io,
551 mtd_name_complete),
552 U_BOOT_SUBCMD_MKENT_COMPLETE(write, 5, 0, do_mtd_io,
553 mtd_name_complete),
554 U_BOOT_SUBCMD_MKENT_COMPLETE(dump, 4, 0, do_mtd_io,
555 mtd_name_complete),
556 U_BOOT_SUBCMD_MKENT_COMPLETE(erase, 4, 0, do_mtd_erase,
557 mtd_name_complete),
558 U_BOOT_SUBCMD_MKENT_COMPLETE(bad, 2, 1, do_mtd_bad,
559 mtd_name_complete));