1fd534e9bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * Read flash partition table from command line
41da177e4SLinus Torvalds *
5a1452a37SDavid Woodhouse * Copyright © 2002 SYSGO Real-Time Solutions GmbH
6a1452a37SDavid Woodhouse * Copyright © 2002-2010 David Woodhouse <dwmw2@infradead.org>
7a1452a37SDavid Woodhouse *
81da177e4SLinus Torvalds * The format for the command line is as follows:
91da177e4SLinus Torvalds *
101da177e4SLinus Torvalds * mtdparts=<mtddef>[;<mtddef]
111da177e4SLinus Torvalds * <mtddef> := <mtd-id>:<partdef>[,<partdef>]
12568d841bSBoris Brezillon * <partdef> := <size>[@<offset>][<name>][ro][lk][slc]
131da177e4SLinus Torvalds * <mtd-id> := unique name used in mapping driver/device (mtd->name)
141da177e4SLinus Torvalds * <size> := standard linux memsize OR "-" to denote all remaining space
15ea8b8e27SChristopher Cordahi * size is automatically truncated at end of device
1629f63f65SGeert Uytterhoeven * if specified or truncated size is 0 the part is skipped
17ea8b8e27SChristopher Cordahi * <offset> := standard linux memsize
18ea8b8e27SChristopher Cordahi * if omitted the part will immediately follow the previous part
19ea8b8e27SChristopher Cordahi * or 0 if the first part
201da177e4SLinus Torvalds * <name> := '(' NAME ')'
21bd6ce5efSChristopher Cordahi * NAME will appear in /proc/mtd
221da177e4SLinus Torvalds *
23ea8b8e27SChristopher Cordahi * <size> and <offset> can be specified such that the parts are out of order
24ea8b8e27SChristopher Cordahi * in physical memory and may even overlap.
25ea8b8e27SChristopher Cordahi *
26ea8b8e27SChristopher Cordahi * The parts are assigned MTD numbers in the order they are specified in the
27ea8b8e27SChristopher Cordahi * command line regardless of their order in physical memory.
28ea8b8e27SChristopher Cordahi *
291da177e4SLinus Torvalds * Examples:
301da177e4SLinus Torvalds *
311da177e4SLinus Torvalds * 1 NOR Flash, with 1 single writable partition:
321da177e4SLinus Torvalds * edb7312-nor:-
331da177e4SLinus Torvalds *
341da177e4SLinus Torvalds * 1 NOR Flash with 2 partitions, 1 NAND with one
351da177e4SLinus Torvalds * edb7312-nor:256k(ARMboot)ro,-(root);edb7312-nand:-(home)
361da177e4SLinus Torvalds */
371da177e4SLinus Torvalds
38ecb43e0aSBrian Norris #define pr_fmt(fmt) "mtd: " fmt
39ecb43e0aSBrian Norris
401da177e4SLinus Torvalds #include <linux/kernel.h>
411da177e4SLinus Torvalds #include <linux/slab.h>
421da177e4SLinus Torvalds #include <linux/mtd/mtd.h>
431da177e4SLinus Torvalds #include <linux/mtd/partitions.h>
44a0e5cc58SPaul Gortmaker #include <linux/module.h>
459e0606fcSArtem Bityutskiy #include <linux/err.h>
461da177e4SLinus Torvalds
471da177e4SLinus Torvalds /* special size referring to all the remaining space in a partition */
486f9f59eeSHuang Shijie #define SIZE_REMAINING ULLONG_MAX
496f9f59eeSHuang Shijie #define OFFSET_CONTINUOUS ULLONG_MAX
501da177e4SLinus Torvalds
511da177e4SLinus Torvalds struct cmdline_mtd_partition {
521da177e4SLinus Torvalds struct cmdline_mtd_partition *next;
531da177e4SLinus Torvalds char *mtd_id;
541da177e4SLinus Torvalds int num_parts;
551da177e4SLinus Torvalds struct mtd_partition *parts;
561da177e4SLinus Torvalds };
571da177e4SLinus Torvalds
581da177e4SLinus Torvalds /* mtdpart_setup() parses into here */
591da177e4SLinus Torvalds static struct cmdline_mtd_partition *partitions;
601da177e4SLinus Torvalds
618abc0d4aSPeter Meerwald /* the command line passed to mtdpart_setup() */
62f5f172dcSLubomir Rintel static char *mtdparts;
631da177e4SLinus Torvalds static char *cmdline;
64aa3c5dc5SArtem Bityutskiy static int cmdline_parsed;
651da177e4SLinus Torvalds
661da177e4SLinus Torvalds /*
671da177e4SLinus Torvalds * Parse one partition definition for an MTD. Since there can be many
681da177e4SLinus Torvalds * comma separated partition definitions, this function calls itself
691da177e4SLinus Torvalds * recursively until no more partition definitions are found. Nice side
701da177e4SLinus Torvalds * effect: the memory to keep the mtd_partition structs and the names
711da177e4SLinus Torvalds * is allocated upon the last definition being found. At that point the
721da177e4SLinus Torvalds * syntax has been verified ok.
731da177e4SLinus Torvalds */
newpart(char * s,char ** retptr,int * num_parts,int this_part,unsigned char ** extra_mem_ptr,int extra_mem_size)741da177e4SLinus Torvalds static struct mtd_partition * newpart(char *s,
751da177e4SLinus Torvalds char **retptr,
761da177e4SLinus Torvalds int *num_parts,
771da177e4SLinus Torvalds int this_part,
781da177e4SLinus Torvalds unsigned char **extra_mem_ptr,
791da177e4SLinus Torvalds int extra_mem_size)
801da177e4SLinus Torvalds {
811da177e4SLinus Torvalds struct mtd_partition *parts;
826f9f59eeSHuang Shijie unsigned long long size, offset = OFFSET_CONTINUOUS;
831da177e4SLinus Torvalds char *name;
841da177e4SLinus Torvalds int name_len;
851da177e4SLinus Torvalds unsigned char *extra_mem;
861da177e4SLinus Torvalds char delim;
87568d841bSBoris Brezillon unsigned int mask_flags, add_flags;
881da177e4SLinus Torvalds
891da177e4SLinus Torvalds /* fetch the partition size */
90fac0077cSArtem Bityutskiy if (*s == '-') {
91fac0077cSArtem Bityutskiy /* assign all remaining space to this partition */
921da177e4SLinus Torvalds size = SIZE_REMAINING;
931da177e4SLinus Torvalds s++;
94fac0077cSArtem Bityutskiy } else {
951da177e4SLinus Torvalds size = memparse(s, &s);
96d855d23bSBrian Norris if (!size) {
97ecb43e0aSBrian Norris pr_err("partition has size 0\n");
989e0606fcSArtem Bityutskiy return ERR_PTR(-EINVAL);
991da177e4SLinus Torvalds }
1001da177e4SLinus Torvalds }
1011da177e4SLinus Torvalds
1021da177e4SLinus Torvalds /* fetch partition name and flags */
1031da177e4SLinus Torvalds mask_flags = 0; /* this is going to be a regular partition */
104568d841bSBoris Brezillon add_flags = 0;
1051da177e4SLinus Torvalds delim = 0;
106fac0077cSArtem Bityutskiy
1071da177e4SLinus Torvalds /* check for offset */
108fac0077cSArtem Bityutskiy if (*s == '@') {
1091da177e4SLinus Torvalds s++;
1101da177e4SLinus Torvalds offset = memparse(s, &s);
1111da177e4SLinus Torvalds }
112fac0077cSArtem Bityutskiy
1131da177e4SLinus Torvalds /* now look for name */
1141da177e4SLinus Torvalds if (*s == '(')
1151da177e4SLinus Torvalds delim = ')';
1161da177e4SLinus Torvalds
117fac0077cSArtem Bityutskiy if (delim) {
1181da177e4SLinus Torvalds char *p;
1191da177e4SLinus Torvalds
1201da177e4SLinus Torvalds name = ++s;
121ed262c4fSAdrian Bunk p = strchr(name, delim);
122fac0077cSArtem Bityutskiy if (!p) {
123ecb43e0aSBrian Norris pr_err("no closing %c found in partition name\n", delim);
1249e0606fcSArtem Bityutskiy return ERR_PTR(-EINVAL);
1251da177e4SLinus Torvalds }
1261da177e4SLinus Torvalds name_len = p - name;
1271da177e4SLinus Torvalds s = p + 1;
128fac0077cSArtem Bityutskiy } else {
1291da177e4SLinus Torvalds name = NULL;
1301da177e4SLinus Torvalds name_len = 13; /* Partition_000 */
1311da177e4SLinus Torvalds }
1321da177e4SLinus Torvalds
1331da177e4SLinus Torvalds /* record name length for memory allocation later */
1341da177e4SLinus Torvalds extra_mem_size += name_len + 1;
1351da177e4SLinus Torvalds
1361da177e4SLinus Torvalds /* test for options */
137fac0077cSArtem Bityutskiy if (strncmp(s, "ro", 2) == 0) {
1381da177e4SLinus Torvalds mask_flags |= MTD_WRITEABLE;
1391da177e4SLinus Torvalds s += 2;
1401da177e4SLinus Torvalds }
1411da177e4SLinus Torvalds
142e619a75fSJustin Treon /* if lk is found do NOT unlock the MTD partition*/
143fac0077cSArtem Bityutskiy if (strncmp(s, "lk", 2) == 0) {
144e619a75fSJustin Treon mask_flags |= MTD_POWERUP_LOCK;
145e619a75fSJustin Treon s += 2;
146e619a75fSJustin Treon }
147e619a75fSJustin Treon
148568d841bSBoris Brezillon /* if slc is found use emulated SLC mode on this partition*/
149568d841bSBoris Brezillon if (!strncmp(s, "slc", 3)) {
150568d841bSBoris Brezillon add_flags |= MTD_SLC_ON_MLC_EMULATION;
151568d841bSBoris Brezillon s += 3;
152568d841bSBoris Brezillon }
153568d841bSBoris Brezillon
1541da177e4SLinus Torvalds /* test if more partitions are following */
155fac0077cSArtem Bityutskiy if (*s == ',') {
156fac0077cSArtem Bityutskiy if (size == SIZE_REMAINING) {
157ecb43e0aSBrian Norris pr_err("no partitions allowed after a fill-up partition\n");
1589e0606fcSArtem Bityutskiy return ERR_PTR(-EINVAL);
1591da177e4SLinus Torvalds }
1601da177e4SLinus Torvalds /* more partitions follow, parse them */
161ed262c4fSAdrian Bunk parts = newpart(s + 1, &s, num_parts, this_part + 1,
162ed262c4fSAdrian Bunk &extra_mem, extra_mem_size);
1639e0606fcSArtem Bityutskiy if (IS_ERR(parts))
1649e0606fcSArtem Bityutskiy return parts;
165fac0077cSArtem Bityutskiy } else {
166fac0077cSArtem Bityutskiy /* this is the last partition: allocate space for all */
1671da177e4SLinus Torvalds int alloc_size;
1681da177e4SLinus Torvalds
1691da177e4SLinus Torvalds *num_parts = this_part + 1;
1701da177e4SLinus Torvalds alloc_size = *num_parts * sizeof(struct mtd_partition) +
1711da177e4SLinus Torvalds extra_mem_size;
172fac0077cSArtem Bityutskiy
17395b93a0cSBurman Yan parts = kzalloc(alloc_size, GFP_KERNEL);
1741da177e4SLinus Torvalds if (!parts)
1759e0606fcSArtem Bityutskiy return ERR_PTR(-ENOMEM);
1761da177e4SLinus Torvalds extra_mem = (unsigned char *)(parts + *num_parts);
1771da177e4SLinus Torvalds }
178fac0077cSArtem Bityutskiy
17934c81907SGeert Uytterhoeven /*
18034c81907SGeert Uytterhoeven * enter this partition (offset will be calculated later if it is
18134c81907SGeert Uytterhoeven * OFFSET_CONTINUOUS at this point)
18234c81907SGeert Uytterhoeven */
1831da177e4SLinus Torvalds parts[this_part].size = size;
1841da177e4SLinus Torvalds parts[this_part].offset = offset;
1851da177e4SLinus Torvalds parts[this_part].mask_flags = mask_flags;
186568d841bSBoris Brezillon parts[this_part].add_flags = add_flags;
1871da177e4SLinus Torvalds if (name)
18880b7e928SWolfram Sang strscpy(extra_mem, name, name_len + 1);
1891da177e4SLinus Torvalds else
1901da177e4SLinus Torvalds sprintf(extra_mem, "Partition_%03d", this_part);
1911da177e4SLinus Torvalds parts[this_part].name = extra_mem;
1921da177e4SLinus Torvalds extra_mem += name_len + 1;
1931da177e4SLinus Torvalds
1942538af03SCsókás, Bence pr_debug("partition %d: name <%s>, offset %llx, size %llx, mask flags %x\n",
195fac0077cSArtem Bityutskiy this_part, parts[this_part].name, parts[this_part].offset,
1962538af03SCsókás, Bence parts[this_part].size, parts[this_part].mask_flags);
1971da177e4SLinus Torvalds
1981da177e4SLinus Torvalds /* return (updated) pointer to extra_mem memory */
1991da177e4SLinus Torvalds if (extra_mem_ptr)
2001da177e4SLinus Torvalds *extra_mem_ptr = extra_mem;
2011da177e4SLinus Torvalds
2021da177e4SLinus Torvalds /* return (updated) pointer command line string */
2031da177e4SLinus Torvalds *retptr = s;
2041da177e4SLinus Torvalds
2051da177e4SLinus Torvalds /* return partition table */
2061da177e4SLinus Torvalds return parts;
2071da177e4SLinus Torvalds }
2081da177e4SLinus Torvalds
2091da177e4SLinus Torvalds /*
2101da177e4SLinus Torvalds * Parse the command line.
2111da177e4SLinus Torvalds */
mtdpart_setup_real(char * s)2121da177e4SLinus Torvalds static int mtdpart_setup_real(char *s)
2131da177e4SLinus Torvalds {
2141da177e4SLinus Torvalds cmdline_parsed = 1;
2151da177e4SLinus Torvalds
2161da177e4SLinus Torvalds for( ; s != NULL; )
2171da177e4SLinus Torvalds {
2181da177e4SLinus Torvalds struct cmdline_mtd_partition *this_mtd;
2191da177e4SLinus Torvalds struct mtd_partition *parts;
220fac0077cSArtem Bityutskiy int mtd_id_len, num_parts;
221639a8243SSven Eckelmann char *p, *mtd_id, *semicol, *open_parenth;
222eb13fa02SBoris Brezillon
223eb13fa02SBoris Brezillon /*
224eb13fa02SBoris Brezillon * Replace the first ';' by a NULL char so strrchr can work
225eb13fa02SBoris Brezillon * properly.
226eb13fa02SBoris Brezillon */
227eb13fa02SBoris Brezillon semicol = strchr(s, ';');
228eb13fa02SBoris Brezillon if (semicol)
229eb13fa02SBoris Brezillon *semicol = '\0';
2301da177e4SLinus Torvalds
231639a8243SSven Eckelmann /*
232639a8243SSven Eckelmann * make sure that part-names with ":" will not be handled as
233639a8243SSven Eckelmann * part of the mtd-id with an ":"
234639a8243SSven Eckelmann */
235639a8243SSven Eckelmann open_parenth = strchr(s, '(');
236639a8243SSven Eckelmann if (open_parenth)
237639a8243SSven Eckelmann *open_parenth = '\0';
238639a8243SSven Eckelmann
2391da177e4SLinus Torvalds mtd_id = s;
240fac0077cSArtem Bityutskiy
241eb13fa02SBoris Brezillon /*
242eb13fa02SBoris Brezillon * fetch <mtd-id>. We use strrchr to ignore all ':' that could
243eb13fa02SBoris Brezillon * be present in the MTD name, only the last one is interpreted
244eb13fa02SBoris Brezillon * as an <mtd-id>/<part-definition> separator.
245eb13fa02SBoris Brezillon */
246eb13fa02SBoris Brezillon p = strrchr(s, ':');
247eb13fa02SBoris Brezillon
248639a8243SSven Eckelmann /* Restore the '(' now. */
249639a8243SSven Eckelmann if (open_parenth)
250639a8243SSven Eckelmann *open_parenth = '(';
251639a8243SSven Eckelmann
252eb13fa02SBoris Brezillon /* Restore the ';' now. */
253eb13fa02SBoris Brezillon if (semicol)
254eb13fa02SBoris Brezillon *semicol = ';';
255eb13fa02SBoris Brezillon
256fac0077cSArtem Bityutskiy if (!p) {
257ecb43e0aSBrian Norris pr_err("no mtd-id\n");
2589e0606fcSArtem Bityutskiy return -EINVAL;
2591da177e4SLinus Torvalds }
2601da177e4SLinus Torvalds mtd_id_len = p - mtd_id;
2611da177e4SLinus Torvalds
2622538af03SCsókás, Bence pr_debug("parsing <%s>\n", p+1);
2631da177e4SLinus Torvalds
2641da177e4SLinus Torvalds /*
2651da177e4SLinus Torvalds * parse one mtd. have it reserve memory for the
2661da177e4SLinus Torvalds * struct cmdline_mtd_partition and the mtd-id string.
2671da177e4SLinus Torvalds */
2681da177e4SLinus Torvalds parts = newpart(p + 1, /* cmdline */
2691da177e4SLinus Torvalds &s, /* out: updated cmdline ptr */
2701da177e4SLinus Torvalds &num_parts, /* out: number of parts */
2711da177e4SLinus Torvalds 0, /* first partition */
2721da177e4SLinus Torvalds (unsigned char**)&this_mtd, /* out: extra mem */
273be76c5fbSJoern Engel mtd_id_len + 1 + sizeof(*this_mtd) +
274be76c5fbSJoern Engel sizeof(void*)-1 /*alignment*/);
275fac0077cSArtem Bityutskiy if (IS_ERR(parts)) {
2761da177e4SLinus Torvalds /*
2771da177e4SLinus Torvalds * An error occurred. We're either:
2781da177e4SLinus Torvalds * a) out of memory, or
2791da177e4SLinus Torvalds * b) in the middle of the partition spec
2801da177e4SLinus Torvalds * Either way, this mtd is hosed and we're
2811da177e4SLinus Torvalds * unlikely to succeed in parsing any more
2821da177e4SLinus Torvalds */
2839e0606fcSArtem Bityutskiy return PTR_ERR(parts);
2841da177e4SLinus Torvalds }
2851da177e4SLinus Torvalds
286be76c5fbSJoern Engel /* align this_mtd */
287be76c5fbSJoern Engel this_mtd = (struct cmdline_mtd_partition *)
288be76c5fbSJoern Engel ALIGN((unsigned long)this_mtd, sizeof(void *));
2891da177e4SLinus Torvalds /* enter results */
2901da177e4SLinus Torvalds this_mtd->parts = parts;
2911da177e4SLinus Torvalds this_mtd->num_parts = num_parts;
2921da177e4SLinus Torvalds this_mtd->mtd_id = (char*)(this_mtd + 1);
29380b7e928SWolfram Sang strscpy(this_mtd->mtd_id, mtd_id, mtd_id_len + 1);
2941da177e4SLinus Torvalds
2951da177e4SLinus Torvalds /* link into chain */
2961da177e4SLinus Torvalds this_mtd->next = partitions;
2971da177e4SLinus Torvalds partitions = this_mtd;
2981da177e4SLinus Torvalds
2992538af03SCsókás, Bence pr_debug("mtdid=<%s> num_parts=<%d>\n",
3002538af03SCsókás, Bence this_mtd->mtd_id, this_mtd->num_parts);
3011da177e4SLinus Torvalds
3021da177e4SLinus Torvalds
3031da177e4SLinus Torvalds /* EOS - we're done */
3041da177e4SLinus Torvalds if (*s == 0)
3051da177e4SLinus Torvalds break;
3061da177e4SLinus Torvalds
3071da177e4SLinus Torvalds /* does another spec follow? */
308fac0077cSArtem Bityutskiy if (*s != ';') {
309ecb43e0aSBrian Norris pr_err("bad character after partition (%c)\n", *s);
3109e0606fcSArtem Bityutskiy return -EINVAL;
3111da177e4SLinus Torvalds }
3121da177e4SLinus Torvalds s++;
3131da177e4SLinus Torvalds }
314fac0077cSArtem Bityutskiy
3159e0606fcSArtem Bityutskiy return 0;
3161da177e4SLinus Torvalds }
3171da177e4SLinus Torvalds
3181da177e4SLinus Torvalds /*
3191da177e4SLinus Torvalds * Main function to be called from the MTD mapping driver/device to
3201da177e4SLinus Torvalds * obtain the partitioning information. At this point the command line
3211da177e4SLinus Torvalds * arguments will actually be parsed and turned to struct mtd_partition
3221da177e4SLinus Torvalds * information. It returns partitions for the requested mtd device, or
3231da177e4SLinus Torvalds * the first one in the chain if a NULL mtd_id is passed in.
3241da177e4SLinus Torvalds */
parse_cmdline_partitions(struct mtd_info * master,const struct mtd_partition ** pparts,struct mtd_part_parser_data * data)3251da177e4SLinus Torvalds static int parse_cmdline_partitions(struct mtd_info *master,
326b9adf469SBrian Norris const struct mtd_partition **pparts,
327c7975330SDmitry Eremin-Solenikov struct mtd_part_parser_data *data)
3281da177e4SLinus Torvalds {
3296f9f59eeSHuang Shijie unsigned long long offset;
3309e0606fcSArtem Bityutskiy int i, err;
3311da177e4SLinus Torvalds struct cmdline_mtd_partition *part;
33236560d25SDavid Howells const char *mtd_id = master->name;
3331da177e4SLinus Torvalds
3341da177e4SLinus Torvalds /* parse command line */
3359e0606fcSArtem Bityutskiy if (!cmdline_parsed) {
3369e0606fcSArtem Bityutskiy err = mtdpart_setup_real(cmdline);
3379e0606fcSArtem Bityutskiy if (err)
3389e0606fcSArtem Bityutskiy return err;
3399e0606fcSArtem Bityutskiy }
3401da177e4SLinus Torvalds
341438db5a9SShmulik Ladkani /*
342438db5a9SShmulik Ladkani * Search for the partition definition matching master->name.
343438db5a9SShmulik Ladkani * If master->name is not set, stop at first partition definition.
344438db5a9SShmulik Ladkani */
345fac0077cSArtem Bityutskiy for (part = partitions; part; part = part->next) {
346438db5a9SShmulik Ladkani if ((!mtd_id) || (!strcmp(part->mtd_id, mtd_id)))
347438db5a9SShmulik Ladkani break;
348438db5a9SShmulik Ladkani }
349438db5a9SShmulik Ladkani
350438db5a9SShmulik Ladkani if (!part)
351438db5a9SShmulik Ladkani return 0;
352438db5a9SShmulik Ladkani
353fac0077cSArtem Bityutskiy for (i = 0, offset = 0; i < part->num_parts; i++) {
354b175d03dSAtsushi Nemoto if (part->parts[i].offset == OFFSET_CONTINUOUS)
3551da177e4SLinus Torvalds part->parts[i].offset = offset;
3561da177e4SLinus Torvalds else
3571da177e4SLinus Torvalds offset = part->parts[i].offset;
358fac0077cSArtem Bityutskiy
3591da177e4SLinus Torvalds if (part->parts[i].size == SIZE_REMAINING)
3601da177e4SLinus Torvalds part->parts[i].size = master->size - offset;
361fac0077cSArtem Bityutskiy
362ebf4f070SChristopher Cordahi if (offset + part->parts[i].size > master->size) {
363ecb43e0aSBrian Norris pr_warn("%s: partitioning exceeds flash size, truncating\n",
364ebf4f070SChristopher Cordahi part->mtd_id);
365ebf4f070SChristopher Cordahi part->parts[i].size = master->size - offset;
366ebf4f070SChristopher Cordahi }
367ebf4f070SChristopher Cordahi offset += part->parts[i].size;
368ebf4f070SChristopher Cordahi
3697baf0426SShmulik Ladkani if (part->parts[i].size == 0) {
370ecb43e0aSBrian Norris pr_warn("%s: skipping zero sized partition\n",
3717baf0426SShmulik Ladkani part->mtd_id);
3727baf0426SShmulik Ladkani part->num_parts--;
373438db5a9SShmulik Ladkani memmove(&part->parts[i], &part->parts[i + 1],
3747baf0426SShmulik Ladkani sizeof(*part->parts) * (part->num_parts - i));
375e25e0a4dSChristopher Cordahi i--;
3767baf0426SShmulik Ladkani }
3771da177e4SLinus Torvalds }
378fac0077cSArtem Bityutskiy
379438db5a9SShmulik Ladkani *pparts = kmemdup(part->parts, sizeof(*part->parts) * part->num_parts,
38017b536ccSAtsushi Nemoto GFP_KERNEL);
38117b536ccSAtsushi Nemoto if (!*pparts)
38217b536ccSAtsushi Nemoto return -ENOMEM;
383fac0077cSArtem Bityutskiy
3841da177e4SLinus Torvalds return part->num_parts;
3851da177e4SLinus Torvalds }
3861da177e4SLinus Torvalds
3871da177e4SLinus Torvalds
3881da177e4SLinus Torvalds /*
3891da177e4SLinus Torvalds * This is the handler for our kernel parameter, called from
3901da177e4SLinus Torvalds * main.c::checksetup(). Note that we can not yet kmalloc() anything,
3911da177e4SLinus Torvalds * so we only save the commandline for later processing.
3921da177e4SLinus Torvalds *
3931da177e4SLinus Torvalds * This function needs to be visible for bootloaders.
3941da177e4SLinus Torvalds */
mtdpart_setup(char * s)395f5f172dcSLubomir Rintel static int __init mtdpart_setup(char *s)
3961da177e4SLinus Torvalds {
3971da177e4SLinus Torvalds cmdline = s;
3981da177e4SLinus Torvalds return 1;
3991da177e4SLinus Torvalds }
4001da177e4SLinus Torvalds
4011da177e4SLinus Torvalds __setup("mtdparts=", mtdpart_setup);
4021da177e4SLinus Torvalds
4031da177e4SLinus Torvalds static struct mtd_part_parser cmdline_parser = {
4041da177e4SLinus Torvalds .parse_fn = parse_cmdline_partitions,
4051da177e4SLinus Torvalds .name = "cmdlinepart",
4061da177e4SLinus Torvalds };
4071da177e4SLinus Torvalds
cmdline_parser_init(void)4081da177e4SLinus Torvalds static int __init cmdline_parser_init(void)
4091da177e4SLinus Torvalds {
410f5f172dcSLubomir Rintel if (mtdparts)
411f5f172dcSLubomir Rintel mtdpart_setup(mtdparts);
4126e14a61dSAxel Lin register_mtd_parser(&cmdline_parser);
4136e14a61dSAxel Lin return 0;
4141da177e4SLinus Torvalds }
4151da177e4SLinus Torvalds
cmdline_parser_exit(void)416422f3890SLubomir Rintel static void __exit cmdline_parser_exit(void)
417422f3890SLubomir Rintel {
418422f3890SLubomir Rintel deregister_mtd_parser(&cmdline_parser);
419422f3890SLubomir Rintel }
420422f3890SLubomir Rintel
4211da177e4SLinus Torvalds module_init(cmdline_parser_init);
422422f3890SLubomir Rintel module_exit(cmdline_parser_exit);
4231da177e4SLinus Torvalds
424f5f172dcSLubomir Rintel MODULE_PARM_DESC(mtdparts, "Partitioning specification");
425f5f172dcSLubomir Rintel module_param(mtdparts, charp, 0);
426f5f172dcSLubomir Rintel
4271da177e4SLinus Torvalds MODULE_LICENSE("GPL");
4281da177e4SLinus Torvalds MODULE_AUTHOR("Marius Groeger <mag@sysgo.de>");
4291da177e4SLinus Torvalds MODULE_DESCRIPTION("Command line configuration of MTD partitions");
430