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