cmd: mtd: add 'mtd' command
[oweals/u-boot.git] / cmd / mtd.c
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 static uint mtd_len_to_pages(struct mtd_info *mtd, u64 len)
19 {
20         do_div(len, mtd->writesize);
21
22         return len;
23 }
24
25 static bool mtd_is_aligned_with_min_io_size(struct mtd_info *mtd, u64 size)
26 {
27         return !do_div(size, mtd->writesize);
28 }
29
30 static bool mtd_is_aligned_with_block_size(struct mtd_info *mtd, u64 size)
31 {
32         return !do_div(size, mtd->erasesize);
33 }
34
35 static 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
52 static 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
85 static 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
100 static 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() */
165 static 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
180 static 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
204 static 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
224 static 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
447 static 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
473 U_BOOT_CMD(mtd, 10, 1, do_mtd, "MTD utils", mtd_help_text);