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