Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Read flash partition table from command line
0004  *
0005  * Copyright © 2002      SYSGO Real-Time Solutions GmbH
0006  * Copyright © 2002-2010 David Woodhouse <dwmw2@infradead.org>
0007  *
0008  * The format for the command line is as follows:
0009  *
0010  * mtdparts=<mtddef>[;<mtddef]
0011  * <mtddef>  := <mtd-id>:<partdef>[,<partdef>]
0012  * <partdef> := <size>[@<offset>][<name>][ro][lk][slc]
0013  * <mtd-id>  := unique name used in mapping driver/device (mtd->name)
0014  * <size>    := standard linux memsize OR "-" to denote all remaining space
0015  *              size is automatically truncated at end of device
0016  *              if specified or truncated size is 0 the part is skipped
0017  * <offset>  := standard linux memsize
0018  *              if omitted the part will immediately follow the previous part
0019  *              or 0 if the first part
0020  * <name>    := '(' NAME ')'
0021  *              NAME will appear in /proc/mtd
0022  *
0023  * <size> and <offset> can be specified such that the parts are out of order
0024  * in physical memory and may even overlap.
0025  *
0026  * The parts are assigned MTD numbers in the order they are specified in the
0027  * command line regardless of their order in physical memory.
0028  *
0029  * Examples:
0030  *
0031  * 1 NOR Flash, with 1 single writable partition:
0032  * edb7312-nor:-
0033  *
0034  * 1 NOR Flash with 2 partitions, 1 NAND with one
0035  * edb7312-nor:256k(ARMboot)ro,-(root);edb7312-nand:-(home)
0036  */
0037 
0038 #define pr_fmt(fmt) "mtd: " fmt
0039 
0040 #include <linux/kernel.h>
0041 #include <linux/slab.h>
0042 #include <linux/mtd/mtd.h>
0043 #include <linux/mtd/partitions.h>
0044 #include <linux/module.h>
0045 #include <linux/err.h>
0046 
0047 /* debug macro */
0048 #if 0
0049 #define dbg(x) do { printk("DEBUG-CMDLINE-PART: "); printk x; } while(0)
0050 #else
0051 #define dbg(x)
0052 #endif
0053 
0054 
0055 /* special size referring to all the remaining space in a partition */
0056 #define SIZE_REMAINING ULLONG_MAX
0057 #define OFFSET_CONTINUOUS ULLONG_MAX
0058 
0059 struct cmdline_mtd_partition {
0060     struct cmdline_mtd_partition *next;
0061     char *mtd_id;
0062     int num_parts;
0063     struct mtd_partition *parts;
0064 };
0065 
0066 /* mtdpart_setup() parses into here */
0067 static struct cmdline_mtd_partition *partitions;
0068 
0069 /* the command line passed to mtdpart_setup() */
0070 static char *mtdparts;
0071 static char *cmdline;
0072 static int cmdline_parsed;
0073 
0074 /*
0075  * Parse one partition definition for an MTD. Since there can be many
0076  * comma separated partition definitions, this function calls itself
0077  * recursively until no more partition definitions are found. Nice side
0078  * effect: the memory to keep the mtd_partition structs and the names
0079  * is allocated upon the last definition being found. At that point the
0080  * syntax has been verified ok.
0081  */
0082 static struct mtd_partition * newpart(char *s,
0083                       char **retptr,
0084                       int *num_parts,
0085                       int this_part,
0086                       unsigned char **extra_mem_ptr,
0087                       int extra_mem_size)
0088 {
0089     struct mtd_partition *parts;
0090     unsigned long long size, offset = OFFSET_CONTINUOUS;
0091     char *name;
0092     int name_len;
0093     unsigned char *extra_mem;
0094     char delim;
0095     unsigned int mask_flags, add_flags;
0096 
0097     /* fetch the partition size */
0098     if (*s == '-') {
0099         /* assign all remaining space to this partition */
0100         size = SIZE_REMAINING;
0101         s++;
0102     } else {
0103         size = memparse(s, &s);
0104         if (!size) {
0105             pr_err("partition has size 0\n");
0106             return ERR_PTR(-EINVAL);
0107         }
0108     }
0109 
0110     /* fetch partition name and flags */
0111     mask_flags = 0; /* this is going to be a regular partition */
0112     add_flags = 0;
0113     delim = 0;
0114 
0115     /* check for offset */
0116     if (*s == '@') {
0117         s++;
0118         offset = memparse(s, &s);
0119     }
0120 
0121     /* now look for name */
0122     if (*s == '(')
0123         delim = ')';
0124 
0125     if (delim) {
0126         char *p;
0127 
0128         name = ++s;
0129         p = strchr(name, delim);
0130         if (!p) {
0131             pr_err("no closing %c found in partition name\n", delim);
0132             return ERR_PTR(-EINVAL);
0133         }
0134         name_len = p - name;
0135         s = p + 1;
0136     } else {
0137         name = NULL;
0138         name_len = 13; /* Partition_000 */
0139     }
0140 
0141     /* record name length for memory allocation later */
0142     extra_mem_size += name_len + 1;
0143 
0144     /* test for options */
0145     if (strncmp(s, "ro", 2) == 0) {
0146         mask_flags |= MTD_WRITEABLE;
0147         s += 2;
0148     }
0149 
0150     /* if lk is found do NOT unlock the MTD partition*/
0151     if (strncmp(s, "lk", 2) == 0) {
0152         mask_flags |= MTD_POWERUP_LOCK;
0153         s += 2;
0154     }
0155 
0156     /* if slc is found use emulated SLC mode on this partition*/
0157     if (!strncmp(s, "slc", 3)) {
0158         add_flags |= MTD_SLC_ON_MLC_EMULATION;
0159         s += 3;
0160     }
0161 
0162     /* test if more partitions are following */
0163     if (*s == ',') {
0164         if (size == SIZE_REMAINING) {
0165             pr_err("no partitions allowed after a fill-up partition\n");
0166             return ERR_PTR(-EINVAL);
0167         }
0168         /* more partitions follow, parse them */
0169         parts = newpart(s + 1, &s, num_parts, this_part + 1,
0170                 &extra_mem, extra_mem_size);
0171         if (IS_ERR(parts))
0172             return parts;
0173     } else {
0174         /* this is the last partition: allocate space for all */
0175         int alloc_size;
0176 
0177         *num_parts = this_part + 1;
0178         alloc_size = *num_parts * sizeof(struct mtd_partition) +
0179                  extra_mem_size;
0180 
0181         parts = kzalloc(alloc_size, GFP_KERNEL);
0182         if (!parts)
0183             return ERR_PTR(-ENOMEM);
0184         extra_mem = (unsigned char *)(parts + *num_parts);
0185     }
0186 
0187     /*
0188      * enter this partition (offset will be calculated later if it is
0189      * OFFSET_CONTINUOUS at this point)
0190      */
0191     parts[this_part].size = size;
0192     parts[this_part].offset = offset;
0193     parts[this_part].mask_flags = mask_flags;
0194     parts[this_part].add_flags = add_flags;
0195     if (name)
0196         strlcpy(extra_mem, name, name_len + 1);
0197     else
0198         sprintf(extra_mem, "Partition_%03d", this_part);
0199     parts[this_part].name = extra_mem;
0200     extra_mem += name_len + 1;
0201 
0202     dbg(("partition %d: name <%s>, offset %llx, size %llx, mask flags %x\n",
0203          this_part, parts[this_part].name, parts[this_part].offset,
0204          parts[this_part].size, parts[this_part].mask_flags));
0205 
0206     /* return (updated) pointer to extra_mem memory */
0207     if (extra_mem_ptr)
0208         *extra_mem_ptr = extra_mem;
0209 
0210     /* return (updated) pointer command line string */
0211     *retptr = s;
0212 
0213     /* return partition table */
0214     return parts;
0215 }
0216 
0217 /*
0218  * Parse the command line.
0219  */
0220 static int mtdpart_setup_real(char *s)
0221 {
0222     cmdline_parsed = 1;
0223 
0224     for( ; s != NULL; )
0225     {
0226         struct cmdline_mtd_partition *this_mtd;
0227         struct mtd_partition *parts;
0228         int mtd_id_len, num_parts;
0229         char *p, *mtd_id, *semicol, *open_parenth;
0230 
0231         /*
0232          * Replace the first ';' by a NULL char so strrchr can work
0233          * properly.
0234          */
0235         semicol = strchr(s, ';');
0236         if (semicol)
0237             *semicol = '\0';
0238 
0239         /*
0240          * make sure that part-names with ":" will not be handled as
0241          * part of the mtd-id with an ":"
0242          */
0243         open_parenth = strchr(s, '(');
0244         if (open_parenth)
0245             *open_parenth = '\0';
0246 
0247         mtd_id = s;
0248 
0249         /*
0250          * fetch <mtd-id>. We use strrchr to ignore all ':' that could
0251          * be present in the MTD name, only the last one is interpreted
0252          * as an <mtd-id>/<part-definition> separator.
0253          */
0254         p = strrchr(s, ':');
0255 
0256         /* Restore the '(' now. */
0257         if (open_parenth)
0258             *open_parenth = '(';
0259 
0260         /* Restore the ';' now. */
0261         if (semicol)
0262             *semicol = ';';
0263 
0264         if (!p) {
0265             pr_err("no mtd-id\n");
0266             return -EINVAL;
0267         }
0268         mtd_id_len = p - mtd_id;
0269 
0270         dbg(("parsing <%s>\n", p+1));
0271 
0272         /*
0273          * parse one mtd. have it reserve memory for the
0274          * struct cmdline_mtd_partition and the mtd-id string.
0275          */
0276         parts = newpart(p + 1,      /* cmdline */
0277                 &s,     /* out: updated cmdline ptr */
0278                 &num_parts, /* out: number of parts */
0279                 0,      /* first partition */
0280                 (unsigned char**)&this_mtd, /* out: extra mem */
0281                 mtd_id_len + 1 + sizeof(*this_mtd) +
0282                 sizeof(void*)-1 /*alignment*/);
0283         if (IS_ERR(parts)) {
0284             /*
0285              * An error occurred. We're either:
0286              * a) out of memory, or
0287              * b) in the middle of the partition spec
0288              * Either way, this mtd is hosed and we're
0289              * unlikely to succeed in parsing any more
0290              */
0291              return PTR_ERR(parts);
0292          }
0293 
0294         /* align this_mtd */
0295         this_mtd = (struct cmdline_mtd_partition *)
0296                 ALIGN((unsigned long)this_mtd, sizeof(void *));
0297         /* enter results */
0298         this_mtd->parts = parts;
0299         this_mtd->num_parts = num_parts;
0300         this_mtd->mtd_id = (char*)(this_mtd + 1);
0301         strlcpy(this_mtd->mtd_id, mtd_id, mtd_id_len + 1);
0302 
0303         /* link into chain */
0304         this_mtd->next = partitions;
0305         partitions = this_mtd;
0306 
0307         dbg(("mtdid=<%s> num_parts=<%d>\n",
0308              this_mtd->mtd_id, this_mtd->num_parts));
0309 
0310 
0311         /* EOS - we're done */
0312         if (*s == 0)
0313             break;
0314 
0315         /* does another spec follow? */
0316         if (*s != ';') {
0317             pr_err("bad character after partition (%c)\n", *s);
0318             return -EINVAL;
0319         }
0320         s++;
0321     }
0322 
0323     return 0;
0324 }
0325 
0326 /*
0327  * Main function to be called from the MTD mapping driver/device to
0328  * obtain the partitioning information. At this point the command line
0329  * arguments will actually be parsed and turned to struct mtd_partition
0330  * information. It returns partitions for the requested mtd device, or
0331  * the first one in the chain if a NULL mtd_id is passed in.
0332  */
0333 static int parse_cmdline_partitions(struct mtd_info *master,
0334                     const struct mtd_partition **pparts,
0335                     struct mtd_part_parser_data *data)
0336 {
0337     unsigned long long offset;
0338     int i, err;
0339     struct cmdline_mtd_partition *part;
0340     const char *mtd_id = master->name;
0341 
0342     /* parse command line */
0343     if (!cmdline_parsed) {
0344         err = mtdpart_setup_real(cmdline);
0345         if (err)
0346             return err;
0347     }
0348 
0349     /*
0350      * Search for the partition definition matching master->name.
0351      * If master->name is not set, stop at first partition definition.
0352      */
0353     for (part = partitions; part; part = part->next) {
0354         if ((!mtd_id) || (!strcmp(part->mtd_id, mtd_id)))
0355             break;
0356     }
0357 
0358     if (!part)
0359         return 0;
0360 
0361     for (i = 0, offset = 0; i < part->num_parts; i++) {
0362         if (part->parts[i].offset == OFFSET_CONTINUOUS)
0363             part->parts[i].offset = offset;
0364         else
0365             offset = part->parts[i].offset;
0366 
0367         if (part->parts[i].size == SIZE_REMAINING)
0368             part->parts[i].size = master->size - offset;
0369 
0370         if (offset + part->parts[i].size > master->size) {
0371             pr_warn("%s: partitioning exceeds flash size, truncating\n",
0372                 part->mtd_id);
0373             part->parts[i].size = master->size - offset;
0374         }
0375         offset += part->parts[i].size;
0376 
0377         if (part->parts[i].size == 0) {
0378             pr_warn("%s: skipping zero sized partition\n",
0379                 part->mtd_id);
0380             part->num_parts--;
0381             memmove(&part->parts[i], &part->parts[i + 1],
0382                 sizeof(*part->parts) * (part->num_parts - i));
0383             i--;
0384         }
0385     }
0386 
0387     *pparts = kmemdup(part->parts, sizeof(*part->parts) * part->num_parts,
0388               GFP_KERNEL);
0389     if (!*pparts)
0390         return -ENOMEM;
0391 
0392     return part->num_parts;
0393 }
0394 
0395 
0396 /*
0397  * This is the handler for our kernel parameter, called from
0398  * main.c::checksetup(). Note that we can not yet kmalloc() anything,
0399  * so we only save the commandline for later processing.
0400  *
0401  * This function needs to be visible for bootloaders.
0402  */
0403 static int __init mtdpart_setup(char *s)
0404 {
0405     cmdline = s;
0406     return 1;
0407 }
0408 
0409 __setup("mtdparts=", mtdpart_setup);
0410 
0411 static struct mtd_part_parser cmdline_parser = {
0412     .parse_fn = parse_cmdline_partitions,
0413     .name = "cmdlinepart",
0414 };
0415 
0416 static int __init cmdline_parser_init(void)
0417 {
0418     if (mtdparts)
0419         mtdpart_setup(mtdparts);
0420     register_mtd_parser(&cmdline_parser);
0421     return 0;
0422 }
0423 
0424 static void __exit cmdline_parser_exit(void)
0425 {
0426     deregister_mtd_parser(&cmdline_parser);
0427 }
0428 
0429 module_init(cmdline_parser_init);
0430 module_exit(cmdline_parser_exit);
0431 
0432 MODULE_PARM_DESC(mtdparts, "Partitioning specification");
0433 module_param(mtdparts, charp, 0);
0434 
0435 MODULE_LICENSE("GPL");
0436 MODULE_AUTHOR("Marius Groeger <mag@sysgo.de>");
0437 MODULE_DESCRIPTION("Command line configuration of MTD partitions");