18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Read flash partition table from command line
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright © 2002      SYSGO Real-Time Solutions GmbH
68c2ecf20Sopenharmony_ci * Copyright © 2002-2010 David Woodhouse <dwmw2@infradead.org>
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * The format for the command line is as follows:
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * mtdparts=<mtddef>[;<mtddef]
118c2ecf20Sopenharmony_ci * <mtddef>  := <mtd-id>:<partdef>[,<partdef>]
128c2ecf20Sopenharmony_ci * <partdef> := <size>[@<offset>][<name>][ro][lk][slc]
138c2ecf20Sopenharmony_ci * <mtd-id>  := unique name used in mapping driver/device (mtd->name)
148c2ecf20Sopenharmony_ci * <size>    := standard linux memsize OR "-" to denote all remaining space
158c2ecf20Sopenharmony_ci *              size is automatically truncated at end of device
168c2ecf20Sopenharmony_ci *              if specified or truncated size is 0 the part is skipped
178c2ecf20Sopenharmony_ci * <offset>  := standard linux memsize
188c2ecf20Sopenharmony_ci *              if omitted the part will immediately follow the previous part
198c2ecf20Sopenharmony_ci *              or 0 if the first part
208c2ecf20Sopenharmony_ci * <name>    := '(' NAME ')'
218c2ecf20Sopenharmony_ci *              NAME will appear in /proc/mtd
228c2ecf20Sopenharmony_ci *
238c2ecf20Sopenharmony_ci * <size> and <offset> can be specified such that the parts are out of order
248c2ecf20Sopenharmony_ci * in physical memory and may even overlap.
258c2ecf20Sopenharmony_ci *
268c2ecf20Sopenharmony_ci * The parts are assigned MTD numbers in the order they are specified in the
278c2ecf20Sopenharmony_ci * command line regardless of their order in physical memory.
288c2ecf20Sopenharmony_ci *
298c2ecf20Sopenharmony_ci * Examples:
308c2ecf20Sopenharmony_ci *
318c2ecf20Sopenharmony_ci * 1 NOR Flash, with 1 single writable partition:
328c2ecf20Sopenharmony_ci * edb7312-nor:-
338c2ecf20Sopenharmony_ci *
348c2ecf20Sopenharmony_ci * 1 NOR Flash with 2 partitions, 1 NAND with one
358c2ecf20Sopenharmony_ci * edb7312-nor:256k(ARMboot)ro,-(root);edb7312-nand:-(home)
368c2ecf20Sopenharmony_ci */
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci#define pr_fmt(fmt)	"mtd: " fmt
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci#include <linux/kernel.h>
418c2ecf20Sopenharmony_ci#include <linux/slab.h>
428c2ecf20Sopenharmony_ci#include <linux/mtd/mtd.h>
438c2ecf20Sopenharmony_ci#include <linux/mtd/partitions.h>
448c2ecf20Sopenharmony_ci#include <linux/module.h>
458c2ecf20Sopenharmony_ci#include <linux/err.h>
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci/* debug macro */
488c2ecf20Sopenharmony_ci#if 0
498c2ecf20Sopenharmony_ci#define dbg(x) do { printk("DEBUG-CMDLINE-PART: "); printk x; } while(0)
508c2ecf20Sopenharmony_ci#else
518c2ecf20Sopenharmony_ci#define dbg(x)
528c2ecf20Sopenharmony_ci#endif
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci/* special size referring to all the remaining space in a partition */
568c2ecf20Sopenharmony_ci#define SIZE_REMAINING ULLONG_MAX
578c2ecf20Sopenharmony_ci#define OFFSET_CONTINUOUS ULLONG_MAX
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistruct cmdline_mtd_partition {
608c2ecf20Sopenharmony_ci	struct cmdline_mtd_partition *next;
618c2ecf20Sopenharmony_ci	char *mtd_id;
628c2ecf20Sopenharmony_ci	int num_parts;
638c2ecf20Sopenharmony_ci	struct mtd_partition *parts;
648c2ecf20Sopenharmony_ci};
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci/* mtdpart_setup() parses into here */
678c2ecf20Sopenharmony_cistatic struct cmdline_mtd_partition *partitions;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci/* the command line passed to mtdpart_setup() */
708c2ecf20Sopenharmony_cistatic char *mtdparts;
718c2ecf20Sopenharmony_cistatic char *cmdline;
728c2ecf20Sopenharmony_cistatic int cmdline_parsed;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci/*
758c2ecf20Sopenharmony_ci * Parse one partition definition for an MTD. Since there can be many
768c2ecf20Sopenharmony_ci * comma separated partition definitions, this function calls itself
778c2ecf20Sopenharmony_ci * recursively until no more partition definitions are found. Nice side
788c2ecf20Sopenharmony_ci * effect: the memory to keep the mtd_partition structs and the names
798c2ecf20Sopenharmony_ci * is allocated upon the last definition being found. At that point the
808c2ecf20Sopenharmony_ci * syntax has been verified ok.
818c2ecf20Sopenharmony_ci */
828c2ecf20Sopenharmony_cistatic struct mtd_partition * newpart(char *s,
838c2ecf20Sopenharmony_ci				      char **retptr,
848c2ecf20Sopenharmony_ci				      int *num_parts,
858c2ecf20Sopenharmony_ci				      int this_part,
868c2ecf20Sopenharmony_ci				      unsigned char **extra_mem_ptr,
878c2ecf20Sopenharmony_ci				      int extra_mem_size)
888c2ecf20Sopenharmony_ci{
898c2ecf20Sopenharmony_ci	struct mtd_partition *parts;
908c2ecf20Sopenharmony_ci	unsigned long long size, offset = OFFSET_CONTINUOUS;
918c2ecf20Sopenharmony_ci	char *name;
928c2ecf20Sopenharmony_ci	int name_len;
938c2ecf20Sopenharmony_ci	unsigned char *extra_mem;
948c2ecf20Sopenharmony_ci	char delim;
958c2ecf20Sopenharmony_ci	unsigned int mask_flags, add_flags;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	/* fetch the partition size */
988c2ecf20Sopenharmony_ci	if (*s == '-') {
998c2ecf20Sopenharmony_ci		/* assign all remaining space to this partition */
1008c2ecf20Sopenharmony_ci		size = SIZE_REMAINING;
1018c2ecf20Sopenharmony_ci		s++;
1028c2ecf20Sopenharmony_ci	} else {
1038c2ecf20Sopenharmony_ci		size = memparse(s, &s);
1048c2ecf20Sopenharmony_ci		if (!size) {
1058c2ecf20Sopenharmony_ci			pr_err("partition has size 0\n");
1068c2ecf20Sopenharmony_ci			return ERR_PTR(-EINVAL);
1078c2ecf20Sopenharmony_ci		}
1088c2ecf20Sopenharmony_ci	}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	/* fetch partition name and flags */
1118c2ecf20Sopenharmony_ci	mask_flags = 0; /* this is going to be a regular partition */
1128c2ecf20Sopenharmony_ci	add_flags = 0;
1138c2ecf20Sopenharmony_ci	delim = 0;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	/* check for offset */
1168c2ecf20Sopenharmony_ci	if (*s == '@') {
1178c2ecf20Sopenharmony_ci		s++;
1188c2ecf20Sopenharmony_ci		offset = memparse(s, &s);
1198c2ecf20Sopenharmony_ci	}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	/* now look for name */
1228c2ecf20Sopenharmony_ci	if (*s == '(')
1238c2ecf20Sopenharmony_ci		delim = ')';
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	if (delim) {
1268c2ecf20Sopenharmony_ci		char *p;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci		name = ++s;
1298c2ecf20Sopenharmony_ci		p = strchr(name, delim);
1308c2ecf20Sopenharmony_ci		if (!p) {
1318c2ecf20Sopenharmony_ci			pr_err("no closing %c found in partition name\n", delim);
1328c2ecf20Sopenharmony_ci			return ERR_PTR(-EINVAL);
1338c2ecf20Sopenharmony_ci		}
1348c2ecf20Sopenharmony_ci		name_len = p - name;
1358c2ecf20Sopenharmony_ci		s = p + 1;
1368c2ecf20Sopenharmony_ci	} else {
1378c2ecf20Sopenharmony_ci		name = NULL;
1388c2ecf20Sopenharmony_ci		name_len = 13; /* Partition_000 */
1398c2ecf20Sopenharmony_ci	}
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	/* record name length for memory allocation later */
1428c2ecf20Sopenharmony_ci	extra_mem_size += name_len + 1;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	/* test for options */
1458c2ecf20Sopenharmony_ci	if (strncmp(s, "ro", 2) == 0) {
1468c2ecf20Sopenharmony_ci		mask_flags |= MTD_WRITEABLE;
1478c2ecf20Sopenharmony_ci		s += 2;
1488c2ecf20Sopenharmony_ci	}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	/* if lk is found do NOT unlock the MTD partition*/
1518c2ecf20Sopenharmony_ci	if (strncmp(s, "lk", 2) == 0) {
1528c2ecf20Sopenharmony_ci		mask_flags |= MTD_POWERUP_LOCK;
1538c2ecf20Sopenharmony_ci		s += 2;
1548c2ecf20Sopenharmony_ci	}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	/* if slc is found use emulated SLC mode on this partition*/
1578c2ecf20Sopenharmony_ci	if (!strncmp(s, "slc", 3)) {
1588c2ecf20Sopenharmony_ci		add_flags |= MTD_SLC_ON_MLC_EMULATION;
1598c2ecf20Sopenharmony_ci		s += 3;
1608c2ecf20Sopenharmony_ci	}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	/* test if more partitions are following */
1638c2ecf20Sopenharmony_ci	if (*s == ',') {
1648c2ecf20Sopenharmony_ci		if (size == SIZE_REMAINING) {
1658c2ecf20Sopenharmony_ci			pr_err("no partitions allowed after a fill-up partition\n");
1668c2ecf20Sopenharmony_ci			return ERR_PTR(-EINVAL);
1678c2ecf20Sopenharmony_ci		}
1688c2ecf20Sopenharmony_ci		/* more partitions follow, parse them */
1698c2ecf20Sopenharmony_ci		parts = newpart(s + 1, &s, num_parts, this_part + 1,
1708c2ecf20Sopenharmony_ci				&extra_mem, extra_mem_size);
1718c2ecf20Sopenharmony_ci		if (IS_ERR(parts))
1728c2ecf20Sopenharmony_ci			return parts;
1738c2ecf20Sopenharmony_ci	} else {
1748c2ecf20Sopenharmony_ci		/* this is the last partition: allocate space for all */
1758c2ecf20Sopenharmony_ci		int alloc_size;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci		*num_parts = this_part + 1;
1788c2ecf20Sopenharmony_ci		alloc_size = *num_parts * sizeof(struct mtd_partition) +
1798c2ecf20Sopenharmony_ci			     extra_mem_size;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci		parts = kzalloc(alloc_size, GFP_KERNEL);
1828c2ecf20Sopenharmony_ci		if (!parts)
1838c2ecf20Sopenharmony_ci			return ERR_PTR(-ENOMEM);
1848c2ecf20Sopenharmony_ci		extra_mem = (unsigned char *)(parts + *num_parts);
1858c2ecf20Sopenharmony_ci	}
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	/*
1888c2ecf20Sopenharmony_ci	 * enter this partition (offset will be calculated later if it is
1898c2ecf20Sopenharmony_ci	 * OFFSET_CONTINUOUS at this point)
1908c2ecf20Sopenharmony_ci	 */
1918c2ecf20Sopenharmony_ci	parts[this_part].size = size;
1928c2ecf20Sopenharmony_ci	parts[this_part].offset = offset;
1938c2ecf20Sopenharmony_ci	parts[this_part].mask_flags = mask_flags;
1948c2ecf20Sopenharmony_ci	parts[this_part].add_flags = add_flags;
1958c2ecf20Sopenharmony_ci	if (name)
1968c2ecf20Sopenharmony_ci		strlcpy(extra_mem, name, name_len + 1);
1978c2ecf20Sopenharmony_ci	else
1988c2ecf20Sopenharmony_ci		sprintf(extra_mem, "Partition_%03d", this_part);
1998c2ecf20Sopenharmony_ci	parts[this_part].name = extra_mem;
2008c2ecf20Sopenharmony_ci	extra_mem += name_len + 1;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	dbg(("partition %d: name <%s>, offset %llx, size %llx, mask flags %x\n",
2038c2ecf20Sopenharmony_ci	     this_part, parts[this_part].name, parts[this_part].offset,
2048c2ecf20Sopenharmony_ci	     parts[this_part].size, parts[this_part].mask_flags));
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	/* return (updated) pointer to extra_mem memory */
2078c2ecf20Sopenharmony_ci	if (extra_mem_ptr)
2088c2ecf20Sopenharmony_ci		*extra_mem_ptr = extra_mem;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	/* return (updated) pointer command line string */
2118c2ecf20Sopenharmony_ci	*retptr = s;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	/* return partition table */
2148c2ecf20Sopenharmony_ci	return parts;
2158c2ecf20Sopenharmony_ci}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci/*
2188c2ecf20Sopenharmony_ci * Parse the command line.
2198c2ecf20Sopenharmony_ci */
2208c2ecf20Sopenharmony_cistatic int mtdpart_setup_real(char *s)
2218c2ecf20Sopenharmony_ci{
2228c2ecf20Sopenharmony_ci	cmdline_parsed = 1;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	for( ; s != NULL; )
2258c2ecf20Sopenharmony_ci	{
2268c2ecf20Sopenharmony_ci		struct cmdline_mtd_partition *this_mtd;
2278c2ecf20Sopenharmony_ci		struct mtd_partition *parts;
2288c2ecf20Sopenharmony_ci		int mtd_id_len, num_parts;
2298c2ecf20Sopenharmony_ci		char *p, *mtd_id, *semicol, *open_parenth;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci		/*
2328c2ecf20Sopenharmony_ci		 * Replace the first ';' by a NULL char so strrchr can work
2338c2ecf20Sopenharmony_ci		 * properly.
2348c2ecf20Sopenharmony_ci		 */
2358c2ecf20Sopenharmony_ci		semicol = strchr(s, ';');
2368c2ecf20Sopenharmony_ci		if (semicol)
2378c2ecf20Sopenharmony_ci			*semicol = '\0';
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci		/*
2408c2ecf20Sopenharmony_ci		 * make sure that part-names with ":" will not be handled as
2418c2ecf20Sopenharmony_ci		 * part of the mtd-id with an ":"
2428c2ecf20Sopenharmony_ci		 */
2438c2ecf20Sopenharmony_ci		open_parenth = strchr(s, '(');
2448c2ecf20Sopenharmony_ci		if (open_parenth)
2458c2ecf20Sopenharmony_ci			*open_parenth = '\0';
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci		mtd_id = s;
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci		/*
2508c2ecf20Sopenharmony_ci		 * fetch <mtd-id>. We use strrchr to ignore all ':' that could
2518c2ecf20Sopenharmony_ci		 * be present in the MTD name, only the last one is interpreted
2528c2ecf20Sopenharmony_ci		 * as an <mtd-id>/<part-definition> separator.
2538c2ecf20Sopenharmony_ci		 */
2548c2ecf20Sopenharmony_ci		p = strrchr(s, ':');
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci		/* Restore the '(' now. */
2578c2ecf20Sopenharmony_ci		if (open_parenth)
2588c2ecf20Sopenharmony_ci			*open_parenth = '(';
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci		/* Restore the ';' now. */
2618c2ecf20Sopenharmony_ci		if (semicol)
2628c2ecf20Sopenharmony_ci			*semicol = ';';
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci		if (!p) {
2658c2ecf20Sopenharmony_ci			pr_err("no mtd-id\n");
2668c2ecf20Sopenharmony_ci			return -EINVAL;
2678c2ecf20Sopenharmony_ci		}
2688c2ecf20Sopenharmony_ci		mtd_id_len = p - mtd_id;
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci		dbg(("parsing <%s>\n", p+1));
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci		/*
2738c2ecf20Sopenharmony_ci		 * parse one mtd. have it reserve memory for the
2748c2ecf20Sopenharmony_ci		 * struct cmdline_mtd_partition and the mtd-id string.
2758c2ecf20Sopenharmony_ci		 */
2768c2ecf20Sopenharmony_ci		parts = newpart(p + 1,		/* cmdline */
2778c2ecf20Sopenharmony_ci				&s,		/* out: updated cmdline ptr */
2788c2ecf20Sopenharmony_ci				&num_parts,	/* out: number of parts */
2798c2ecf20Sopenharmony_ci				0,		/* first partition */
2808c2ecf20Sopenharmony_ci				(unsigned char**)&this_mtd, /* out: extra mem */
2818c2ecf20Sopenharmony_ci				mtd_id_len + 1 + sizeof(*this_mtd) +
2828c2ecf20Sopenharmony_ci				sizeof(void*)-1 /*alignment*/);
2838c2ecf20Sopenharmony_ci		if (IS_ERR(parts)) {
2848c2ecf20Sopenharmony_ci			/*
2858c2ecf20Sopenharmony_ci			 * An error occurred. We're either:
2868c2ecf20Sopenharmony_ci			 * a) out of memory, or
2878c2ecf20Sopenharmony_ci			 * b) in the middle of the partition spec
2888c2ecf20Sopenharmony_ci			 * Either way, this mtd is hosed and we're
2898c2ecf20Sopenharmony_ci			 * unlikely to succeed in parsing any more
2908c2ecf20Sopenharmony_ci			 */
2918c2ecf20Sopenharmony_ci			 return PTR_ERR(parts);
2928c2ecf20Sopenharmony_ci		 }
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci		/* align this_mtd */
2958c2ecf20Sopenharmony_ci		this_mtd = (struct cmdline_mtd_partition *)
2968c2ecf20Sopenharmony_ci				ALIGN((unsigned long)this_mtd, sizeof(void *));
2978c2ecf20Sopenharmony_ci		/* enter results */
2988c2ecf20Sopenharmony_ci		this_mtd->parts = parts;
2998c2ecf20Sopenharmony_ci		this_mtd->num_parts = num_parts;
3008c2ecf20Sopenharmony_ci		this_mtd->mtd_id = (char*)(this_mtd + 1);
3018c2ecf20Sopenharmony_ci		strlcpy(this_mtd->mtd_id, mtd_id, mtd_id_len + 1);
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci		/* link into chain */
3048c2ecf20Sopenharmony_ci		this_mtd->next = partitions;
3058c2ecf20Sopenharmony_ci		partitions = this_mtd;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci		dbg(("mtdid=<%s> num_parts=<%d>\n",
3088c2ecf20Sopenharmony_ci		     this_mtd->mtd_id, this_mtd->num_parts));
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci		/* EOS - we're done */
3128c2ecf20Sopenharmony_ci		if (*s == 0)
3138c2ecf20Sopenharmony_ci			break;
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci		/* does another spec follow? */
3168c2ecf20Sopenharmony_ci		if (*s != ';') {
3178c2ecf20Sopenharmony_ci			pr_err("bad character after partition (%c)\n", *s);
3188c2ecf20Sopenharmony_ci			return -EINVAL;
3198c2ecf20Sopenharmony_ci		}
3208c2ecf20Sopenharmony_ci		s++;
3218c2ecf20Sopenharmony_ci	}
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	return 0;
3248c2ecf20Sopenharmony_ci}
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci/*
3278c2ecf20Sopenharmony_ci * Main function to be called from the MTD mapping driver/device to
3288c2ecf20Sopenharmony_ci * obtain the partitioning information. At this point the command line
3298c2ecf20Sopenharmony_ci * arguments will actually be parsed and turned to struct mtd_partition
3308c2ecf20Sopenharmony_ci * information. It returns partitions for the requested mtd device, or
3318c2ecf20Sopenharmony_ci * the first one in the chain if a NULL mtd_id is passed in.
3328c2ecf20Sopenharmony_ci */
3338c2ecf20Sopenharmony_cistatic int parse_cmdline_partitions(struct mtd_info *master,
3348c2ecf20Sopenharmony_ci				    const struct mtd_partition **pparts,
3358c2ecf20Sopenharmony_ci				    struct mtd_part_parser_data *data)
3368c2ecf20Sopenharmony_ci{
3378c2ecf20Sopenharmony_ci	unsigned long long offset;
3388c2ecf20Sopenharmony_ci	int i, err;
3398c2ecf20Sopenharmony_ci	struct cmdline_mtd_partition *part;
3408c2ecf20Sopenharmony_ci	const char *mtd_id = master->name;
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	/* parse command line */
3438c2ecf20Sopenharmony_ci	if (!cmdline_parsed) {
3448c2ecf20Sopenharmony_ci		err = mtdpart_setup_real(cmdline);
3458c2ecf20Sopenharmony_ci		if (err)
3468c2ecf20Sopenharmony_ci			return err;
3478c2ecf20Sopenharmony_ci	}
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	/*
3508c2ecf20Sopenharmony_ci	 * Search for the partition definition matching master->name.
3518c2ecf20Sopenharmony_ci	 * If master->name is not set, stop at first partition definition.
3528c2ecf20Sopenharmony_ci	 */
3538c2ecf20Sopenharmony_ci	for (part = partitions; part; part = part->next) {
3548c2ecf20Sopenharmony_ci		if ((!mtd_id) || (!strcmp(part->mtd_id, mtd_id)))
3558c2ecf20Sopenharmony_ci			break;
3568c2ecf20Sopenharmony_ci	}
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	if (!part)
3598c2ecf20Sopenharmony_ci		return 0;
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	for (i = 0, offset = 0; i < part->num_parts; i++) {
3628c2ecf20Sopenharmony_ci		if (part->parts[i].offset == OFFSET_CONTINUOUS)
3638c2ecf20Sopenharmony_ci			part->parts[i].offset = offset;
3648c2ecf20Sopenharmony_ci		else
3658c2ecf20Sopenharmony_ci			offset = part->parts[i].offset;
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci		if (part->parts[i].size == SIZE_REMAINING)
3688c2ecf20Sopenharmony_ci			part->parts[i].size = master->size - offset;
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci		if (offset + part->parts[i].size > master->size) {
3718c2ecf20Sopenharmony_ci			pr_warn("%s: partitioning exceeds flash size, truncating\n",
3728c2ecf20Sopenharmony_ci				part->mtd_id);
3738c2ecf20Sopenharmony_ci			part->parts[i].size = master->size - offset;
3748c2ecf20Sopenharmony_ci		}
3758c2ecf20Sopenharmony_ci		offset += part->parts[i].size;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci		if (part->parts[i].size == 0) {
3788c2ecf20Sopenharmony_ci			pr_warn("%s: skipping zero sized partition\n",
3798c2ecf20Sopenharmony_ci				part->mtd_id);
3808c2ecf20Sopenharmony_ci			part->num_parts--;
3818c2ecf20Sopenharmony_ci			memmove(&part->parts[i], &part->parts[i + 1],
3828c2ecf20Sopenharmony_ci				sizeof(*part->parts) * (part->num_parts - i));
3838c2ecf20Sopenharmony_ci			i--;
3848c2ecf20Sopenharmony_ci		}
3858c2ecf20Sopenharmony_ci	}
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	*pparts = kmemdup(part->parts, sizeof(*part->parts) * part->num_parts,
3888c2ecf20Sopenharmony_ci			  GFP_KERNEL);
3898c2ecf20Sopenharmony_ci	if (!*pparts)
3908c2ecf20Sopenharmony_ci		return -ENOMEM;
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	return part->num_parts;
3938c2ecf20Sopenharmony_ci}
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci/*
3978c2ecf20Sopenharmony_ci * This is the handler for our kernel parameter, called from
3988c2ecf20Sopenharmony_ci * main.c::checksetup(). Note that we can not yet kmalloc() anything,
3998c2ecf20Sopenharmony_ci * so we only save the commandline for later processing.
4008c2ecf20Sopenharmony_ci *
4018c2ecf20Sopenharmony_ci * This function needs to be visible for bootloaders.
4028c2ecf20Sopenharmony_ci */
4038c2ecf20Sopenharmony_cistatic int __init mtdpart_setup(char *s)
4048c2ecf20Sopenharmony_ci{
4058c2ecf20Sopenharmony_ci	cmdline = s;
4068c2ecf20Sopenharmony_ci	return 1;
4078c2ecf20Sopenharmony_ci}
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci__setup("mtdparts=", mtdpart_setup);
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_cistatic struct mtd_part_parser cmdline_parser = {
4128c2ecf20Sopenharmony_ci	.parse_fn = parse_cmdline_partitions,
4138c2ecf20Sopenharmony_ci	.name = "cmdlinepart",
4148c2ecf20Sopenharmony_ci};
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_cistatic int __init cmdline_parser_init(void)
4178c2ecf20Sopenharmony_ci{
4188c2ecf20Sopenharmony_ci	if (mtdparts)
4198c2ecf20Sopenharmony_ci		mtdpart_setup(mtdparts);
4208c2ecf20Sopenharmony_ci	register_mtd_parser(&cmdline_parser);
4218c2ecf20Sopenharmony_ci	return 0;
4228c2ecf20Sopenharmony_ci}
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_cistatic void __exit cmdline_parser_exit(void)
4258c2ecf20Sopenharmony_ci{
4268c2ecf20Sopenharmony_ci	deregister_mtd_parser(&cmdline_parser);
4278c2ecf20Sopenharmony_ci}
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_cimodule_init(cmdline_parser_init);
4308c2ecf20Sopenharmony_cimodule_exit(cmdline_parser_exit);
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ciMODULE_PARM_DESC(mtdparts, "Partitioning specification");
4338c2ecf20Sopenharmony_cimodule_param(mtdparts, charp, 0);
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
4368c2ecf20Sopenharmony_ciMODULE_AUTHOR("Marius Groeger <mag@sysgo.de>");
4378c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Command line configuration of MTD partitions");
438