diff options
Diffstat (limited to 'drivers/mtd/cmdlinepart.c')
| -rw-r--r-- | drivers/mtd/cmdlinepart.c | 270 | 
1 files changed, 145 insertions, 125 deletions
diff --git a/drivers/mtd/cmdlinepart.c b/drivers/mtd/cmdlinepart.c index e790f38893b..3e829b37af8 100644 --- a/drivers/mtd/cmdlinepart.c +++ b/drivers/mtd/cmdlinepart.c @@ -22,11 +22,22 @@   *   * mtdparts=<mtddef>[;<mtddef]   * <mtddef>  := <mtd-id>:<partdef>[,<partdef>] - *              where <mtd-id> is the name from the "cat /proc/mtd" command - * <partdef> := <size>[@offset][<name>][ro][lk] + * <partdef> := <size>[@<offset>][<name>][ro][lk]   * <mtd-id>  := unique name used in mapping driver/device (mtd->name)   * <size>    := standard linux memsize OR "-" to denote all remaining space + *              size is automatically truncated at end of device + *              if specified or trucated size is 0 the part is skipped + * <offset>  := standard linux memsize + *              if omitted the part will immediately follow the previous part + *              or 0 if the first part   * <name>    := '(' NAME ')' + *              NAME will appear in /proc/mtd + * + * <size> and <offset> can be specified such that the parts are out of order + * in physical memory and may even overlap. + * + * The parts are assigned MTD numbers in the order they are specified in the + * command line regardless of their order in physical memory.   *   * Examples:   * @@ -39,10 +50,10 @@  #include <linux/kernel.h>  #include <linux/slab.h> -  #include <linux/mtd/mtd.h>  #include <linux/mtd/partitions.h> -#include <linux/bootmem.h> +#include <linux/module.h> +#include <linux/err.h>  /* error message prefix */  #define ERRP "mtd: " @@ -56,8 +67,8 @@  /* special size referring to all the remaining space in a partition */ -#define SIZE_REMAINING UINT_MAX -#define OFFSET_CONTINUOUS UINT_MAX +#define SIZE_REMAINING ULLONG_MAX +#define OFFSET_CONTINUOUS ULLONG_MAX  struct cmdline_mtd_partition {  	struct cmdline_mtd_partition *next; @@ -69,9 +80,10 @@ struct cmdline_mtd_partition {  /* mtdpart_setup() parses into here */  static struct cmdline_mtd_partition *partitions; -/* the command line passed to mtdpart_setupd() */ +/* the command line passed to mtdpart_setup() */ +static char *mtdparts;  static char *cmdline; -static int cmdline_parsed = 0; +static int cmdline_parsed;  /*   * Parse one partition definition for an MTD. Since there can be many @@ -82,15 +94,14 @@ static int cmdline_parsed = 0;   * syntax has been verified ok.   */  static struct mtd_partition * newpart(char *s, -                                      char **retptr, -                                      int *num_parts, -                                      int this_part, -                                      unsigned char **extra_mem_ptr, -                                      int extra_mem_size) +				      char **retptr, +				      int *num_parts, +				      int this_part, +				      unsigned char **extra_mem_ptr, +				      int extra_mem_size)  {  	struct mtd_partition *parts; -	unsigned long size; -	unsigned long offset = OFFSET_CONTINUOUS; +	unsigned long long size, offset = OFFSET_CONTINUOUS;  	char *name;  	int name_len;  	unsigned char *extra_mem; @@ -98,127 +109,107 @@ static struct mtd_partition * newpart(char *s,  	unsigned int mask_flags;  	/* fetch the partition size */ -	if (*s == '-') -	{	/* assign all remaining space to this partition */ +	if (*s == '-') { +		/* assign all remaining space to this partition */  		size = SIZE_REMAINING;  		s++; -	} -	else -	{ +	} else {  		size = memparse(s, &s); -		if (size < PAGE_SIZE) -		{ -			printk(KERN_ERR ERRP "partition size too small (%lx)\n", size); -			return NULL; +		if (size < PAGE_SIZE) { +			printk(KERN_ERR ERRP "partition size too small (%llx)\n", +			       size); +			return ERR_PTR(-EINVAL);  		}  	}  	/* fetch partition name and flags */  	mask_flags = 0; /* this is going to be a regular partition */  	delim = 0; -        /* check for offset */ -        if (*s == '@') -	{ -                s++; -                offset = memparse(s, &s); -        } -        /* now look for name */ + +	/* check for offset */ +	if (*s == '@') { +		s++; +		offset = memparse(s, &s); +	} + +	/* now look for name */  	if (*s == '(') -	{  		delim = ')'; -	} -	if (delim) -	{ +	if (delim) {  		char *p; -	    	name = ++s; +		name = ++s;  		p = strchr(name, delim); -		if (!p) -		{ +		if (!p) {  			printk(KERN_ERR ERRP "no closing %c found in partition name\n", delim); -			return NULL; +			return ERR_PTR(-EINVAL);  		}  		name_len = p - name;  		s = p + 1; -	} -	else -	{ -	    	name = NULL; +	} else { +		name = NULL;  		name_len = 13; /* Partition_000 */  	}  	/* record name length for memory allocation later */  	extra_mem_size += name_len + 1; -        /* test for options */ -        if (strncmp(s, "ro", 2) == 0) -	{ +	/* test for options */ +	if (strncmp(s, "ro", 2) == 0) {  		mask_flags |= MTD_WRITEABLE;  		s += 2; -        } +	} -        /* if lk is found do NOT unlock the MTD partition*/ -        if (strncmp(s, "lk", 2) == 0) -	{ +	/* if lk is found do NOT unlock the MTD partition*/ +	if (strncmp(s, "lk", 2) == 0) {  		mask_flags |= MTD_POWERUP_LOCK;  		s += 2; -        } +	}  	/* test if more partitions are following */ -	if (*s == ',') -	{ -		if (size == SIZE_REMAINING) -		{ +	if (*s == ',') { +		if (size == SIZE_REMAINING) {  			printk(KERN_ERR ERRP "no partitions allowed after a fill-up partition\n"); -			return NULL; +			return ERR_PTR(-EINVAL);  		}  		/* more partitions follow, parse them */  		parts = newpart(s + 1, &s, num_parts, this_part + 1,  				&extra_mem, extra_mem_size); -		if (!parts) -			return NULL; -	} -	else -	{	/* this is the last partition: allocate space for all */ +		if (IS_ERR(parts)) +			return parts; +	} else { +		/* this is the last partition: allocate space for all */  		int alloc_size;  		*num_parts = this_part + 1;  		alloc_size = *num_parts * sizeof(struct mtd_partition) +  			     extra_mem_size; +  		parts = kzalloc(alloc_size, GFP_KERNEL);  		if (!parts) -		{ -			printk(KERN_ERR ERRP "out of memory\n"); -			return NULL; -		} +			return ERR_PTR(-ENOMEM);  		extra_mem = (unsigned char *)(parts + *num_parts);  	} +  	/* enter this partition (offset will be calculated later if it is zero at this point) */  	parts[this_part].size = size;  	parts[this_part].offset = offset;  	parts[this_part].mask_flags = mask_flags;  	if (name) -	{  		strlcpy(extra_mem, name, name_len + 1); -	}  	else -	{  		sprintf(extra_mem, "Partition_%03d", this_part); -	}  	parts[this_part].name = extra_mem;  	extra_mem += name_len + 1;  	dbg(("partition %d: name <%s>, offset %llx, size %llx, mask flags %x\n", -	     this_part, -	     parts[this_part].name, -	     parts[this_part].offset, -	     parts[this_part].size, -	     parts[this_part].mask_flags)); +	     this_part, parts[this_part].name, parts[this_part].offset, +	     parts[this_part].size, parts[this_part].mask_flags));  	/* return (updated) pointer to extra_mem memory */  	if (extra_mem_ptr) -	  *extra_mem_ptr = extra_mem; +		*extra_mem_ptr = extra_mem;  	/* return (updated) pointer command line string */  	*retptr = s; @@ -238,16 +229,16 @@ static int mtdpart_setup_real(char *s)  	{  		struct cmdline_mtd_partition *this_mtd;  		struct mtd_partition *parts; -	    	int mtd_id_len; -		int num_parts; +		int mtd_id_len, num_parts;  		char *p, *mtd_id; -	    	mtd_id = s; +		mtd_id = s; +  		/* fetch <mtd-id> */ -		if (!(p = strchr(s, ':'))) -		{ +		p = strchr(s, ':'); +		if (!p) {  			printk(KERN_ERR ERRP "no mtd-id\n"); -			return 0; +			return -EINVAL;  		}  		mtd_id_len = p - mtd_id; @@ -264,8 +255,7 @@ static int mtdpart_setup_real(char *s)  				(unsigned char**)&this_mtd, /* out: extra mem */  				mtd_id_len + 1 + sizeof(*this_mtd) +  				sizeof(void*)-1 /*alignment*/); -		if(!parts) -		{ +		if (IS_ERR(parts)) {  			/*  			 * An error occurred. We're either:  			 * a) out of memory, or @@ -273,12 +263,12 @@ static int mtdpart_setup_real(char *s)  			 * Either way, this mtd is hosed and we're  			 * unlikely to succeed in parsing any more  			 */ -			 return 0; +			 return PTR_ERR(parts);  		 }  		/* align this_mtd */  		this_mtd = (struct cmdline_mtd_partition *) -			ALIGN((unsigned long)this_mtd, sizeof(void*)); +				ALIGN((unsigned long)this_mtd, sizeof(void *));  		/* enter results */  		this_mtd->parts = parts;  		this_mtd->num_parts = num_parts; @@ -298,14 +288,14 @@ static int mtdpart_setup_real(char *s)  			break;  		/* does another spec follow? */ -		if (*s != ';') -		{ +		if (*s != ';') {  			printk(KERN_ERR ERRP "bad character after partition (%c)\n", *s); -			return 0; +			return -EINVAL;  		}  		s++;  	} -	return 1; + +	return 0;  }  /* @@ -316,49 +306,67 @@ static int mtdpart_setup_real(char *s)   * the first one in the chain if a NULL mtd_id is passed in.   */  static int parse_cmdline_partitions(struct mtd_info *master, -                             struct mtd_partition **pparts, -                             unsigned long origin) +				    struct mtd_partition **pparts, +				    struct mtd_part_parser_data *data)  { -	unsigned long offset; -	int i; +	unsigned long long offset; +	int i, err;  	struct cmdline_mtd_partition *part;  	const char *mtd_id = master->name;  	/* parse command line */ -	if (!cmdline_parsed) -		mtdpart_setup_real(cmdline); +	if (!cmdline_parsed) { +		err = mtdpart_setup_real(cmdline); +		if (err) +			return err; +	} -	for(part = partitions; part; part = part->next) -	{ +	/* +	 * Search for the partition definition matching master->name. +	 * If master->name is not set, stop at first partition definition. +	 */ +	for (part = partitions; part; part = part->next) {  		if ((!mtd_id) || (!strcmp(part->mtd_id, mtd_id))) -		{ -			for(i = 0, offset = 0; i < part->num_parts; i++) -			{ -				if (part->parts[i].offset == OFFSET_CONTINUOUS) -				  part->parts[i].offset = offset; -				else -				  offset = part->parts[i].offset; -				if (part->parts[i].size == SIZE_REMAINING) -				  part->parts[i].size = master->size - offset; -				if (offset + part->parts[i].size > master->size) -				{ -					printk(KERN_WARNING ERRP -					       "%s: partitioning exceeds flash size, truncating\n", -					       part->mtd_id); -					part->parts[i].size = master->size - offset; -					part->num_parts = i; -				} -				offset += part->parts[i].size; -			} -			*pparts = kmemdup(part->parts, -					sizeof(*part->parts) * part->num_parts, -					GFP_KERNEL); -			if (!*pparts) -				return -ENOMEM; -			return part->num_parts; +			break; +	} + +	if (!part) +		return 0; + +	for (i = 0, offset = 0; i < part->num_parts; i++) { +		if (part->parts[i].offset == OFFSET_CONTINUOUS) +			part->parts[i].offset = offset; +		else +			offset = part->parts[i].offset; + +		if (part->parts[i].size == SIZE_REMAINING) +			part->parts[i].size = master->size - offset; + +		if (offset + part->parts[i].size > master->size) { +			printk(KERN_WARNING ERRP +			       "%s: partitioning exceeds flash size, truncating\n", +			       part->mtd_id); +			part->parts[i].size = master->size - offset; +		} +		offset += part->parts[i].size; + +		if (part->parts[i].size == 0) { +			printk(KERN_WARNING ERRP +			       "%s: skipping zero sized partition\n", +			       part->mtd_id); +			part->num_parts--; +			memmove(&part->parts[i], &part->parts[i + 1], +				sizeof(*part->parts) * (part->num_parts - i)); +			i--;  		}  	} -	return 0; + +	*pparts = kmemdup(part->parts, sizeof(*part->parts) * part->num_parts, +			  GFP_KERNEL); +	if (!*pparts) +		return -ENOMEM; + +	return part->num_parts;  } @@ -369,7 +377,7 @@ static int parse_cmdline_partitions(struct mtd_info *master,   *   * This function needs to be visible for bootloaders.   */ -static int mtdpart_setup(char *s) +static int __init mtdpart_setup(char *s)  {  	cmdline = s;  	return 1; @@ -385,10 +393,22 @@ static struct mtd_part_parser cmdline_parser = {  static int __init cmdline_parser_init(void)  { -	return register_mtd_parser(&cmdline_parser); +	if (mtdparts) +		mtdpart_setup(mtdparts); +	register_mtd_parser(&cmdline_parser); +	return 0; +} + +static void __exit cmdline_parser_exit(void) +{ +	deregister_mtd_parser(&cmdline_parser);  }  module_init(cmdline_parser_init); +module_exit(cmdline_parser_exit); + +MODULE_PARM_DESC(mtdparts, "Partitioning specification"); +module_param(mtdparts, charp, 0);  MODULE_LICENSE("GPL");  MODULE_AUTHOR("Marius Groeger <mag@sysgo.de>");  | 
