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