162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Read flash partition table from command line
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright © 2002      SYSGO Real-Time Solutions GmbH
662306a36Sopenharmony_ci * Copyright © 2002-2010 David Woodhouse <dwmw2@infradead.org>
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * The format for the command line is as follows:
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci * mtdparts=<mtddef>[;<mtddef]
1162306a36Sopenharmony_ci * <mtddef>  := <mtd-id>:<partdef>[,<partdef>]
1262306a36Sopenharmony_ci * <partdef> := <size>[@<offset>][<name>][ro][lk][slc]
1362306a36Sopenharmony_ci * <mtd-id>  := unique name used in mapping driver/device (mtd->name)
1462306a36Sopenharmony_ci * <size>    := standard linux memsize OR "-" to denote all remaining space
1562306a36Sopenharmony_ci *              size is automatically truncated at end of device
1662306a36Sopenharmony_ci *              if specified or truncated size is 0 the part is skipped
1762306a36Sopenharmony_ci * <offset>  := standard linux memsize
1862306a36Sopenharmony_ci *              if omitted the part will immediately follow the previous part
1962306a36Sopenharmony_ci *              or 0 if the first part
2062306a36Sopenharmony_ci * <name>    := '(' NAME ')'
2162306a36Sopenharmony_ci *              NAME will appear in /proc/mtd
2262306a36Sopenharmony_ci *
2362306a36Sopenharmony_ci * <size> and <offset> can be specified such that the parts are out of order
2462306a36Sopenharmony_ci * in physical memory and may even overlap.
2562306a36Sopenharmony_ci *
2662306a36Sopenharmony_ci * The parts are assigned MTD numbers in the order they are specified in the
2762306a36Sopenharmony_ci * command line regardless of their order in physical memory.
2862306a36Sopenharmony_ci *
2962306a36Sopenharmony_ci * Examples:
3062306a36Sopenharmony_ci *
3162306a36Sopenharmony_ci * 1 NOR Flash, with 1 single writable partition:
3262306a36Sopenharmony_ci * edb7312-nor:-
3362306a36Sopenharmony_ci *
3462306a36Sopenharmony_ci * 1 NOR Flash with 2 partitions, 1 NAND with one
3562306a36Sopenharmony_ci * edb7312-nor:256k(ARMboot)ro,-(root);edb7312-nand:-(home)
3662306a36Sopenharmony_ci */
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#define pr_fmt(fmt)	"mtd: " fmt
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci#include <linux/kernel.h>
4162306a36Sopenharmony_ci#include <linux/slab.h>
4262306a36Sopenharmony_ci#include <linux/mtd/mtd.h>
4362306a36Sopenharmony_ci#include <linux/mtd/partitions.h>
4462306a36Sopenharmony_ci#include <linux/module.h>
4562306a36Sopenharmony_ci#include <linux/err.h>
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci/* debug macro */
4862306a36Sopenharmony_ci#if 0
4962306a36Sopenharmony_ci#define dbg(x) do { printk("DEBUG-CMDLINE-PART: "); printk x; } while(0)
5062306a36Sopenharmony_ci#else
5162306a36Sopenharmony_ci#define dbg(x)
5262306a36Sopenharmony_ci#endif
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci/* special size referring to all the remaining space in a partition */
5662306a36Sopenharmony_ci#define SIZE_REMAINING ULLONG_MAX
5762306a36Sopenharmony_ci#define OFFSET_CONTINUOUS ULLONG_MAX
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistruct cmdline_mtd_partition {
6062306a36Sopenharmony_ci	struct cmdline_mtd_partition *next;
6162306a36Sopenharmony_ci	char *mtd_id;
6262306a36Sopenharmony_ci	int num_parts;
6362306a36Sopenharmony_ci	struct mtd_partition *parts;
6462306a36Sopenharmony_ci};
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci/* mtdpart_setup() parses into here */
6762306a36Sopenharmony_cistatic struct cmdline_mtd_partition *partitions;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci/* the command line passed to mtdpart_setup() */
7062306a36Sopenharmony_cistatic char *mtdparts;
7162306a36Sopenharmony_cistatic char *cmdline;
7262306a36Sopenharmony_cistatic int cmdline_parsed;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci/*
7562306a36Sopenharmony_ci * Parse one partition definition for an MTD. Since there can be many
7662306a36Sopenharmony_ci * comma separated partition definitions, this function calls itself
7762306a36Sopenharmony_ci * recursively until no more partition definitions are found. Nice side
7862306a36Sopenharmony_ci * effect: the memory to keep the mtd_partition structs and the names
7962306a36Sopenharmony_ci * is allocated upon the last definition being found. At that point the
8062306a36Sopenharmony_ci * syntax has been verified ok.
8162306a36Sopenharmony_ci */
8262306a36Sopenharmony_cistatic struct mtd_partition * newpart(char *s,
8362306a36Sopenharmony_ci				      char **retptr,
8462306a36Sopenharmony_ci				      int *num_parts,
8562306a36Sopenharmony_ci				      int this_part,
8662306a36Sopenharmony_ci				      unsigned char **extra_mem_ptr,
8762306a36Sopenharmony_ci				      int extra_mem_size)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	struct mtd_partition *parts;
9062306a36Sopenharmony_ci	unsigned long long size, offset = OFFSET_CONTINUOUS;
9162306a36Sopenharmony_ci	char *name;
9262306a36Sopenharmony_ci	int name_len;
9362306a36Sopenharmony_ci	unsigned char *extra_mem;
9462306a36Sopenharmony_ci	char delim;
9562306a36Sopenharmony_ci	unsigned int mask_flags, add_flags;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	/* fetch the partition size */
9862306a36Sopenharmony_ci	if (*s == '-') {
9962306a36Sopenharmony_ci		/* assign all remaining space to this partition */
10062306a36Sopenharmony_ci		size = SIZE_REMAINING;
10162306a36Sopenharmony_ci		s++;
10262306a36Sopenharmony_ci	} else {
10362306a36Sopenharmony_ci		size = memparse(s, &s);
10462306a36Sopenharmony_ci		if (!size) {
10562306a36Sopenharmony_ci			pr_err("partition has size 0\n");
10662306a36Sopenharmony_ci			return ERR_PTR(-EINVAL);
10762306a36Sopenharmony_ci		}
10862306a36Sopenharmony_ci	}
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	/* fetch partition name and flags */
11162306a36Sopenharmony_ci	mask_flags = 0; /* this is going to be a regular partition */
11262306a36Sopenharmony_ci	add_flags = 0;
11362306a36Sopenharmony_ci	delim = 0;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	/* check for offset */
11662306a36Sopenharmony_ci	if (*s == '@') {
11762306a36Sopenharmony_ci		s++;
11862306a36Sopenharmony_ci		offset = memparse(s, &s);
11962306a36Sopenharmony_ci	}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	/* now look for name */
12262306a36Sopenharmony_ci	if (*s == '(')
12362306a36Sopenharmony_ci		delim = ')';
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	if (delim) {
12662306a36Sopenharmony_ci		char *p;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci		name = ++s;
12962306a36Sopenharmony_ci		p = strchr(name, delim);
13062306a36Sopenharmony_ci		if (!p) {
13162306a36Sopenharmony_ci			pr_err("no closing %c found in partition name\n", delim);
13262306a36Sopenharmony_ci			return ERR_PTR(-EINVAL);
13362306a36Sopenharmony_ci		}
13462306a36Sopenharmony_ci		name_len = p - name;
13562306a36Sopenharmony_ci		s = p + 1;
13662306a36Sopenharmony_ci	} else {
13762306a36Sopenharmony_ci		name = NULL;
13862306a36Sopenharmony_ci		name_len = 13; /* Partition_000 */
13962306a36Sopenharmony_ci	}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	/* record name length for memory allocation later */
14262306a36Sopenharmony_ci	extra_mem_size += name_len + 1;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	/* test for options */
14562306a36Sopenharmony_ci	if (strncmp(s, "ro", 2) == 0) {
14662306a36Sopenharmony_ci		mask_flags |= MTD_WRITEABLE;
14762306a36Sopenharmony_ci		s += 2;
14862306a36Sopenharmony_ci	}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	/* if lk is found do NOT unlock the MTD partition*/
15162306a36Sopenharmony_ci	if (strncmp(s, "lk", 2) == 0) {
15262306a36Sopenharmony_ci		mask_flags |= MTD_POWERUP_LOCK;
15362306a36Sopenharmony_ci		s += 2;
15462306a36Sopenharmony_ci	}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	/* if slc is found use emulated SLC mode on this partition*/
15762306a36Sopenharmony_ci	if (!strncmp(s, "slc", 3)) {
15862306a36Sopenharmony_ci		add_flags |= MTD_SLC_ON_MLC_EMULATION;
15962306a36Sopenharmony_ci		s += 3;
16062306a36Sopenharmony_ci	}
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	/* test if more partitions are following */
16362306a36Sopenharmony_ci	if (*s == ',') {
16462306a36Sopenharmony_ci		if (size == SIZE_REMAINING) {
16562306a36Sopenharmony_ci			pr_err("no partitions allowed after a fill-up partition\n");
16662306a36Sopenharmony_ci			return ERR_PTR(-EINVAL);
16762306a36Sopenharmony_ci		}
16862306a36Sopenharmony_ci		/* more partitions follow, parse them */
16962306a36Sopenharmony_ci		parts = newpart(s + 1, &s, num_parts, this_part + 1,
17062306a36Sopenharmony_ci				&extra_mem, extra_mem_size);
17162306a36Sopenharmony_ci		if (IS_ERR(parts))
17262306a36Sopenharmony_ci			return parts;
17362306a36Sopenharmony_ci	} else {
17462306a36Sopenharmony_ci		/* this is the last partition: allocate space for all */
17562306a36Sopenharmony_ci		int alloc_size;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci		*num_parts = this_part + 1;
17862306a36Sopenharmony_ci		alloc_size = *num_parts * sizeof(struct mtd_partition) +
17962306a36Sopenharmony_ci			     extra_mem_size;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci		parts = kzalloc(alloc_size, GFP_KERNEL);
18262306a36Sopenharmony_ci		if (!parts)
18362306a36Sopenharmony_ci			return ERR_PTR(-ENOMEM);
18462306a36Sopenharmony_ci		extra_mem = (unsigned char *)(parts + *num_parts);
18562306a36Sopenharmony_ci	}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	/*
18862306a36Sopenharmony_ci	 * enter this partition (offset will be calculated later if it is
18962306a36Sopenharmony_ci	 * OFFSET_CONTINUOUS at this point)
19062306a36Sopenharmony_ci	 */
19162306a36Sopenharmony_ci	parts[this_part].size = size;
19262306a36Sopenharmony_ci	parts[this_part].offset = offset;
19362306a36Sopenharmony_ci	parts[this_part].mask_flags = mask_flags;
19462306a36Sopenharmony_ci	parts[this_part].add_flags = add_flags;
19562306a36Sopenharmony_ci	if (name)
19662306a36Sopenharmony_ci		strscpy(extra_mem, name, name_len + 1);
19762306a36Sopenharmony_ci	else
19862306a36Sopenharmony_ci		sprintf(extra_mem, "Partition_%03d", this_part);
19962306a36Sopenharmony_ci	parts[this_part].name = extra_mem;
20062306a36Sopenharmony_ci	extra_mem += name_len + 1;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	dbg(("partition %d: name <%s>, offset %llx, size %llx, mask flags %x\n",
20362306a36Sopenharmony_ci	     this_part, parts[this_part].name, parts[this_part].offset,
20462306a36Sopenharmony_ci	     parts[this_part].size, parts[this_part].mask_flags));
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	/* return (updated) pointer to extra_mem memory */
20762306a36Sopenharmony_ci	if (extra_mem_ptr)
20862306a36Sopenharmony_ci		*extra_mem_ptr = extra_mem;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	/* return (updated) pointer command line string */
21162306a36Sopenharmony_ci	*retptr = s;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	/* return partition table */
21462306a36Sopenharmony_ci	return parts;
21562306a36Sopenharmony_ci}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci/*
21862306a36Sopenharmony_ci * Parse the command line.
21962306a36Sopenharmony_ci */
22062306a36Sopenharmony_cistatic int mtdpart_setup_real(char *s)
22162306a36Sopenharmony_ci{
22262306a36Sopenharmony_ci	cmdline_parsed = 1;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	for( ; s != NULL; )
22562306a36Sopenharmony_ci	{
22662306a36Sopenharmony_ci		struct cmdline_mtd_partition *this_mtd;
22762306a36Sopenharmony_ci		struct mtd_partition *parts;
22862306a36Sopenharmony_ci		int mtd_id_len, num_parts;
22962306a36Sopenharmony_ci		char *p, *mtd_id, *semicol, *open_parenth;
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci		/*
23262306a36Sopenharmony_ci		 * Replace the first ';' by a NULL char so strrchr can work
23362306a36Sopenharmony_ci		 * properly.
23462306a36Sopenharmony_ci		 */
23562306a36Sopenharmony_ci		semicol = strchr(s, ';');
23662306a36Sopenharmony_ci		if (semicol)
23762306a36Sopenharmony_ci			*semicol = '\0';
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci		/*
24062306a36Sopenharmony_ci		 * make sure that part-names with ":" will not be handled as
24162306a36Sopenharmony_ci		 * part of the mtd-id with an ":"
24262306a36Sopenharmony_ci		 */
24362306a36Sopenharmony_ci		open_parenth = strchr(s, '(');
24462306a36Sopenharmony_ci		if (open_parenth)
24562306a36Sopenharmony_ci			*open_parenth = '\0';
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci		mtd_id = s;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci		/*
25062306a36Sopenharmony_ci		 * fetch <mtd-id>. We use strrchr to ignore all ':' that could
25162306a36Sopenharmony_ci		 * be present in the MTD name, only the last one is interpreted
25262306a36Sopenharmony_ci		 * as an <mtd-id>/<part-definition> separator.
25362306a36Sopenharmony_ci		 */
25462306a36Sopenharmony_ci		p = strrchr(s, ':');
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci		/* Restore the '(' now. */
25762306a36Sopenharmony_ci		if (open_parenth)
25862306a36Sopenharmony_ci			*open_parenth = '(';
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci		/* Restore the ';' now. */
26162306a36Sopenharmony_ci		if (semicol)
26262306a36Sopenharmony_ci			*semicol = ';';
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci		if (!p) {
26562306a36Sopenharmony_ci			pr_err("no mtd-id\n");
26662306a36Sopenharmony_ci			return -EINVAL;
26762306a36Sopenharmony_ci		}
26862306a36Sopenharmony_ci		mtd_id_len = p - mtd_id;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci		dbg(("parsing <%s>\n", p+1));
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci		/*
27362306a36Sopenharmony_ci		 * parse one mtd. have it reserve memory for the
27462306a36Sopenharmony_ci		 * struct cmdline_mtd_partition and the mtd-id string.
27562306a36Sopenharmony_ci		 */
27662306a36Sopenharmony_ci		parts = newpart(p + 1,		/* cmdline */
27762306a36Sopenharmony_ci				&s,		/* out: updated cmdline ptr */
27862306a36Sopenharmony_ci				&num_parts,	/* out: number of parts */
27962306a36Sopenharmony_ci				0,		/* first partition */
28062306a36Sopenharmony_ci				(unsigned char**)&this_mtd, /* out: extra mem */
28162306a36Sopenharmony_ci				mtd_id_len + 1 + sizeof(*this_mtd) +
28262306a36Sopenharmony_ci				sizeof(void*)-1 /*alignment*/);
28362306a36Sopenharmony_ci		if (IS_ERR(parts)) {
28462306a36Sopenharmony_ci			/*
28562306a36Sopenharmony_ci			 * An error occurred. We're either:
28662306a36Sopenharmony_ci			 * a) out of memory, or
28762306a36Sopenharmony_ci			 * b) in the middle of the partition spec
28862306a36Sopenharmony_ci			 * Either way, this mtd is hosed and we're
28962306a36Sopenharmony_ci			 * unlikely to succeed in parsing any more
29062306a36Sopenharmony_ci			 */
29162306a36Sopenharmony_ci			 return PTR_ERR(parts);
29262306a36Sopenharmony_ci		 }
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci		/* align this_mtd */
29562306a36Sopenharmony_ci		this_mtd = (struct cmdline_mtd_partition *)
29662306a36Sopenharmony_ci				ALIGN((unsigned long)this_mtd, sizeof(void *));
29762306a36Sopenharmony_ci		/* enter results */
29862306a36Sopenharmony_ci		this_mtd->parts = parts;
29962306a36Sopenharmony_ci		this_mtd->num_parts = num_parts;
30062306a36Sopenharmony_ci		this_mtd->mtd_id = (char*)(this_mtd + 1);
30162306a36Sopenharmony_ci		strscpy(this_mtd->mtd_id, mtd_id, mtd_id_len + 1);
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci		/* link into chain */
30462306a36Sopenharmony_ci		this_mtd->next = partitions;
30562306a36Sopenharmony_ci		partitions = this_mtd;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci		dbg(("mtdid=<%s> num_parts=<%d>\n",
30862306a36Sopenharmony_ci		     this_mtd->mtd_id, this_mtd->num_parts));
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci		/* EOS - we're done */
31262306a36Sopenharmony_ci		if (*s == 0)
31362306a36Sopenharmony_ci			break;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci		/* does another spec follow? */
31662306a36Sopenharmony_ci		if (*s != ';') {
31762306a36Sopenharmony_ci			pr_err("bad character after partition (%c)\n", *s);
31862306a36Sopenharmony_ci			return -EINVAL;
31962306a36Sopenharmony_ci		}
32062306a36Sopenharmony_ci		s++;
32162306a36Sopenharmony_ci	}
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	return 0;
32462306a36Sopenharmony_ci}
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci/*
32762306a36Sopenharmony_ci * Main function to be called from the MTD mapping driver/device to
32862306a36Sopenharmony_ci * obtain the partitioning information. At this point the command line
32962306a36Sopenharmony_ci * arguments will actually be parsed and turned to struct mtd_partition
33062306a36Sopenharmony_ci * information. It returns partitions for the requested mtd device, or
33162306a36Sopenharmony_ci * the first one in the chain if a NULL mtd_id is passed in.
33262306a36Sopenharmony_ci */
33362306a36Sopenharmony_cistatic int parse_cmdline_partitions(struct mtd_info *master,
33462306a36Sopenharmony_ci				    const struct mtd_partition **pparts,
33562306a36Sopenharmony_ci				    struct mtd_part_parser_data *data)
33662306a36Sopenharmony_ci{
33762306a36Sopenharmony_ci	unsigned long long offset;
33862306a36Sopenharmony_ci	int i, err;
33962306a36Sopenharmony_ci	struct cmdline_mtd_partition *part;
34062306a36Sopenharmony_ci	const char *mtd_id = master->name;
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	/* parse command line */
34362306a36Sopenharmony_ci	if (!cmdline_parsed) {
34462306a36Sopenharmony_ci		err = mtdpart_setup_real(cmdline);
34562306a36Sopenharmony_ci		if (err)
34662306a36Sopenharmony_ci			return err;
34762306a36Sopenharmony_ci	}
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	/*
35062306a36Sopenharmony_ci	 * Search for the partition definition matching master->name.
35162306a36Sopenharmony_ci	 * If master->name is not set, stop at first partition definition.
35262306a36Sopenharmony_ci	 */
35362306a36Sopenharmony_ci	for (part = partitions; part; part = part->next) {
35462306a36Sopenharmony_ci		if ((!mtd_id) || (!strcmp(part->mtd_id, mtd_id)))
35562306a36Sopenharmony_ci			break;
35662306a36Sopenharmony_ci	}
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	if (!part)
35962306a36Sopenharmony_ci		return 0;
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	for (i = 0, offset = 0; i < part->num_parts; i++) {
36262306a36Sopenharmony_ci		if (part->parts[i].offset == OFFSET_CONTINUOUS)
36362306a36Sopenharmony_ci			part->parts[i].offset = offset;
36462306a36Sopenharmony_ci		else
36562306a36Sopenharmony_ci			offset = part->parts[i].offset;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci		if (part->parts[i].size == SIZE_REMAINING)
36862306a36Sopenharmony_ci			part->parts[i].size = master->size - offset;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci		if (offset + part->parts[i].size > master->size) {
37162306a36Sopenharmony_ci			pr_warn("%s: partitioning exceeds flash size, truncating\n",
37262306a36Sopenharmony_ci				part->mtd_id);
37362306a36Sopenharmony_ci			part->parts[i].size = master->size - offset;
37462306a36Sopenharmony_ci		}
37562306a36Sopenharmony_ci		offset += part->parts[i].size;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci		if (part->parts[i].size == 0) {
37862306a36Sopenharmony_ci			pr_warn("%s: skipping zero sized partition\n",
37962306a36Sopenharmony_ci				part->mtd_id);
38062306a36Sopenharmony_ci			part->num_parts--;
38162306a36Sopenharmony_ci			memmove(&part->parts[i], &part->parts[i + 1],
38262306a36Sopenharmony_ci				sizeof(*part->parts) * (part->num_parts - i));
38362306a36Sopenharmony_ci			i--;
38462306a36Sopenharmony_ci		}
38562306a36Sopenharmony_ci	}
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	*pparts = kmemdup(part->parts, sizeof(*part->parts) * part->num_parts,
38862306a36Sopenharmony_ci			  GFP_KERNEL);
38962306a36Sopenharmony_ci	if (!*pparts)
39062306a36Sopenharmony_ci		return -ENOMEM;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	return part->num_parts;
39362306a36Sopenharmony_ci}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci/*
39762306a36Sopenharmony_ci * This is the handler for our kernel parameter, called from
39862306a36Sopenharmony_ci * main.c::checksetup(). Note that we can not yet kmalloc() anything,
39962306a36Sopenharmony_ci * so we only save the commandline for later processing.
40062306a36Sopenharmony_ci *
40162306a36Sopenharmony_ci * This function needs to be visible for bootloaders.
40262306a36Sopenharmony_ci */
40362306a36Sopenharmony_cistatic int __init mtdpart_setup(char *s)
40462306a36Sopenharmony_ci{
40562306a36Sopenharmony_ci	cmdline = s;
40662306a36Sopenharmony_ci	return 1;
40762306a36Sopenharmony_ci}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci__setup("mtdparts=", mtdpart_setup);
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_cistatic struct mtd_part_parser cmdline_parser = {
41262306a36Sopenharmony_ci	.parse_fn = parse_cmdline_partitions,
41362306a36Sopenharmony_ci	.name = "cmdlinepart",
41462306a36Sopenharmony_ci};
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_cistatic int __init cmdline_parser_init(void)
41762306a36Sopenharmony_ci{
41862306a36Sopenharmony_ci	if (mtdparts)
41962306a36Sopenharmony_ci		mtdpart_setup(mtdparts);
42062306a36Sopenharmony_ci	register_mtd_parser(&cmdline_parser);
42162306a36Sopenharmony_ci	return 0;
42262306a36Sopenharmony_ci}
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_cistatic void __exit cmdline_parser_exit(void)
42562306a36Sopenharmony_ci{
42662306a36Sopenharmony_ci	deregister_mtd_parser(&cmdline_parser);
42762306a36Sopenharmony_ci}
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_cimodule_init(cmdline_parser_init);
43062306a36Sopenharmony_cimodule_exit(cmdline_parser_exit);
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ciMODULE_PARM_DESC(mtdparts, "Partitioning specification");
43362306a36Sopenharmony_cimodule_param(mtdparts, charp, 0);
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ciMODULE_LICENSE("GPL");
43662306a36Sopenharmony_ciMODULE_AUTHOR("Marius Groeger <mag@sysgo.de>");
43762306a36Sopenharmony_ciMODULE_DESCRIPTION("Command line configuration of MTD partitions");
438