xref: /openbmc/u-boot/cmd/mtdparts.c (revision 730d2544)
1 /*
2  * (C) Copyright 2002
3  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
4  *
5  * (C) Copyright 2002
6  * Robert Schwebel, Pengutronix, <r.schwebel@pengutronix.de>
7  *
8  * (C) Copyright 2003
9  * Kai-Uwe Bloem, Auerswald GmbH & Co KG, <linux-development@auerswald.de>
10  *
11  * (C) Copyright 2005
12  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
13  *
14  *   Added support for reading flash partition table from environment.
15  *   Parsing routines are based on driver/mtd/cmdline.c from the linux 2.4
16  *   kernel tree.
17  *
18  * (C) Copyright 2008
19  * Harald Welte, OpenMoko, Inc., Harald Welte <laforge@openmoko.org>
20  *
21  *   $Id: cmdlinepart.c,v 1.17 2004/11/26 11:18:47 lavinen Exp $
22  *   Copyright 2002 SYSGO Real-Time Solutions GmbH
23  *
24  * SPDX-License-Identifier:	GPL-2.0+
25  */
26 
27 /*
28  * Three environment variables are used by the parsing routines:
29  *
30  * 'partition' - keeps current partition identifier
31  *
32  * partition  := <part-id>
33  * <part-id>  := <dev-id>,part_num
34  *
35  *
36  * 'mtdids' - linux kernel mtd device id <-> u-boot device id mapping
37  *
38  * mtdids=<idmap>[,<idmap>,...]
39  *
40  * <idmap>    := <dev-id>=<mtd-id>
41  * <dev-id>   := 'nand'|'nor'|'onenand'<dev-num>
42  * <dev-num>  := mtd device number, 0...
43  * <mtd-id>   := unique device tag used by linux kernel to find mtd device (mtd->name)
44  *
45  *
46  * 'mtdparts' - partition list
47  *
48  * mtdparts=mtdparts=<mtd-def>[;<mtd-def>...]
49  *
50  * <mtd-def>  := <mtd-id>:<part-def>[,<part-def>...]
51  * <mtd-id>   := unique device tag used by linux kernel to find mtd device (mtd->name)
52  * <part-def> := <size>[@<offset>][<name>][<ro-flag>]
53  * <size>     := standard linux memsize OR '-' to denote all remaining space
54  * <offset>   := partition start offset within the device
55  * <name>     := '(' NAME ')'
56  * <ro-flag>  := when set to 'ro' makes partition read-only (not used, passed to kernel)
57  *
58  * Notes:
59  * - each <mtd-id> used in mtdparts must albo exist in 'mtddis' mapping
60  * - if the above variables are not set defaults for a given target are used
61  *
62  * Examples:
63  *
64  * 1 NOR Flash, with 1 single writable partition:
65  * mtdids=nor0=edb7312-nor
66  * mtdparts=mtdparts=edb7312-nor:-
67  *
68  * 1 NOR Flash with 2 partitions, 1 NAND with one
69  * mtdids=nor0=edb7312-nor,nand0=edb7312-nand
70  * mtdparts=mtdparts=edb7312-nor:256k(ARMboot)ro,-(root);edb7312-nand:-(home)
71  *
72  */
73 
74 #include <common.h>
75 #include <command.h>
76 #include <malloc.h>
77 #include <jffs2/load_kernel.h>
78 #include <linux/list.h>
79 #include <linux/ctype.h>
80 #include <linux/err.h>
81 #include <linux/mtd/mtd.h>
82 
83 #if defined(CONFIG_CMD_NAND)
84 #include <linux/mtd/nand.h>
85 #include <nand.h>
86 #endif
87 
88 #if defined(CONFIG_CMD_ONENAND)
89 #include <linux/mtd/onenand.h>
90 #include <onenand_uboot.h>
91 #endif
92 
93 DECLARE_GLOBAL_DATA_PTR;
94 
95 /* special size referring to all the remaining space in a partition */
96 #define SIZE_REMAINING		(~0llu)
97 
98 /* special offset value, it is used when not provided by user
99  *
100  * this value is used temporarily during parsing, later such offests
101  * are recalculated */
102 #define OFFSET_NOT_SPECIFIED	(~0llu)
103 
104 /* minimum partition size */
105 #define MIN_PART_SIZE		4096
106 
107 /* this flag needs to be set in part_info struct mask_flags
108  * field for read-only partitions */
109 #define MTD_WRITEABLE_CMD		1
110 
111 /* default values for mtdids and mtdparts variables */
112 #if !defined(MTDIDS_DEFAULT)
113 #define MTDIDS_DEFAULT NULL
114 #endif
115 #if !defined(MTDPARTS_DEFAULT)
116 #define MTDPARTS_DEFAULT NULL
117 #endif
118 #if defined(CONFIG_SYS_MTDPARTS_RUNTIME)
119 extern void board_mtdparts_default(const char **mtdids, const char **mtdparts);
120 #endif
121 static const char *mtdids_default = MTDIDS_DEFAULT;
122 static const char *mtdparts_default = MTDPARTS_DEFAULT;
123 
124 /* copies of last seen 'mtdids', 'mtdparts' and 'partition' env variables */
125 #define MTDIDS_MAXLEN		128
126 #define MTDPARTS_MAXLEN		512
127 #define PARTITION_MAXLEN	16
128 static char last_ids[MTDIDS_MAXLEN];
129 static char last_parts[MTDPARTS_MAXLEN];
130 static char last_partition[PARTITION_MAXLEN];
131 
132 /* low level jffs2 cache cleaning routine */
133 extern void jffs2_free_cache(struct part_info *part);
134 
135 /* mtdids mapping list, filled by parse_ids() */
136 static struct list_head mtdids;
137 
138 /* device/partition list, parse_cmdline() parses into here */
139 static struct list_head devices;
140 
141 /* current active device and partition number */
142 struct mtd_device *current_mtd_dev = NULL;
143 u8 current_mtd_partnum = 0;
144 
145 u8 use_defaults;
146 
147 static struct part_info* mtd_part_info(struct mtd_device *dev, unsigned int part_num);
148 
149 /* command line only routines */
150 static struct mtdids* id_find_by_mtd_id(const char *mtd_id, unsigned int mtd_id_len);
151 static int device_del(struct mtd_device *dev);
152 
153 /**
154  * Parses a string into a number.  The number stored at ptr is
155  * potentially suffixed with K (for kilobytes, or 1024 bytes),
156  * M (for megabytes, or 1048576 bytes), or G (for gigabytes, or
157  * 1073741824).  If the number is suffixed with K, M, or G, then
158  * the return value is the number multiplied by one kilobyte, one
159  * megabyte, or one gigabyte, respectively.
160  *
161  * @param ptr where parse begins
162  * @param retptr output pointer to next char after parse completes (output)
163  * @return resulting unsigned int
164  */
165 static u64 memsize_parse (const char *const ptr, const char **retptr)
166 {
167 	u64 ret = simple_strtoull(ptr, (char **)retptr, 0);
168 
169 	switch (**retptr) {
170 		case 'G':
171 		case 'g':
172 			ret <<= 10;
173 		case 'M':
174 		case 'm':
175 			ret <<= 10;
176 		case 'K':
177 		case 'k':
178 			ret <<= 10;
179 			(*retptr)++;
180 		default:
181 			break;
182 	}
183 
184 	return ret;
185 }
186 
187 /**
188  * Format string describing supplied size. This routine does the opposite job
189  * to memsize_parse(). Size in bytes is converted to string and if possible
190  * shortened by using k (kilobytes), m (megabytes) or g (gigabytes) suffix.
191  *
192  * Note, that this routine does not check for buffer overflow, it's the caller
193  * who must assure enough space.
194  *
195  * @param buf output buffer
196  * @param size size to be converted to string
197  */
198 static void memsize_format(char *buf, u64 size)
199 {
200 #define SIZE_GB ((u32)1024*1024*1024)
201 #define SIZE_MB ((u32)1024*1024)
202 #define SIZE_KB ((u32)1024)
203 
204 	if ((size % SIZE_GB) == 0)
205 		sprintf(buf, "%llug", size/SIZE_GB);
206 	else if ((size % SIZE_MB) == 0)
207 		sprintf(buf, "%llum", size/SIZE_MB);
208 	else if (size % SIZE_KB == 0)
209 		sprintf(buf, "%lluk", size/SIZE_KB);
210 	else
211 		sprintf(buf, "%llu", size);
212 }
213 
214 /**
215  * This routine does global indexing of all partitions. Resulting index for
216  * current partition is saved in 'mtddevnum'. Current partition name in
217  * 'mtddevname'.
218  */
219 static void index_partitions(void)
220 {
221 	u16 mtddevnum;
222 	struct part_info *part;
223 	struct list_head *dentry;
224 	struct mtd_device *dev;
225 
226 	debug("--- index partitions ---\n");
227 
228 	if (current_mtd_dev) {
229 		mtddevnum = 0;
230 		list_for_each(dentry, &devices) {
231 			dev = list_entry(dentry, struct mtd_device, link);
232 			if (dev == current_mtd_dev) {
233 				mtddevnum += current_mtd_partnum;
234 				setenv_ulong("mtddevnum", mtddevnum);
235 				break;
236 			}
237 			mtddevnum += dev->num_parts;
238 		}
239 
240 		part = mtd_part_info(current_mtd_dev, current_mtd_partnum);
241 		setenv("mtddevname", part->name);
242 
243 		debug("=> mtddevnum %d,\n=> mtddevname %s\n", mtddevnum, part->name);
244 	} else {
245 		setenv("mtddevnum", NULL);
246 		setenv("mtddevname", NULL);
247 
248 		debug("=> mtddevnum NULL\n=> mtddevname NULL\n");
249 	}
250 }
251 
252 /**
253  * Save current device and partition in environment variable 'partition'.
254  */
255 static void current_save(void)
256 {
257 	char buf[16];
258 
259 	debug("--- current_save ---\n");
260 
261 	if (current_mtd_dev) {
262 		sprintf(buf, "%s%d,%d", MTD_DEV_TYPE(current_mtd_dev->id->type),
263 					current_mtd_dev->id->num, current_mtd_partnum);
264 
265 		setenv("partition", buf);
266 		strncpy(last_partition, buf, 16);
267 
268 		debug("=> partition %s\n", buf);
269 	} else {
270 		setenv("partition", NULL);
271 		last_partition[0] = '\0';
272 
273 		debug("=> partition NULL\n");
274 	}
275 	index_partitions();
276 }
277 
278 
279 /**
280  * Produce a mtd_info given a type and num.
281  *
282  * @param type mtd type
283  * @param num mtd number
284  * @param mtd a pointer to an mtd_info instance (output)
285  * @return 0 if device is valid, 1 otherwise
286  */
287 static int get_mtd_info(u8 type, u8 num, struct mtd_info **mtd)
288 {
289 	char mtd_dev[16];
290 
291 	sprintf(mtd_dev, "%s%d", MTD_DEV_TYPE(type), num);
292 	*mtd = get_mtd_device_nm(mtd_dev);
293 	if (IS_ERR(*mtd)) {
294 		printf("Device %s not found!\n", mtd_dev);
295 		return 1;
296 	}
297 	put_mtd_device(*mtd);
298 
299 	return 0;
300 }
301 
302 /**
303  * Performs sanity check for supplied flash partition.
304  * Table of existing MTD flash devices is searched and partition device
305  * is located. Alignment with the granularity of nand erasesize is verified.
306  *
307  * @param id of the parent device
308  * @param part partition to validate
309  * @return 0 if partition is valid, 1 otherwise
310  */
311 static int part_validate_eraseblock(struct mtdids *id, struct part_info *part)
312 {
313 	struct mtd_info *mtd = NULL;
314 	int i, j;
315 	ulong start;
316 	u64 offset, size;
317 
318 	if (get_mtd_info(id->type, id->num, &mtd))
319 		return 1;
320 
321 	part->sector_size = mtd->erasesize;
322 
323 	if (!mtd->numeraseregions) {
324 		/*
325 		 * Only one eraseregion (NAND, OneNAND or uniform NOR),
326 		 * checking for alignment is easy here
327 		 */
328 		offset = part->offset;
329 		if (do_div(offset, mtd->erasesize)) {
330 			printf("%s%d: partition (%s) start offset"
331 			       "alignment incorrect\n",
332 			       MTD_DEV_TYPE(id->type), id->num, part->name);
333 			return 1;
334 		}
335 
336 		size = part->size;
337 		if (do_div(size, mtd->erasesize)) {
338 			printf("%s%d: partition (%s) size alignment incorrect\n",
339 			       MTD_DEV_TYPE(id->type), id->num, part->name);
340 			return 1;
341 		}
342 	} else {
343 		/*
344 		 * Multiple eraseregions (non-uniform NOR),
345 		 * checking for alignment is more complex here
346 		 */
347 
348 		/* Check start alignment */
349 		for (i = 0; i < mtd->numeraseregions; i++) {
350 			start = mtd->eraseregions[i].offset;
351 			for (j = 0; j < mtd->eraseregions[i].numblocks; j++) {
352 				if (part->offset == start)
353 					goto start_ok;
354 				start += mtd->eraseregions[i].erasesize;
355 			}
356 		}
357 
358 		printf("%s%d: partition (%s) start offset alignment incorrect\n",
359 		       MTD_DEV_TYPE(id->type), id->num, part->name);
360 		return 1;
361 
362 	start_ok:
363 
364 		/* Check end/size alignment */
365 		for (i = 0; i < mtd->numeraseregions; i++) {
366 			start = mtd->eraseregions[i].offset;
367 			for (j = 0; j < mtd->eraseregions[i].numblocks; j++) {
368 				if ((part->offset + part->size) == start)
369 					goto end_ok;
370 				start += mtd->eraseregions[i].erasesize;
371 			}
372 		}
373 		/* Check last sector alignment */
374 		if ((part->offset + part->size) == start)
375 			goto end_ok;
376 
377 		printf("%s%d: partition (%s) size alignment incorrect\n",
378 		       MTD_DEV_TYPE(id->type), id->num, part->name);
379 		return 1;
380 
381 	end_ok:
382 		return 0;
383 	}
384 
385 	return 0;
386 }
387 
388 
389 /**
390  * Performs sanity check for supplied partition. Offset and size are
391  * verified to be within valid range. Partition type is checked and
392  * part_validate_eraseblock() is called with the argument of part.
393  *
394  * @param id of the parent device
395  * @param part partition to validate
396  * @return 0 if partition is valid, 1 otherwise
397  */
398 static int part_validate(struct mtdids *id, struct part_info *part)
399 {
400 	if (part->size == SIZE_REMAINING)
401 		part->size = id->size - part->offset;
402 
403 	if (part->offset > id->size) {
404 		printf("%s: offset %08llx beyond flash size %08llx\n",
405 				id->mtd_id, part->offset, id->size);
406 		return 1;
407 	}
408 
409 	if ((part->offset + part->size) <= part->offset) {
410 		printf("%s%d: partition (%s) size too big\n",
411 				MTD_DEV_TYPE(id->type), id->num, part->name);
412 		return 1;
413 	}
414 
415 	if (part->offset + part->size > id->size) {
416 		printf("%s: partitioning exceeds flash size\n", id->mtd_id);
417 		return 1;
418 	}
419 
420 	/*
421 	 * Now we need to check if the partition starts and ends on
422 	 * sector (eraseblock) regions
423 	 */
424 	return part_validate_eraseblock(id, part);
425 }
426 
427 /**
428  * Delete selected partition from the partition list of the specified device.
429  *
430  * @param dev device to delete partition from
431  * @param part partition to delete
432  * @return 0 on success, 1 otherwise
433  */
434 static int part_del(struct mtd_device *dev, struct part_info *part)
435 {
436 	u8 current_save_needed = 0;
437 
438 	/* if there is only one partition, remove whole device */
439 	if (dev->num_parts == 1)
440 		return device_del(dev);
441 
442 	/* otherwise just delete this partition */
443 
444 	if (dev == current_mtd_dev) {
445 		/* we are modyfing partitions for the current device,
446 		 * update current */
447 		struct part_info *curr_pi;
448 		curr_pi = mtd_part_info(current_mtd_dev, current_mtd_partnum);
449 
450 		if (curr_pi) {
451 			if (curr_pi == part) {
452 				printf("current partition deleted, resetting current to 0\n");
453 				current_mtd_partnum = 0;
454 			} else if (part->offset <= curr_pi->offset) {
455 				current_mtd_partnum--;
456 			}
457 			current_save_needed = 1;
458 		}
459 	}
460 
461 	list_del(&part->link);
462 	free(part);
463 	dev->num_parts--;
464 
465 	if (current_save_needed > 0)
466 		current_save();
467 	else
468 		index_partitions();
469 
470 	return 0;
471 }
472 
473 /**
474  * Delete all partitions from parts head list, free memory.
475  *
476  * @param head list of partitions to delete
477  */
478 static void part_delall(struct list_head *head)
479 {
480 	struct list_head *entry, *n;
481 	struct part_info *part_tmp;
482 
483 	/* clean tmp_list and free allocated memory */
484 	list_for_each_safe(entry, n, head) {
485 		part_tmp = list_entry(entry, struct part_info, link);
486 
487 		list_del(entry);
488 		free(part_tmp);
489 	}
490 }
491 
492 /**
493  * Add new partition to the supplied partition list. Make sure partitions are
494  * sorted by offset in ascending order.
495  *
496  * @param head list this partition is to be added to
497  * @param new partition to be added
498  */
499 static int part_sort_add(struct mtd_device *dev, struct part_info *part)
500 {
501 	struct list_head *entry;
502 	struct part_info *new_pi, *curr_pi;
503 
504 	/* link partition to parrent dev */
505 	part->dev = dev;
506 
507 	if (list_empty(&dev->parts)) {
508 		debug("part_sort_add: list empty\n");
509 		list_add(&part->link, &dev->parts);
510 		dev->num_parts++;
511 		index_partitions();
512 		return 0;
513 	}
514 
515 	new_pi = list_entry(&part->link, struct part_info, link);
516 
517 	/* get current partition info if we are updating current device */
518 	curr_pi = NULL;
519 	if (dev == current_mtd_dev)
520 		curr_pi = mtd_part_info(current_mtd_dev, current_mtd_partnum);
521 
522 	list_for_each(entry, &dev->parts) {
523 		struct part_info *pi;
524 
525 		pi = list_entry(entry, struct part_info, link);
526 
527 		/* be compliant with kernel cmdline, allow only one partition at offset zero */
528 		if ((new_pi->offset == pi->offset) && (pi->offset == 0)) {
529 			printf("cannot add second partition at offset 0\n");
530 			return 1;
531 		}
532 
533 		if (new_pi->offset <= pi->offset) {
534 			list_add_tail(&part->link, entry);
535 			dev->num_parts++;
536 
537 			if (curr_pi && (pi->offset <= curr_pi->offset)) {
538 				/* we are modyfing partitions for the current
539 				 * device, update current */
540 				current_mtd_partnum++;
541 				current_save();
542 			} else {
543 				index_partitions();
544 			}
545 			return 0;
546 		}
547 	}
548 
549 	list_add_tail(&part->link, &dev->parts);
550 	dev->num_parts++;
551 	index_partitions();
552 	return 0;
553 }
554 
555 /**
556  * Add provided partition to the partition list of a given device.
557  *
558  * @param dev device to which partition is added
559  * @param part partition to be added
560  * @return 0 on success, 1 otherwise
561  */
562 static int part_add(struct mtd_device *dev, struct part_info *part)
563 {
564 	/* verify alignment and size */
565 	if (part_validate(dev->id, part) != 0)
566 		return 1;
567 
568 	/* partition is ok, add it to the list */
569 	if (part_sort_add(dev, part) != 0)
570 		return 1;
571 
572 	return 0;
573 }
574 
575 /**
576  * Parse one partition definition, allocate memory and return pointer to this
577  * location in retpart.
578  *
579  * @param partdef pointer to the partition definition string i.e. <part-def>
580  * @param ret output pointer to next char after parse completes (output)
581  * @param retpart pointer to the allocated partition (output)
582  * @return 0 on success, 1 otherwise
583  */
584 static int part_parse(const char *const partdef, const char **ret, struct part_info **retpart)
585 {
586 	struct part_info *part;
587 	u64 size;
588 	u64 offset;
589 	const char *name;
590 	int name_len;
591 	unsigned int mask_flags;
592 	const char *p;
593 
594 	p = partdef;
595 	*retpart = NULL;
596 	*ret = NULL;
597 
598 	/* fetch the partition size */
599 	if (*p == '-') {
600 		/* assign all remaining space to this partition */
601 		debug("'-': remaining size assigned\n");
602 		size = SIZE_REMAINING;
603 		p++;
604 	} else {
605 		size = memsize_parse(p, &p);
606 		if (size < MIN_PART_SIZE) {
607 			printf("partition size too small (%llx)\n", size);
608 			return 1;
609 		}
610 	}
611 
612 	/* check for offset */
613 	offset = OFFSET_NOT_SPECIFIED;
614 	if (*p == '@') {
615 		p++;
616 		offset = memsize_parse(p, &p);
617 	}
618 
619 	/* now look for the name */
620 	if (*p == '(') {
621 		name = ++p;
622 		if ((p = strchr(name, ')')) == NULL) {
623 			printf("no closing ) found in partition name\n");
624 			return 1;
625 		}
626 		name_len = p - name + 1;
627 		if ((name_len - 1) == 0) {
628 			printf("empty partition name\n");
629 			return 1;
630 		}
631 		p++;
632 	} else {
633 		/* 0x00000000@0x00000000 */
634 		name_len = 22;
635 		name = NULL;
636 	}
637 
638 	/* test for options */
639 	mask_flags = 0;
640 	if (strncmp(p, "ro", 2) == 0) {
641 		mask_flags |= MTD_WRITEABLE_CMD;
642 		p += 2;
643 	}
644 
645 	/* check for next partition definition */
646 	if (*p == ',') {
647 		if (size == SIZE_REMAINING) {
648 			*ret = NULL;
649 			printf("no partitions allowed after a fill-up partition\n");
650 			return 1;
651 		}
652 		*ret = ++p;
653 	} else if ((*p == ';') || (*p == '\0')) {
654 		*ret = p;
655 	} else {
656 		printf("unexpected character '%c' at the end of partition\n", *p);
657 		*ret = NULL;
658 		return 1;
659 	}
660 
661 	/*  allocate memory */
662 	part = (struct part_info *)malloc(sizeof(struct part_info) + name_len);
663 	if (!part) {
664 		printf("out of memory\n");
665 		return 1;
666 	}
667 	memset(part, 0, sizeof(struct part_info) + name_len);
668 	part->size = size;
669 	part->offset = offset;
670 	part->mask_flags = mask_flags;
671 	part->name = (char *)(part + 1);
672 
673 	if (name) {
674 		/* copy user provided name */
675 		strncpy(part->name, name, name_len - 1);
676 		part->auto_name = 0;
677 	} else {
678 		/* auto generated name in form of size@offset */
679 		sprintf(part->name, "0x%08llx@0x%08llx", size, offset);
680 		part->auto_name = 1;
681 	}
682 
683 	part->name[name_len - 1] = '\0';
684 	INIT_LIST_HEAD(&part->link);
685 
686 	debug("+ partition: name %-22s size 0x%08llx offset 0x%08llx mask flags %d\n",
687 			part->name, part->size,
688 			part->offset, part->mask_flags);
689 
690 	*retpart = part;
691 	return 0;
692 }
693 
694 /**
695  * Check device number to be within valid range for given device type.
696  *
697  * @param type mtd type
698  * @param num mtd number
699  * @param size a pointer to the size of the mtd device (output)
700  * @return 0 if device is valid, 1 otherwise
701  */
702 static int mtd_device_validate(u8 type, u8 num, u64 *size)
703 {
704 	struct mtd_info *mtd = NULL;
705 
706 	if (get_mtd_info(type, num, &mtd))
707 		return 1;
708 
709 	*size = mtd->size;
710 
711 	return 0;
712 }
713 
714 /**
715  * Delete all mtd devices from a supplied devices list, free memory allocated for
716  * each device and delete all device partitions.
717  *
718  * @return 0 on success, 1 otherwise
719  */
720 static int device_delall(struct list_head *head)
721 {
722 	struct list_head *entry, *n;
723 	struct mtd_device *dev_tmp;
724 
725 	/* clean devices list */
726 	list_for_each_safe(entry, n, head) {
727 		dev_tmp = list_entry(entry, struct mtd_device, link);
728 		list_del(entry);
729 		part_delall(&dev_tmp->parts);
730 		free(dev_tmp);
731 	}
732 	INIT_LIST_HEAD(&devices);
733 
734 	return 0;
735 }
736 
737 /**
738  * If provided device exists it's partitions are deleted, device is removed
739  * from device list and device memory is freed.
740  *
741  * @param dev device to be deleted
742  * @return 0 on success, 1 otherwise
743  */
744 static int device_del(struct mtd_device *dev)
745 {
746 	part_delall(&dev->parts);
747 	list_del(&dev->link);
748 	free(dev);
749 
750 	if (dev == current_mtd_dev) {
751 		/* we just deleted current device */
752 		if (list_empty(&devices)) {
753 			current_mtd_dev = NULL;
754 		} else {
755 			/* reset first partition from first dev from the
756 			 * devices list as current */
757 			current_mtd_dev = list_entry(devices.next, struct mtd_device, link);
758 			current_mtd_partnum = 0;
759 		}
760 		current_save();
761 		return 0;
762 	}
763 
764 	index_partitions();
765 	return 0;
766 }
767 
768 /**
769  * Search global device list and return pointer to the device of type and num
770  * specified.
771  *
772  * @param type device type
773  * @param num device number
774  * @return NULL if requested device does not exist
775  */
776 struct mtd_device *device_find(u8 type, u8 num)
777 {
778 	struct list_head *entry;
779 	struct mtd_device *dev_tmp;
780 
781 	list_for_each(entry, &devices) {
782 		dev_tmp = list_entry(entry, struct mtd_device, link);
783 
784 		if ((dev_tmp->id->type == type) && (dev_tmp->id->num == num))
785 			return dev_tmp;
786 	}
787 
788 	return NULL;
789 }
790 
791 /**
792  * Add specified device to the global device list.
793  *
794  * @param dev device to be added
795  */
796 static void device_add(struct mtd_device *dev)
797 {
798 	u8 current_save_needed = 0;
799 
800 	if (list_empty(&devices)) {
801 		current_mtd_dev = dev;
802 		current_mtd_partnum = 0;
803 		current_save_needed = 1;
804 	}
805 
806 	list_add_tail(&dev->link, &devices);
807 
808 	if (current_save_needed > 0)
809 		current_save();
810 	else
811 		index_partitions();
812 }
813 
814 /**
815  * Parse device type, name and mtd-id. If syntax is ok allocate memory and
816  * return pointer to the device structure.
817  *
818  * @param mtd_dev pointer to the device definition string i.e. <mtd-dev>
819  * @param ret output pointer to next char after parse completes (output)
820  * @param retdev pointer to the allocated device (output)
821  * @return 0 on success, 1 otherwise
822  */
823 static int device_parse(const char *const mtd_dev, const char **ret, struct mtd_device **retdev)
824 {
825 	struct mtd_device *dev;
826 	struct part_info *part;
827 	struct mtdids *id;
828 	const char *mtd_id;
829 	unsigned int mtd_id_len;
830 	const char *p;
831 	const char *pend;
832 	LIST_HEAD(tmp_list);
833 	struct list_head *entry, *n;
834 	u16 num_parts;
835 	u64 offset;
836 	int err = 1;
837 
838 	debug("===device_parse===\n");
839 
840 	assert(retdev);
841 	*retdev = NULL;
842 
843 	if (ret)
844 		*ret = NULL;
845 
846 	/* fetch <mtd-id> */
847 	mtd_id = p = mtd_dev;
848 	if (!(p = strchr(mtd_id, ':'))) {
849 		printf("no <mtd-id> identifier\n");
850 		return 1;
851 	}
852 	mtd_id_len = p - mtd_id + 1;
853 	p++;
854 
855 	/* verify if we have a valid device specified */
856 	if ((id = id_find_by_mtd_id(mtd_id, mtd_id_len - 1)) == NULL) {
857 		printf("invalid mtd device '%.*s'\n", mtd_id_len - 1, mtd_id);
858 		return 1;
859 	}
860 
861 #ifdef DEBUG
862 	pend = strchr(p, ';');
863 #endif
864 	debug("dev type = %d (%s), dev num = %d, mtd-id = %s\n",
865 			id->type, MTD_DEV_TYPE(id->type),
866 			id->num, id->mtd_id);
867 	debug("parsing partitions %.*s\n", (int)(pend ? pend - p : strlen(p)), p);
868 
869 
870 	/* parse partitions */
871 	num_parts = 0;
872 
873 	offset = 0;
874 	if ((dev = device_find(id->type, id->num)) != NULL) {
875 		/* if device already exists start at the end of the last partition */
876 		part = list_entry(dev->parts.prev, struct part_info, link);
877 		offset = part->offset + part->size;
878 	}
879 
880 	while (p && (*p != '\0') && (*p != ';')) {
881 		err = 1;
882 		if ((part_parse(p, &p, &part) != 0) || (!part))
883 			break;
884 
885 		/* calculate offset when not specified */
886 		if (part->offset == OFFSET_NOT_SPECIFIED)
887 			part->offset = offset;
888 		else
889 			offset = part->offset;
890 
891 		/* verify alignment and size */
892 		if (part_validate(id, part) != 0)
893 			break;
894 
895 		offset += part->size;
896 
897 		/* partition is ok, add it to the list */
898 		list_add_tail(&part->link, &tmp_list);
899 		num_parts++;
900 		err = 0;
901 	}
902 	if (err == 1) {
903 		part_delall(&tmp_list);
904 		return 1;
905 	}
906 
907 	if (num_parts == 0) {
908 		printf("no partitions for device %s%d (%s)\n",
909 				MTD_DEV_TYPE(id->type), id->num, id->mtd_id);
910 		return 1;
911 	}
912 
913 	debug("\ntotal partitions: %d\n", num_parts);
914 
915 	/* check for next device presence */
916 	if (p) {
917 		if (*p == ';') {
918 			if (ret)
919 				*ret = ++p;
920 		} else if (*p == '\0') {
921 			if (ret)
922 				*ret = p;
923 		} else {
924 			printf("unexpected character '%c' at the end of device\n", *p);
925 			if (ret)
926 				*ret = NULL;
927 			return 1;
928 		}
929 	}
930 
931 	/* allocate memory for mtd_device structure */
932 	if ((dev = (struct mtd_device *)malloc(sizeof(struct mtd_device))) == NULL) {
933 		printf("out of memory\n");
934 		return 1;
935 	}
936 	memset(dev, 0, sizeof(struct mtd_device));
937 	dev->id = id;
938 	dev->num_parts = 0; /* part_sort_add increments num_parts */
939 	INIT_LIST_HEAD(&dev->parts);
940 	INIT_LIST_HEAD(&dev->link);
941 
942 	/* move partitions from tmp_list to dev->parts */
943 	list_for_each_safe(entry, n, &tmp_list) {
944 		part = list_entry(entry, struct part_info, link);
945 		list_del(entry);
946 		if (part_sort_add(dev, part) != 0) {
947 			device_del(dev);
948 			return 1;
949 		}
950 	}
951 
952 	*retdev = dev;
953 
954 	debug("===\n\n");
955 	return 0;
956 }
957 
958 /**
959  * Initialize global device list.
960  *
961  * @return 0 on success, 1 otherwise
962  */
963 static int mtd_devices_init(void)
964 {
965 	last_parts[0] = '\0';
966 	current_mtd_dev = NULL;
967 	current_save();
968 
969 	return device_delall(&devices);
970 }
971 
972 /*
973  * Search global mtdids list and find id of requested type and number.
974  *
975  * @return pointer to the id if it exists, NULL otherwise
976  */
977 static struct mtdids* id_find(u8 type, u8 num)
978 {
979 	struct list_head *entry;
980 	struct mtdids *id;
981 
982 	list_for_each(entry, &mtdids) {
983 		id = list_entry(entry, struct mtdids, link);
984 
985 		if ((id->type == type) && (id->num == num))
986 			return id;
987 	}
988 
989 	return NULL;
990 }
991 
992 /**
993  * Search global mtdids list and find id of a requested mtd_id.
994  *
995  * Note: first argument is not null terminated.
996  *
997  * @param mtd_id string containing requested mtd_id
998  * @param mtd_id_len length of supplied mtd_id
999  * @return pointer to the id if it exists, NULL otherwise
1000  */
1001 static struct mtdids* id_find_by_mtd_id(const char *mtd_id, unsigned int mtd_id_len)
1002 {
1003 	struct list_head *entry;
1004 	struct mtdids *id;
1005 
1006 	debug("--- id_find_by_mtd_id: '%.*s' (len = %d)\n",
1007 			mtd_id_len, mtd_id, mtd_id_len);
1008 
1009 	list_for_each(entry, &mtdids) {
1010 		id = list_entry(entry, struct mtdids, link);
1011 
1012 		debug("entry: '%s' (len = %zu)\n",
1013 				id->mtd_id, strlen(id->mtd_id));
1014 
1015 		if (mtd_id_len != strlen(id->mtd_id))
1016 			continue;
1017 		if (strncmp(id->mtd_id, mtd_id, mtd_id_len) == 0)
1018 			return id;
1019 	}
1020 
1021 	return NULL;
1022 }
1023 
1024 /**
1025  * Parse device id string <dev-id> := 'nand'|'nor'|'onenand'<dev-num>,
1026  * return device type and number.
1027  *
1028  * @param id string describing device id
1029  * @param ret_id output pointer to next char after parse completes (output)
1030  * @param dev_type parsed device type (output)
1031  * @param dev_num parsed device number (output)
1032  * @return 0 on success, 1 otherwise
1033  */
1034 int mtd_id_parse(const char *id, const char **ret_id, u8 *dev_type,
1035 		 u8 *dev_num)
1036 {
1037 	const char *p = id;
1038 
1039 	*dev_type = 0;
1040 	if (strncmp(p, "nand", 4) == 0) {
1041 		*dev_type = MTD_DEV_TYPE_NAND;
1042 		p += 4;
1043 	} else if (strncmp(p, "nor", 3) == 0) {
1044 		*dev_type = MTD_DEV_TYPE_NOR;
1045 		p += 3;
1046 	} else if (strncmp(p, "onenand", 7) == 0) {
1047 		*dev_type = MTD_DEV_TYPE_ONENAND;
1048 		p += 7;
1049 	} else {
1050 		printf("incorrect device type in %s\n", id);
1051 		return 1;
1052 	}
1053 
1054 	if (!isdigit(*p)) {
1055 		printf("incorrect device number in %s\n", id);
1056 		return 1;
1057 	}
1058 
1059 	*dev_num = simple_strtoul(p, (char **)&p, 0);
1060 	if (ret_id)
1061 		*ret_id = p;
1062 	return 0;
1063 }
1064 
1065 /**
1066  * Process all devices and generate corresponding mtdparts string describing
1067  * all partitions on all devices.
1068  *
1069  * @param buf output buffer holding generated mtdparts string (output)
1070  * @param buflen buffer size
1071  * @return 0 on success, 1 otherwise
1072  */
1073 static int generate_mtdparts(char *buf, u32 buflen)
1074 {
1075 	struct list_head *pentry, *dentry;
1076 	struct mtd_device *dev;
1077 	struct part_info *part, *prev_part;
1078 	char *p = buf;
1079 	char tmpbuf[32];
1080 	u64 size, offset;
1081 	u32 len, part_cnt;
1082 	u32 maxlen = buflen - 1;
1083 
1084 	debug("--- generate_mtdparts ---\n");
1085 
1086 	if (list_empty(&devices)) {
1087 		buf[0] = '\0';
1088 		return 0;
1089 	}
1090 
1091 	strcpy(p, "mtdparts=");
1092 	p += 9;
1093 
1094 	list_for_each(dentry, &devices) {
1095 		dev = list_entry(dentry, struct mtd_device, link);
1096 
1097 		/* copy mtd_id */
1098 		len = strlen(dev->id->mtd_id) + 1;
1099 		if (len > maxlen)
1100 			goto cleanup;
1101 		memcpy(p, dev->id->mtd_id, len - 1);
1102 		p += len - 1;
1103 		*(p++) = ':';
1104 		maxlen -= len;
1105 
1106 		/* format partitions */
1107 		prev_part = NULL;
1108 		part_cnt = 0;
1109 		list_for_each(pentry, &dev->parts) {
1110 			part = list_entry(pentry, struct part_info, link);
1111 			size = part->size;
1112 			offset = part->offset;
1113 			part_cnt++;
1114 
1115 			/* partition size */
1116 			memsize_format(tmpbuf, size);
1117 			len = strlen(tmpbuf);
1118 			if (len > maxlen)
1119 				goto cleanup;
1120 			memcpy(p, tmpbuf, len);
1121 			p += len;
1122 			maxlen -= len;
1123 
1124 
1125 			/* add offset only when there is a gap between
1126 			 * partitions */
1127 			if ((!prev_part && (offset != 0)) ||
1128 					(prev_part && ((prev_part->offset + prev_part->size) != part->offset))) {
1129 
1130 				memsize_format(tmpbuf, offset);
1131 				len = strlen(tmpbuf) + 1;
1132 				if (len > maxlen)
1133 					goto cleanup;
1134 				*(p++) = '@';
1135 				memcpy(p, tmpbuf, len - 1);
1136 				p += len - 1;
1137 				maxlen -= len;
1138 			}
1139 
1140 			/* copy name only if user supplied */
1141 			if(!part->auto_name) {
1142 				len = strlen(part->name) + 2;
1143 				if (len > maxlen)
1144 					goto cleanup;
1145 
1146 				*(p++) = '(';
1147 				memcpy(p, part->name, len - 2);
1148 				p += len - 2;
1149 				*(p++) = ')';
1150 				maxlen -= len;
1151 			}
1152 
1153 			/* ro mask flag */
1154 			if (part->mask_flags && MTD_WRITEABLE_CMD) {
1155 				len = 2;
1156 				if (len > maxlen)
1157 					goto cleanup;
1158 				*(p++) = 'r';
1159 				*(p++) = 'o';
1160 				maxlen -= 2;
1161 			}
1162 
1163 			/* print ',' separator if there are other partitions
1164 			 * following */
1165 			if (dev->num_parts > part_cnt) {
1166 				if (1 > maxlen)
1167 					goto cleanup;
1168 				*(p++) = ',';
1169 				maxlen--;
1170 			}
1171 			prev_part = part;
1172 		}
1173 		/* print ';' separator if there are other devices following */
1174 		if (dentry->next != &devices) {
1175 			if (1 > maxlen)
1176 				goto cleanup;
1177 			*(p++) = ';';
1178 			maxlen--;
1179 		}
1180 	}
1181 
1182 	/* we still have at least one char left, as we decremented maxlen at
1183 	 * the begining */
1184 	*p = '\0';
1185 
1186 	return 0;
1187 
1188 cleanup:
1189 	last_parts[0] = '\0';
1190 	return 1;
1191 }
1192 
1193 /**
1194  * Call generate_mtdparts to process all devices and generate corresponding
1195  * mtdparts string, save it in mtdparts environment variable.
1196  *
1197  * @param buf output buffer holding generated mtdparts string (output)
1198  * @param buflen buffer size
1199  * @return 0 on success, 1 otherwise
1200  */
1201 static int generate_mtdparts_save(char *buf, u32 buflen)
1202 {
1203 	int ret;
1204 
1205 	ret = generate_mtdparts(buf, buflen);
1206 
1207 	if ((buf[0] != '\0') && (ret == 0))
1208 		setenv("mtdparts", buf);
1209 	else
1210 		setenv("mtdparts", NULL);
1211 
1212 	return ret;
1213 }
1214 
1215 #if defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES)
1216 /**
1217  * Get the net size (w/o bad blocks) of the given partition.
1218  *
1219  * @param mtd the mtd info
1220  * @param part the partition
1221  * @return the calculated net size of this partition
1222  */
1223 static uint64_t net_part_size(struct mtd_info *mtd, struct part_info *part)
1224 {
1225 	uint64_t i, net_size = 0;
1226 
1227 	if (!mtd->block_isbad)
1228 		return part->size;
1229 
1230 	for (i = 0; i < part->size; i += mtd->erasesize) {
1231 		if (!mtd->block_isbad(mtd, part->offset + i))
1232 			net_size += mtd->erasesize;
1233 	}
1234 
1235 	return net_size;
1236 }
1237 #endif
1238 
1239 static void print_partition_table(void)
1240 {
1241 	struct list_head *dentry, *pentry;
1242 	struct part_info *part;
1243 	struct mtd_device *dev;
1244 	int part_num;
1245 
1246 	list_for_each(dentry, &devices) {
1247 		dev = list_entry(dentry, struct mtd_device, link);
1248 		/* list partitions for given device */
1249 		part_num = 0;
1250 #if defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES)
1251 		struct mtd_info *mtd;
1252 
1253 		if (get_mtd_info(dev->id->type, dev->id->num, &mtd))
1254 			return;
1255 
1256 		printf("\ndevice %s%d <%s>, # parts = %d\n",
1257 				MTD_DEV_TYPE(dev->id->type), dev->id->num,
1258 				dev->id->mtd_id, dev->num_parts);
1259 		printf(" #: name\t\tsize\t\tnet size\toffset\t\tmask_flags\n");
1260 
1261 		list_for_each(pentry, &dev->parts) {
1262 			u32 net_size;
1263 			char *size_note;
1264 
1265 			part = list_entry(pentry, struct part_info, link);
1266 			net_size = net_part_size(mtd, part);
1267 			size_note = part->size == net_size ? " " : " (!)";
1268 			printf("%2d: %-20s0x%08x\t0x%08x%s\t0x%08x\t%d\n",
1269 					part_num, part->name, part->size,
1270 					net_size, size_note, part->offset,
1271 					part->mask_flags);
1272 #else /* !defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES) */
1273 		printf("\ndevice %s%d <%s>, # parts = %d\n",
1274 				MTD_DEV_TYPE(dev->id->type), dev->id->num,
1275 				dev->id->mtd_id, dev->num_parts);
1276 		printf(" #: name\t\tsize\t\toffset\t\tmask_flags\n");
1277 
1278 		list_for_each(pentry, &dev->parts) {
1279 			part = list_entry(pentry, struct part_info, link);
1280 			printf("%2d: %-20s0x%08llx\t0x%08llx\t%d\n",
1281 					part_num, part->name, part->size,
1282 					part->offset, part->mask_flags);
1283 #endif /* defined(CONFIG_CMD_MTDPARTS_SHOW_NET_SIZES) */
1284 			part_num++;
1285 		}
1286 	}
1287 
1288 	if (list_empty(&devices))
1289 		printf("no partitions defined\n");
1290 }
1291 
1292 /**
1293  * Format and print out a partition list for each device from global device
1294  * list.
1295  */
1296 static void list_partitions(void)
1297 {
1298 	struct part_info *part;
1299 
1300 	debug("\n---list_partitions---\n");
1301 	print_partition_table();
1302 
1303 	/* current_mtd_dev is not NULL only when we have non empty device list */
1304 	if (current_mtd_dev) {
1305 		part = mtd_part_info(current_mtd_dev, current_mtd_partnum);
1306 		if (part) {
1307 			printf("\nactive partition: %s%d,%d - (%s) 0x%08llx @ 0x%08llx\n",
1308 					MTD_DEV_TYPE(current_mtd_dev->id->type),
1309 					current_mtd_dev->id->num, current_mtd_partnum,
1310 					part->name, part->size, part->offset);
1311 		} else {
1312 			printf("could not get current partition info\n\n");
1313 		}
1314 	}
1315 
1316 	printf("\ndefaults:\n");
1317 	printf("mtdids  : %s\n",
1318 		mtdids_default ? mtdids_default : "none");
1319 	/*
1320 	 * Using printf() here results in printbuffer overflow
1321 	 * if default mtdparts string is greater than console
1322 	 * printbuffer. Use puts() to prevent system crashes.
1323 	 */
1324 	puts("mtdparts: ");
1325 	puts(mtdparts_default ? mtdparts_default : "none");
1326 	puts("\n");
1327 }
1328 
1329 /**
1330  * Given partition identifier in form of <dev_type><dev_num>,<part_num> find
1331  * corresponding device and verify partition number.
1332  *
1333  * @param id string describing device and partition or partition name
1334  * @param dev pointer to the requested device (output)
1335  * @param part_num verified partition number (output)
1336  * @param part pointer to requested partition (output)
1337  * @return 0 on success, 1 otherwise
1338  */
1339 int find_dev_and_part(const char *id, struct mtd_device **dev,
1340 		u8 *part_num, struct part_info **part)
1341 {
1342 	struct list_head *dentry, *pentry;
1343 	u8 type, dnum, pnum;
1344 	const char *p;
1345 
1346 	debug("--- find_dev_and_part ---\nid = %s\n", id);
1347 
1348 	list_for_each(dentry, &devices) {
1349 		*part_num = 0;
1350 		*dev = list_entry(dentry, struct mtd_device, link);
1351 		list_for_each(pentry, &(*dev)->parts) {
1352 			*part = list_entry(pentry, struct part_info, link);
1353 			if (strcmp((*part)->name, id) == 0)
1354 				return 0;
1355 			(*part_num)++;
1356 		}
1357 	}
1358 
1359 	p = id;
1360 	*dev = NULL;
1361 	*part = NULL;
1362 	*part_num = 0;
1363 
1364 	if (mtd_id_parse(p, &p, &type, &dnum) != 0)
1365 		return 1;
1366 
1367 	if ((*p++ != ',') || (*p == '\0')) {
1368 		printf("no partition number specified\n");
1369 		return 1;
1370 	}
1371 	pnum = simple_strtoul(p, (char **)&p, 0);
1372 	if (*p != '\0') {
1373 		printf("unexpected trailing character '%c'\n", *p);
1374 		return 1;
1375 	}
1376 
1377 	if ((*dev = device_find(type, dnum)) == NULL) {
1378 		printf("no such device %s%d\n", MTD_DEV_TYPE(type), dnum);
1379 		return 1;
1380 	}
1381 
1382 	if ((*part = mtd_part_info(*dev, pnum)) == NULL) {
1383 		printf("no such partition\n");
1384 		*dev = NULL;
1385 		return 1;
1386 	}
1387 
1388 	*part_num = pnum;
1389 
1390 	return 0;
1391 }
1392 
1393 /**
1394  * Find and delete partition. For partition id format see find_dev_and_part().
1395  *
1396  * @param id string describing device and partition
1397  * @return 0 on success, 1 otherwise
1398  */
1399 static int delete_partition(const char *id)
1400 {
1401 	u8 pnum;
1402 	struct mtd_device *dev;
1403 	struct part_info *part;
1404 
1405 	if (find_dev_and_part(id, &dev, &pnum, &part) == 0) {
1406 
1407 		debug("delete_partition: device = %s%d, partition %d = (%s) 0x%08llx@0x%08llx\n",
1408 				MTD_DEV_TYPE(dev->id->type), dev->id->num, pnum,
1409 				part->name, part->size, part->offset);
1410 
1411 		if (part_del(dev, part) != 0)
1412 			return 1;
1413 
1414 		if (generate_mtdparts_save(last_parts, MTDPARTS_MAXLEN) != 0) {
1415 			printf("generated mtdparts too long, resetting to null\n");
1416 			return 1;
1417 		}
1418 		return 0;
1419 	}
1420 
1421 	printf("partition %s not found\n", id);
1422 	return 1;
1423 }
1424 
1425 #if defined(CONFIG_CMD_MTDPARTS_SPREAD)
1426 /**
1427  * Increase the size of the given partition so that it's net size is at least
1428  * as large as the size member and such that the next partition would start on a
1429  * good block if it were adjacent to this partition.
1430  *
1431  * @param mtd the mtd device
1432  * @param part the partition
1433  * @param next_offset pointer to the offset of the next partition after this
1434  *                    partition's size has been modified (output)
1435  */
1436 static void spread_partition(struct mtd_info *mtd, struct part_info *part,
1437 			     uint64_t *next_offset)
1438 {
1439 	uint64_t net_size, padding_size = 0;
1440 	int truncated;
1441 
1442 	mtd_get_len_incl_bad(mtd, part->offset, part->size, &net_size,
1443 			     &truncated);
1444 
1445 	/*
1446 	 * Absorb bad blocks immediately following this
1447 	 * partition also into the partition, such that
1448 	 * the next partition starts with a good block.
1449 	 */
1450 	if (!truncated) {
1451 		mtd_get_len_incl_bad(mtd, part->offset + net_size,
1452 				     mtd->erasesize, &padding_size, &truncated);
1453 		if (truncated)
1454 			padding_size = 0;
1455 		else
1456 			padding_size -= mtd->erasesize;
1457 	}
1458 
1459 	if (truncated) {
1460 		printf("truncated partition %s to %lld bytes\n", part->name,
1461 		       (uint64_t) net_size + padding_size);
1462 	}
1463 
1464 	part->size = net_size + padding_size;
1465 	*next_offset = part->offset + part->size;
1466 }
1467 
1468 /**
1469  * Adjust all of the partition sizes, such that all partitions are at least
1470  * as big as their mtdparts environment variable sizes and they each start
1471  * on a good block.
1472  *
1473  * @return 0 on success, 1 otherwise
1474  */
1475 static int spread_partitions(void)
1476 {
1477 	struct list_head *dentry, *pentry;
1478 	struct mtd_device *dev;
1479 	struct part_info *part;
1480 	struct mtd_info *mtd;
1481 	int part_num;
1482 	uint64_t cur_offs;
1483 
1484 	list_for_each(dentry, &devices) {
1485 		dev = list_entry(dentry, struct mtd_device, link);
1486 
1487 		if (get_mtd_info(dev->id->type, dev->id->num, &mtd))
1488 			return 1;
1489 
1490 		part_num = 0;
1491 		cur_offs = 0;
1492 		list_for_each(pentry, &dev->parts) {
1493 			part = list_entry(pentry, struct part_info, link);
1494 
1495 			debug("spread_partitions: device = %s%d, partition %d ="
1496 				" (%s) 0x%08llx@0x%08llx\n",
1497 				MTD_DEV_TYPE(dev->id->type), dev->id->num,
1498 				part_num, part->name, part->size,
1499 				part->offset);
1500 
1501 			if (cur_offs > part->offset)
1502 				part->offset = cur_offs;
1503 
1504 			spread_partition(mtd, part, &cur_offs);
1505 
1506 			part_num++;
1507 		}
1508 	}
1509 
1510 	index_partitions();
1511 
1512 	if (generate_mtdparts_save(last_parts, MTDPARTS_MAXLEN) != 0) {
1513 		printf("generated mtdparts too long, resetting to null\n");
1514 		return 1;
1515 	}
1516 	return 0;
1517 }
1518 #endif /* CONFIG_CMD_MTDPARTS_SPREAD */
1519 
1520 /**
1521  * The mtdparts variable tends to be long. If we need to access it
1522  * before the env is relocated, then we need to use our own stack
1523  * buffer.  gd->env_buf will be too small.
1524  *
1525  * @param buf temporary buffer pointer MTDPARTS_MAXLEN long
1526  * @return mtdparts variable string, NULL if not found
1527  */
1528 static const char *getenv_mtdparts(char *buf)
1529 {
1530 	if (gd->flags & GD_FLG_ENV_READY)
1531 		return getenv("mtdparts");
1532 	if (getenv_f("mtdparts", buf, MTDPARTS_MAXLEN) != -1)
1533 		return buf;
1534 	return NULL;
1535 }
1536 
1537 /**
1538  * Accept character string describing mtd partitions and call device_parse()
1539  * for each entry. Add created devices to the global devices list.
1540  *
1541  * @param mtdparts string specifing mtd partitions
1542  * @return 0 on success, 1 otherwise
1543  */
1544 static int parse_mtdparts(const char *const mtdparts)
1545 {
1546 	const char *p;
1547 	struct mtd_device *dev;
1548 	int err = 1;
1549 	char tmp_parts[MTDPARTS_MAXLEN];
1550 
1551 	debug("\n---parse_mtdparts---\nmtdparts = %s\n\n", p);
1552 
1553 	/* delete all devices and partitions */
1554 	if (mtd_devices_init() != 0) {
1555 		printf("could not initialise device list\n");
1556 		return err;
1557 	}
1558 
1559 	/* re-read 'mtdparts' variable, mtd_devices_init may be updating env */
1560 	p = getenv_mtdparts(tmp_parts);
1561 	if (!p)
1562 		p = mtdparts;
1563 
1564 	if (strncmp(p, "mtdparts=", 9) != 0) {
1565 		printf("mtdparts variable doesn't start with 'mtdparts='\n");
1566 		return err;
1567 	}
1568 	p += 9;
1569 
1570 	while (*p != '\0') {
1571 		err = 1;
1572 		if ((device_parse(p, &p, &dev) != 0) || (!dev))
1573 			break;
1574 
1575 		debug("+ device: %s\t%d\t%s\n", MTD_DEV_TYPE(dev->id->type),
1576 				dev->id->num, dev->id->mtd_id);
1577 
1578 		/* check if parsed device is already on the list */
1579 		if (device_find(dev->id->type, dev->id->num) != NULL) {
1580 			printf("device %s%d redefined, please correct mtdparts variable\n",
1581 					MTD_DEV_TYPE(dev->id->type), dev->id->num);
1582 			break;
1583 		}
1584 
1585 		list_add_tail(&dev->link, &devices);
1586 		err = 0;
1587 	}
1588 	if (err == 1)
1589 		device_delall(&devices);
1590 
1591 	return err;
1592 }
1593 
1594 /**
1595  * Parse provided string describing mtdids mapping (see file header for mtdids
1596  * variable format). Allocate memory for each entry and add all found entries
1597  * to the global mtdids list.
1598  *
1599  * @param ids mapping string
1600  * @return 0 on success, 1 otherwise
1601  */
1602 static int parse_mtdids(const char *const ids)
1603 {
1604 	const char *p = ids;
1605 	const char *mtd_id;
1606 	int mtd_id_len;
1607 	struct mtdids *id;
1608 	struct list_head *entry, *n;
1609 	struct mtdids *id_tmp;
1610 	u8 type, num;
1611 	u64 size;
1612 	int ret = 1;
1613 
1614 	debug("\n---parse_mtdids---\nmtdids = %s\n\n", ids);
1615 
1616 	/* clean global mtdids list */
1617 	list_for_each_safe(entry, n, &mtdids) {
1618 		id_tmp = list_entry(entry, struct mtdids, link);
1619 		debug("mtdids del: %d %d\n", id_tmp->type, id_tmp->num);
1620 		list_del(entry);
1621 		free(id_tmp);
1622 	}
1623 	last_ids[0] = '\0';
1624 	INIT_LIST_HEAD(&mtdids);
1625 
1626 	while(p && (*p != '\0')) {
1627 
1628 		ret = 1;
1629 		/* parse 'nor'|'nand'|'onenand'<dev-num> */
1630 		if (mtd_id_parse(p, &p, &type, &num) != 0)
1631 			break;
1632 
1633 		if (*p != '=') {
1634 			printf("mtdids: incorrect <dev-num>\n");
1635 			break;
1636 		}
1637 		p++;
1638 
1639 		/* check if requested device exists */
1640 		if (mtd_device_validate(type, num, &size) != 0)
1641 			return 1;
1642 
1643 		/* locate <mtd-id> */
1644 		mtd_id = p;
1645 		if ((p = strchr(mtd_id, ',')) != NULL) {
1646 			mtd_id_len = p - mtd_id + 1;
1647 			p++;
1648 		} else {
1649 			mtd_id_len = strlen(mtd_id) + 1;
1650 		}
1651 		if (mtd_id_len == 0) {
1652 			printf("mtdids: no <mtd-id> identifier\n");
1653 			break;
1654 		}
1655 
1656 		/* check if this id is already on the list */
1657 		int double_entry = 0;
1658 		list_for_each(entry, &mtdids) {
1659 			id_tmp = list_entry(entry, struct mtdids, link);
1660 			if ((id_tmp->type == type) && (id_tmp->num == num)) {
1661 				double_entry = 1;
1662 				break;
1663 			}
1664 		}
1665 		if (double_entry) {
1666 			printf("device id %s%d redefined, please correct mtdids variable\n",
1667 					MTD_DEV_TYPE(type), num);
1668 			break;
1669 		}
1670 
1671 		/* allocate mtdids structure */
1672 		if (!(id = (struct mtdids *)malloc(sizeof(struct mtdids) + mtd_id_len))) {
1673 			printf("out of memory\n");
1674 			break;
1675 		}
1676 		memset(id, 0, sizeof(struct mtdids) + mtd_id_len);
1677 		id->num = num;
1678 		id->type = type;
1679 		id->size = size;
1680 		id->mtd_id = (char *)(id + 1);
1681 		strncpy(id->mtd_id, mtd_id, mtd_id_len - 1);
1682 		id->mtd_id[mtd_id_len - 1] = '\0';
1683 		INIT_LIST_HEAD(&id->link);
1684 
1685 		debug("+ id %s%d\t%16lld bytes\t%s\n",
1686 				MTD_DEV_TYPE(id->type), id->num,
1687 				id->size, id->mtd_id);
1688 
1689 		list_add_tail(&id->link, &mtdids);
1690 		ret = 0;
1691 	}
1692 	if (ret == 1) {
1693 		/* clean mtdids list and free allocated memory */
1694 		list_for_each_safe(entry, n, &mtdids) {
1695 			id_tmp = list_entry(entry, struct mtdids, link);
1696 			list_del(entry);
1697 			free(id_tmp);
1698 		}
1699 		return 1;
1700 	}
1701 
1702 	return 0;
1703 }
1704 
1705 
1706 /**
1707  * Parse and initialize global mtdids mapping and create global
1708  * device/partition list.
1709  *
1710  * @return 0 on success, 1 otherwise
1711  */
1712 int mtdparts_init(void)
1713 {
1714 	static int initialized = 0;
1715 	const char *ids, *parts;
1716 	const char *current_partition;
1717 	int ids_changed;
1718 	char tmp_ep[PARTITION_MAXLEN];
1719 	char tmp_parts[MTDPARTS_MAXLEN];
1720 
1721 	debug("\n---mtdparts_init---\n");
1722 	if (!initialized) {
1723 		INIT_LIST_HEAD(&mtdids);
1724 		INIT_LIST_HEAD(&devices);
1725 		memset(last_ids, 0, MTDIDS_MAXLEN);
1726 		memset(last_parts, 0, MTDPARTS_MAXLEN);
1727 		memset(last_partition, 0, PARTITION_MAXLEN);
1728 #if defined(CONFIG_SYS_MTDPARTS_RUNTIME)
1729 		board_mtdparts_default(&mtdids_default, &mtdparts_default);
1730 #endif
1731 		use_defaults = 1;
1732 		initialized = 1;
1733 	}
1734 
1735 	/* get variables */
1736 	ids = getenv("mtdids");
1737 	parts = getenv_mtdparts(tmp_parts);
1738 	current_partition = getenv("partition");
1739 
1740 	/* save it for later parsing, cannot rely on current partition pointer
1741 	 * as 'partition' variable may be updated during init */
1742 	tmp_ep[0] = '\0';
1743 	if (current_partition)
1744 		strncpy(tmp_ep, current_partition, PARTITION_MAXLEN);
1745 
1746 	debug("last_ids  : %s\n", last_ids);
1747 	debug("env_ids   : %s\n", ids);
1748 	debug("last_parts: %s\n", last_parts);
1749 	debug("env_parts : %s\n\n", parts);
1750 
1751 	debug("last_partition : %s\n", last_partition);
1752 	debug("env_partition  : %s\n", current_partition);
1753 
1754 	/* if mtdids variable is empty try to use defaults */
1755 	if (!ids) {
1756 		if (mtdids_default) {
1757 			debug("mtdids variable not defined, using default\n");
1758 			ids = mtdids_default;
1759 			setenv("mtdids", (char *)ids);
1760 		} else {
1761 			printf("mtdids not defined, no default present\n");
1762 			return 1;
1763 		}
1764 	}
1765 	if (strlen(ids) > MTDIDS_MAXLEN - 1) {
1766 		printf("mtdids too long (> %d)\n", MTDIDS_MAXLEN);
1767 		return 1;
1768 	}
1769 
1770 	/* use defaults when mtdparts variable is not defined
1771 	 * once mtdparts is saved environment, drop use_defaults flag */
1772 	if (!parts) {
1773 		if (mtdparts_default && use_defaults) {
1774 			parts = mtdparts_default;
1775 			if (setenv("mtdparts", (char *)parts) == 0)
1776 				use_defaults = 0;
1777 		} else
1778 			printf("mtdparts variable not set, see 'help mtdparts'\n");
1779 	}
1780 
1781 	if (parts && (strlen(parts) > MTDPARTS_MAXLEN - 1)) {
1782 		printf("mtdparts too long (> %d)\n", MTDPARTS_MAXLEN);
1783 		return 1;
1784 	}
1785 
1786 	/* check if we have already parsed those mtdids */
1787 	if ((last_ids[0] != '\0') && (strcmp(last_ids, ids) == 0)) {
1788 		ids_changed = 0;
1789 	} else {
1790 		ids_changed = 1;
1791 
1792 		if (parse_mtdids(ids) != 0) {
1793 			mtd_devices_init();
1794 			return 1;
1795 		}
1796 
1797 		/* ok it's good, save new ids */
1798 		strncpy(last_ids, ids, MTDIDS_MAXLEN);
1799 	}
1800 
1801 	/* parse partitions if either mtdparts or mtdids were updated */
1802 	if (parts && ((last_parts[0] == '\0') || ((strcmp(last_parts, parts) != 0)) || ids_changed)) {
1803 		if (parse_mtdparts(parts) != 0)
1804 			return 1;
1805 
1806 		if (list_empty(&devices)) {
1807 			printf("mtdparts_init: no valid partitions\n");
1808 			return 1;
1809 		}
1810 
1811 		/* ok it's good, save new parts */
1812 		strncpy(last_parts, parts, MTDPARTS_MAXLEN);
1813 
1814 		/* reset first partition from first dev from the list as current */
1815 		current_mtd_dev = list_entry(devices.next, struct mtd_device, link);
1816 		current_mtd_partnum = 0;
1817 		current_save();
1818 
1819 		debug("mtdparts_init: current_mtd_dev  = %s%d, current_mtd_partnum = %d\n",
1820 				MTD_DEV_TYPE(current_mtd_dev->id->type),
1821 				current_mtd_dev->id->num, current_mtd_partnum);
1822 	}
1823 
1824 	/* mtdparts variable was reset to NULL, delete all devices/partitions */
1825 	if (!parts && (last_parts[0] != '\0'))
1826 		return mtd_devices_init();
1827 
1828 	/* do not process current partition if mtdparts variable is null */
1829 	if (!parts)
1830 		return 0;
1831 
1832 	/* is current partition set in environment? if so, use it */
1833 	if ((tmp_ep[0] != '\0') && (strcmp(tmp_ep, last_partition) != 0)) {
1834 		struct part_info *p;
1835 		struct mtd_device *cdev;
1836 		u8 pnum;
1837 
1838 		debug("--- getting current partition: %s\n", tmp_ep);
1839 
1840 		if (find_dev_and_part(tmp_ep, &cdev, &pnum, &p) == 0) {
1841 			current_mtd_dev = cdev;
1842 			current_mtd_partnum = pnum;
1843 			current_save();
1844 		}
1845 	} else if (getenv("partition") == NULL) {
1846 		debug("no partition variable set, setting...\n");
1847 		current_save();
1848 	}
1849 
1850 	return 0;
1851 }
1852 
1853 /**
1854  * Return pointer to the partition of a requested number from a requested
1855  * device.
1856  *
1857  * @param dev device that is to be searched for a partition
1858  * @param part_num requested partition number
1859  * @return pointer to the part_info, NULL otherwise
1860  */
1861 static struct part_info* mtd_part_info(struct mtd_device *dev, unsigned int part_num)
1862 {
1863 	struct list_head *entry;
1864 	struct part_info *part;
1865 	int num;
1866 
1867 	if (!dev)
1868 		return NULL;
1869 
1870 	debug("\n--- mtd_part_info: partition number %d for device %s%d (%s)\n",
1871 			part_num, MTD_DEV_TYPE(dev->id->type),
1872 			dev->id->num, dev->id->mtd_id);
1873 
1874 	if (part_num >= dev->num_parts) {
1875 		printf("invalid partition number %d for device %s%d (%s)\n",
1876 				part_num, MTD_DEV_TYPE(dev->id->type),
1877 				dev->id->num, dev->id->mtd_id);
1878 		return NULL;
1879 	}
1880 
1881 	/* locate partition number, return it */
1882 	num = 0;
1883 	list_for_each(entry, &dev->parts) {
1884 		part = list_entry(entry, struct part_info, link);
1885 
1886 		if (part_num == num++) {
1887 			return part;
1888 		}
1889 	}
1890 
1891 	return NULL;
1892 }
1893 
1894 /***************************************************/
1895 /* U-Boot commands				   */
1896 /***************************************************/
1897 /* command line only */
1898 /**
1899  * Routine implementing u-boot chpart command. Sets new current partition based
1900  * on the user supplied partition id. For partition id format see find_dev_and_part().
1901  *
1902  * @param cmdtp command internal data
1903  * @param flag command flag
1904  * @param argc number of arguments supplied to the command
1905  * @param argv arguments list
1906  * @return 0 on success, 1 otherwise
1907  */
1908 static int do_chpart(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
1909 {
1910 /* command line only */
1911 	struct mtd_device *dev;
1912 	struct part_info *part;
1913 	u8 pnum;
1914 
1915 	if (mtdparts_init() !=0)
1916 		return 1;
1917 
1918 	if (argc < 2) {
1919 		printf("no partition id specified\n");
1920 		return 1;
1921 	}
1922 
1923 	if (find_dev_and_part(argv[1], &dev, &pnum, &part) != 0)
1924 		return 1;
1925 
1926 	current_mtd_dev = dev;
1927 	current_mtd_partnum = pnum;
1928 	current_save();
1929 
1930 	printf("partition changed to %s%d,%d\n",
1931 			MTD_DEV_TYPE(dev->id->type), dev->id->num, pnum);
1932 
1933 	return 0;
1934 }
1935 
1936 /**
1937  * Routine implementing u-boot mtdparts command. Initialize/update default global
1938  * partition list and process user partition request (list, add, del).
1939  *
1940  * @param cmdtp command internal data
1941  * @param flag command flag
1942  * @param argc number of arguments supplied to the command
1943  * @param argv arguments list
1944  * @return 0 on success, 1 otherwise
1945  */
1946 static int do_mtdparts(cmd_tbl_t *cmdtp, int flag, int argc,
1947 		       char * const argv[])
1948 {
1949 	if (argc == 2) {
1950 		if (strcmp(argv[1], "default") == 0) {
1951 			setenv("mtdids", NULL);
1952 			setenv("mtdparts", NULL);
1953 			setenv("partition", NULL);
1954 			use_defaults = 1;
1955 
1956 			mtdparts_init();
1957 			return 0;
1958 		} else if (strcmp(argv[1], "delall") == 0) {
1959 			/* this may be the first run, initialize lists if needed */
1960 			mtdparts_init();
1961 
1962 			setenv("mtdparts", NULL);
1963 
1964 			/* mtd_devices_init() calls current_save() */
1965 			return mtd_devices_init();
1966 		}
1967 	}
1968 
1969 	/* make sure we are in sync with env variables */
1970 	if (mtdparts_init() != 0)
1971 		return 1;
1972 
1973 	if (argc == 1) {
1974 		list_partitions();
1975 		return 0;
1976 	}
1977 
1978 	/* mtdparts add <mtd-dev> <size>[@<offset>] <name> [ro] */
1979 	if (((argc == 5) || (argc == 6)) && (strncmp(argv[1], "add", 3) == 0)) {
1980 #define PART_ADD_DESC_MAXLEN 64
1981 		char tmpbuf[PART_ADD_DESC_MAXLEN];
1982 #if defined(CONFIG_CMD_MTDPARTS_SPREAD)
1983 		struct mtd_info *mtd;
1984 		uint64_t next_offset;
1985 #endif
1986 		u8 type, num, len;
1987 		struct mtd_device *dev;
1988 		struct mtd_device *dev_tmp;
1989 		struct mtdids *id;
1990 		struct part_info *p;
1991 
1992 		if (mtd_id_parse(argv[2], NULL, &type, &num) != 0)
1993 			return 1;
1994 
1995 		if ((id = id_find(type, num)) == NULL) {
1996 			printf("no such device %s defined in mtdids variable\n", argv[2]);
1997 			return 1;
1998 		}
1999 
2000 		len = strlen(id->mtd_id) + 1;	/* 'mtd_id:' */
2001 		len += strlen(argv[3]);		/* size@offset */
2002 		len += strlen(argv[4]) + 2;	/* '(' name ')' */
2003 		if (argv[5] && (strlen(argv[5]) == 2))
2004 			len += 2;		/* 'ro' */
2005 
2006 		if (len >= PART_ADD_DESC_MAXLEN) {
2007 			printf("too long partition description\n");
2008 			return 1;
2009 		}
2010 		sprintf(tmpbuf, "%s:%s(%s)%s",
2011 				id->mtd_id, argv[3], argv[4], argv[5] ? argv[5] : "");
2012 		debug("add tmpbuf: %s\n", tmpbuf);
2013 
2014 		if ((device_parse(tmpbuf, NULL, &dev) != 0) || (!dev))
2015 			return 1;
2016 
2017 		debug("+ %s\t%d\t%s\n", MTD_DEV_TYPE(dev->id->type),
2018 				dev->id->num, dev->id->mtd_id);
2019 
2020 		p = list_entry(dev->parts.next, struct part_info, link);
2021 
2022 #if defined(CONFIG_CMD_MTDPARTS_SPREAD)
2023 		if (get_mtd_info(dev->id->type, dev->id->num, &mtd))
2024 			return 1;
2025 
2026 		if (!strcmp(&argv[1][3], ".spread")) {
2027 			spread_partition(mtd, p, &next_offset);
2028 			debug("increased %s to %llu bytes\n", p->name, p->size);
2029 		}
2030 #endif
2031 
2032 		dev_tmp = device_find(dev->id->type, dev->id->num);
2033 		if (dev_tmp == NULL) {
2034 			device_add(dev);
2035 		} else if (part_add(dev_tmp, p) != 0) {
2036 			/* merge new partition with existing ones*/
2037 			device_del(dev);
2038 			return 1;
2039 		}
2040 
2041 		if (generate_mtdparts_save(last_parts, MTDPARTS_MAXLEN) != 0) {
2042 			printf("generated mtdparts too long, resetting to null\n");
2043 			return 1;
2044 		}
2045 
2046 		return 0;
2047 	}
2048 
2049 	/* mtdparts del part-id */
2050 	if ((argc == 3) && (strcmp(argv[1], "del") == 0)) {
2051 		debug("del: part-id = %s\n", argv[2]);
2052 
2053 		return delete_partition(argv[2]);
2054 	}
2055 
2056 #if defined(CONFIG_CMD_MTDPARTS_SPREAD)
2057 	if ((argc == 2) && (strcmp(argv[1], "spread") == 0))
2058 		return spread_partitions();
2059 #endif /* CONFIG_CMD_MTDPARTS_SPREAD */
2060 
2061 	return CMD_RET_USAGE;
2062 }
2063 
2064 /***************************************************/
2065 U_BOOT_CMD(
2066 	chpart,	2,	0,	do_chpart,
2067 	"change active partition",
2068 	"part-id\n"
2069 	"    - change active partition (e.g. part-id = nand0,1)"
2070 );
2071 
2072 #ifdef CONFIG_SYS_LONGHELP
2073 static char mtdparts_help_text[] =
2074 	"\n"
2075 	"    - list partition table\n"
2076 	"mtdparts delall\n"
2077 	"    - delete all partitions\n"
2078 	"mtdparts del part-id\n"
2079 	"    - delete partition (e.g. part-id = nand0,1)\n"
2080 	"mtdparts add <mtd-dev> <size>[@<offset>] [<name>] [ro]\n"
2081 	"    - add partition\n"
2082 #if defined(CONFIG_CMD_MTDPARTS_SPREAD)
2083 	"mtdparts add.spread <mtd-dev> <size>[@<offset>] [<name>] [ro]\n"
2084 	"    - add partition, padding size by skipping bad blocks\n"
2085 #endif
2086 	"mtdparts default\n"
2087 	"    - reset partition table to defaults\n"
2088 #if defined(CONFIG_CMD_MTDPARTS_SPREAD)
2089 	"mtdparts spread\n"
2090 	"    - adjust the sizes of the partitions so they are\n"
2091 	"      at least as big as the mtdparts variable specifies\n"
2092 	"      and they each start on a good block\n\n"
2093 #else
2094 	"\n"
2095 #endif /* CONFIG_CMD_MTDPARTS_SPREAD */
2096 	"-----\n\n"
2097 	"this command uses three environment variables:\n\n"
2098 	"'partition' - keeps current partition identifier\n\n"
2099 	"partition  := <part-id>\n"
2100 	"<part-id>  := <dev-id>,part_num\n\n"
2101 	"'mtdids' - linux kernel mtd device id <-> u-boot device id mapping\n\n"
2102 	"mtdids=<idmap>[,<idmap>,...]\n\n"
2103 	"<idmap>    := <dev-id>=<mtd-id>\n"
2104 	"<dev-id>   := 'nand'|'nor'|'onenand'<dev-num>\n"
2105 	"<dev-num>  := mtd device number, 0...\n"
2106 	"<mtd-id>   := unique device tag used by linux kernel to find mtd device (mtd->name)\n\n"
2107 	"'mtdparts' - partition list\n\n"
2108 	"mtdparts=mtdparts=<mtd-def>[;<mtd-def>...]\n\n"
2109 	"<mtd-def>  := <mtd-id>:<part-def>[,<part-def>...]\n"
2110 	"<mtd-id>   := unique device tag used by linux kernel to find mtd device (mtd->name)\n"
2111 	"<part-def> := <size>[@<offset>][<name>][<ro-flag>]\n"
2112 	"<size>     := standard linux memsize OR '-' to denote all remaining space\n"
2113 	"<offset>   := partition start offset within the device\n"
2114 	"<name>     := '(' NAME ')'\n"
2115 	"<ro-flag>  := when set to 'ro' makes partition read-only (not used, passed to kernel)";
2116 #endif
2117 
2118 U_BOOT_CMD(
2119 	mtdparts,	6,	0,	do_mtdparts,
2120 	"define flash/nand partitions", mtdparts_help_text
2121 );
2122 /***************************************************/
2123