2019-05-23 12:14:39 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2005-04-17 02:20:36 +04:00
/*
* Read flash partition table from command line
*
2010-08-08 23:58:20 +04:00
* Copyright © 2002 SYSGO Real - Time Solutions GmbH
* Copyright © 2002 - 2010 David Woodhouse < dwmw2 @ infradead . org >
*
2005-04-17 02:20:36 +04:00
* The format for the command line is as follows :
2005-11-07 14:15:26 +03:00
*
2005-04-17 02:20:36 +04:00
* mtdparts = < mtddef > [ ; < mtddef ]
* < mtddef > : = < mtd - id > : < partdef > [ , < partdef > ]
2012-12-19 01:11:51 +04:00
* < partdef > : = < size > [ @ < offset > ] [ < name > ] [ ro ] [ lk ]
2005-04-17 02:20:36 +04:00
* < mtd - id > : = unique name used in mapping driver / device ( mtd - > name )
* < size > : = standard linux memsize OR " - " to denote all remaining space
2012-12-19 01:11:51 +04:00
* size is automatically truncated at end of device
2014-06-29 14:21:37 +04:00
* if specified or truncated size is 0 the part is skipped
2012-12-19 01:11:51 +04:00
* < offset > : = standard linux memsize
* if omitted the part will immediately follow the previous part
* or 0 if the first part
2005-04-17 02:20:36 +04:00
* < name > : = ' ( ' NAME ' ) '
2012-12-19 01:11:52 +04:00
* NAME will appear in / proc / mtd
2005-11-07 14:15:26 +03:00
*
2012-12-19 01:11:51 +04:00
* < 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 .
*
2005-04-17 02:20:36 +04:00
* Examples :
2005-11-07 14:15:26 +03:00
*
2005-04-17 02:20:36 +04:00
* 1 NOR Flash , with 1 single writable partition :
* edb7312 - nor : -
2005-11-07 14:15:26 +03:00
*
2005-04-17 02:20:36 +04:00
* 1 NOR Flash with 2 partitions , 1 NAND with one
* edb7312 - nor : 256 k ( ARMboot ) ro , - ( root ) ; edb7312 - nand : - ( home )
*/
2015-06-19 23:10:07 +03:00
# define pr_fmt(fmt) "mtd: " fmt
2005-04-17 02:20:36 +04:00
# include <linux/kernel.h>
# include <linux/slab.h>
# include <linux/mtd/mtd.h>
# include <linux/mtd/partitions.h>
2011-07-03 23:17:31 +04:00
# include <linux/module.h>
2012-09-02 14:54:14 +04:00
# include <linux/err.h>
2005-04-17 02:20:36 +04:00
/* debug macro */
#if 0
# define dbg(x) do { printk("DEBUG-CMDLINE-PART: "); printk x; } while(0)
# else
# define dbg(x)
# endif
/* special size referring to all the remaining space in a partition */
2012-11-01 09:58:17 +04:00
# define SIZE_REMAINING ULLONG_MAX
# define OFFSET_CONTINUOUS ULLONG_MAX
2005-04-17 02:20:36 +04:00
struct cmdline_mtd_partition {
struct cmdline_mtd_partition * next ;
char * mtd_id ;
int num_parts ;
struct mtd_partition * parts ;
} ;
/* mtdpart_setup() parses into here */
static struct cmdline_mtd_partition * partitions ;
2012-04-25 17:14:17 +04:00
/* the command line passed to mtdpart_setup() */
2013-01-16 05:12:49 +04:00
static char * mtdparts ;
2005-04-17 02:20:36 +04:00
static char * cmdline ;
2012-08-31 17:10:02 +04:00
static int cmdline_parsed ;
2005-04-17 02:20:36 +04:00
/*
* Parse one partition definition for an MTD . Since there can be many
2005-11-07 14:15:26 +03:00
* comma separated partition definitions , this function calls itself
2005-04-17 02:20:36 +04:00
* recursively until no more partition definitions are found . Nice side
* effect : the memory to keep the mtd_partition structs and the names
* is allocated upon the last definition being found . At that point the
* syntax has been verified ok .
*/
2005-11-07 14:15:26 +03:00
static struct mtd_partition * newpart ( char * s ,
2012-09-03 10:33:32 +04:00
char * * retptr ,
int * num_parts ,
int this_part ,
unsigned char * * extra_mem_ptr ,
int extra_mem_size )
2005-04-17 02:20:36 +04:00
{
struct mtd_partition * parts ;
2012-11-01 09:58:17 +04:00
unsigned long long size , offset = OFFSET_CONTINUOUS ;
2005-04-17 02:20:36 +04:00
char * name ;
int name_len ;
unsigned char * extra_mem ;
char delim ;
unsigned int mask_flags ;
/* fetch the partition size */
2012-09-03 10:33:32 +04:00
if ( * s = = ' - ' ) {
/* assign all remaining space to this partition */
2005-04-17 02:20:36 +04:00
size = SIZE_REMAINING ;
s + + ;
2012-09-03 10:33:32 +04:00
} else {
2005-04-17 02:20:36 +04:00
size = memparse ( s , & s ) ;
2015-06-19 23:10:06 +03:00
if ( ! size ) {
2015-06-19 23:10:07 +03:00
pr_err ( " partition has size 0 \n " ) ;
2012-09-02 14:54:14 +04:00
return ERR_PTR ( - EINVAL ) ;
2005-04-17 02:20:36 +04:00
}
}
/* fetch partition name and flags */
mask_flags = 0 ; /* this is going to be a regular partition */
delim = 0 ;
2012-09-03 10:33:32 +04:00
/* check for offset */
if ( * s = = ' @ ' ) {
s + + ;
offset = memparse ( s , & s ) ;
}
/* now look for name */
2005-04-17 02:20:36 +04:00
if ( * s = = ' ( ' )
delim = ' ) ' ;
2005-11-07 14:15:26 +03:00
2012-09-03 10:33:32 +04:00
if ( delim ) {
2005-04-17 02:20:36 +04:00
char * p ;
2012-09-03 10:33:32 +04:00
name = + + s ;
2008-04-14 18:20:04 +04:00
p = strchr ( name , delim ) ;
2012-09-03 10:33:32 +04:00
if ( ! p ) {
2015-06-19 23:10:07 +03:00
pr_err ( " no closing %c found in partition name \n " , delim ) ;
2012-09-02 14:54:14 +04:00
return ERR_PTR ( - EINVAL ) ;
2005-04-17 02:20:36 +04:00
}
name_len = p - name ;
s = p + 1 ;
2012-09-03 10:33:32 +04:00
} else {
name = NULL ;
2005-04-17 02:20:36 +04:00
name_len = 13 ; /* Partition_000 */
}
2005-11-07 14:15:26 +03:00
2005-04-17 02:20:36 +04:00
/* record name length for memory allocation later */
extra_mem_size + = name_len + 1 ;
2012-09-03 10:33:32 +04:00
/* test for options */
if ( strncmp ( s , " ro " , 2 ) = = 0 ) {
2005-04-17 02:20:36 +04:00
mask_flags | = MTD_WRITEABLE ;
s + = 2 ;
2012-09-03 10:33:32 +04:00
}
2005-04-17 02:20:36 +04:00
2012-09-03 10:33:32 +04:00
/* if lk is found do NOT unlock the MTD partition*/
if ( strncmp ( s , " lk " , 2 ) = = 0 ) {
2008-01-30 21:25:49 +03:00
mask_flags | = MTD_POWERUP_LOCK ;
s + = 2 ;
2012-09-03 10:33:32 +04:00
}
2008-01-30 21:25:49 +03:00
2005-04-17 02:20:36 +04:00
/* test if more partitions are following */
2012-09-03 10:33:32 +04:00
if ( * s = = ' , ' ) {
if ( size = = SIZE_REMAINING ) {
2015-06-19 23:10:07 +03:00
pr_err ( " no partitions allowed after a fill-up partition \n " ) ;
2012-09-02 14:54:14 +04:00
return ERR_PTR ( - EINVAL ) ;
2005-04-17 02:20:36 +04:00
}
/* more partitions follow, parse them */
2008-04-14 18:20:04 +04:00
parts = newpart ( s + 1 , & s , num_parts , this_part + 1 ,
& extra_mem , extra_mem_size ) ;
2012-09-02 14:54:14 +04:00
if ( IS_ERR ( parts ) )
return parts ;
2012-09-03 10:33:32 +04:00
} else {
/* this is the last partition: allocate space for all */
2005-04-17 02:20:36 +04:00
int alloc_size ;
* num_parts = this_part + 1 ;
alloc_size = * num_parts * sizeof ( struct mtd_partition ) +
extra_mem_size ;
2012-09-03 10:33:32 +04:00
2006-11-15 22:10:29 +03:00
parts = kzalloc ( alloc_size , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! parts )
2012-09-02 14:54:14 +04:00
return ERR_PTR ( - ENOMEM ) ;
2005-04-17 02:20:36 +04:00
extra_mem = ( unsigned char * ) ( parts + * num_parts ) ;
}
2012-09-03 10:33:32 +04:00
2018-05-22 18:07:53 +03:00
/*
* enter this partition ( offset will be calculated later if it is
* OFFSET_CONTINUOUS at this point )
*/
2005-04-17 02:20:36 +04:00
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 ;
2009-06-25 01:39:09 +04:00
dbg ( ( " partition %d: name <%s>, offset %llx, size %llx, mask flags %x \n " ,
2012-09-03 10:33:32 +04:00
this_part , parts [ this_part ] . name , parts [ this_part ] . offset ,
parts [ this_part ] . size , parts [ this_part ] . mask_flags ) ) ;
2005-04-17 02:20:36 +04:00
/* return (updated) pointer to extra_mem memory */
if ( extra_mem_ptr )
2012-09-03 10:33:32 +04:00
* extra_mem_ptr = extra_mem ;
2005-04-17 02:20:36 +04:00
/* return (updated) pointer command line string */
* retptr = s ;
/* return partition table */
return parts ;
}
2005-11-07 14:15:26 +03:00
/*
* Parse the command line .
2005-04-17 02:20:36 +04:00
*/
static int mtdpart_setup_real ( char * s )
{
cmdline_parsed = 1 ;
for ( ; s ! = NULL ; )
{
struct cmdline_mtd_partition * this_mtd ;
struct mtd_partition * parts ;
2012-09-03 10:33:32 +04:00
int mtd_id_len , num_parts ;
2005-04-17 02:20:36 +04:00
char * p , * mtd_id ;
2012-09-03 10:33:32 +04:00
mtd_id = s ;
2005-04-17 02:20:36 +04:00
/* fetch <mtd-id> */
2012-09-03 10:33:32 +04:00
p = strchr ( s , ' : ' ) ;
if ( ! p ) {
2015-06-19 23:10:07 +03:00
pr_err ( " no mtd-id \n " ) ;
2012-09-02 14:54:14 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
}
mtd_id_len = p - mtd_id ;
dbg ( ( " parsing <%s> \n " , p + 1 ) ) ;
2005-11-07 14:15:26 +03:00
/*
2005-04-17 02:20:36 +04:00
* parse one mtd . have it reserve memory for the
* struct cmdline_mtd_partition and the mtd - id string .
*/
parts = newpart ( p + 1 , /* cmdline */
& s , /* out: updated cmdline ptr */
& num_parts , /* out: number of parts */
0 , /* first partition */
( unsigned char * * ) & this_mtd , /* out: extra mem */
2005-11-07 14:15:26 +03:00
mtd_id_len + 1 + sizeof ( * this_mtd ) +
2005-06-07 19:04:29 +04:00
sizeof ( void * ) - 1 /*alignment*/ ) ;
2012-09-03 10:33:32 +04:00
if ( IS_ERR ( parts ) ) {
2005-04-17 02:20:36 +04:00
/*
* An error occurred . We ' re either :
* a ) out of memory , or
* b ) in the middle of the partition spec
* Either way , this mtd is hosed and we ' re
* unlikely to succeed in parsing any more
*/
2012-09-02 14:54:14 +04:00
return PTR_ERR ( parts ) ;
2005-04-17 02:20:36 +04:00
}
2005-06-07 19:04:29 +04:00
/* align this_mtd */
2005-11-07 14:15:26 +03:00
this_mtd = ( struct cmdline_mtd_partition * )
2012-09-03 10:33:32 +04:00
ALIGN ( ( unsigned long ) this_mtd , sizeof ( void * ) ) ;
2005-11-07 14:15:26 +03:00
/* enter results */
2005-04-17 02:20:36 +04:00
this_mtd - > parts = parts ;
this_mtd - > num_parts = num_parts ;
this_mtd - > mtd_id = ( char * ) ( this_mtd + 1 ) ;
strlcpy ( this_mtd - > mtd_id , mtd_id , mtd_id_len + 1 ) ;
/* link into chain */
2005-11-07 14:15:26 +03:00
this_mtd - > next = partitions ;
2005-04-17 02:20:36 +04:00
partitions = this_mtd ;
2005-11-07 14:15:26 +03:00
dbg ( ( " mtdid=<%s> num_parts=<%d> \n " ,
2005-04-17 02:20:36 +04:00
this_mtd - > mtd_id , this_mtd - > num_parts ) ) ;
2005-11-07 14:15:26 +03:00
2005-04-17 02:20:36 +04:00
/* EOS - we're done */
if ( * s = = 0 )
break ;
/* does another spec follow? */
2012-09-03 10:33:32 +04:00
if ( * s ! = ' ; ' ) {
2015-06-19 23:10:07 +03:00
pr_err ( " bad character after partition (%c) \n " , * s ) ;
2012-09-02 14:54:14 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
}
s + + ;
}
2012-09-03 10:33:32 +04:00
2012-09-02 14:54:14 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
/*
* Main function to be called from the MTD mapping driver / device to
* obtain the partitioning information . At this point the command line
* arguments will actually be parsed and turned to struct mtd_partition
* information . It returns partitions for the requested mtd device , or
* the first one in the chain if a NULL mtd_id is passed in .
*/
2005-11-07 14:15:26 +03:00
static int parse_cmdline_partitions ( struct mtd_info * master ,
2015-12-05 02:25:14 +03:00
const struct mtd_partition * * pparts ,
2011-06-10 18:18:28 +04:00
struct mtd_part_parser_data * data )
2005-04-17 02:20:36 +04:00
{
2012-11-01 09:58:17 +04:00
unsigned long long offset ;
2012-09-02 14:54:14 +04:00
int i , err ;
2005-04-17 02:20:36 +04:00
struct cmdline_mtd_partition * part ;
2008-07-08 20:09:03 +04:00
const char * mtd_id = master - > name ;
2005-04-17 02:20:36 +04:00
/* parse command line */
2012-09-02 14:54:14 +04:00
if ( ! cmdline_parsed ) {
err = mtdpart_setup_real ( cmdline ) ;
if ( err )
return err ;
}
2005-04-17 02:20:36 +04:00
2012-09-23 11:52:29 +04:00
/*
* Search for the partition definition matching master - > name .
* If master - > name is not set , stop at first partition definition .
*/
2012-09-03 10:33:32 +04:00
for ( part = partitions ; part ; part = part - > next ) {
2012-09-23 11:52:29 +04:00
if ( ( ! mtd_id ) | | ( ! strcmp ( part - > mtd_id , mtd_id ) ) )
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 ;
2012-12-17 04:59:30 +04:00
if ( offset + part - > parts [ i ] . size > master - > size ) {
2015-06-19 23:10:07 +03:00
pr_warn ( " %s: partitioning exceeds flash size, truncating \n " ,
part - > mtd_id ) ;
2012-12-17 04:59:30 +04:00
part - > parts [ i ] . size = master - > size - offset ;
}
offset + = part - > parts [ i ] . size ;
2012-09-23 11:52:29 +04:00
if ( part - > parts [ i ] . size = = 0 ) {
2015-06-19 23:10:07 +03:00
pr_warn ( " %s: skipping zero sized partition \n " ,
part - > mtd_id ) ;
2012-09-23 11:52:29 +04:00
part - > num_parts - - ;
memmove ( & part - > parts [ i ] , & part - > parts [ i + 1 ] ,
sizeof ( * part - > parts ) * ( part - > num_parts - i ) ) ;
2012-12-17 04:59:29 +04:00
i - - ;
2005-04-17 02:20:36 +04:00
}
}
2012-09-03 10:33:32 +04:00
2012-09-23 11:52:29 +04:00
* pparts = kmemdup ( part - > parts , sizeof ( * part - > parts ) * part - > num_parts ,
GFP_KERNEL ) ;
if ( ! * pparts )
return - ENOMEM ;
return part - > num_parts ;
2005-04-17 02:20:36 +04:00
}
2005-11-07 14:15:26 +03:00
/*
* This is the handler for our kernel parameter , called from
2005-04-17 02:20:36 +04:00
* main . c : : checksetup ( ) . Note that we can not yet kmalloc ( ) anything ,
* so we only save the commandline for later processing .
*
* This function needs to be visible for bootloaders .
*/
2013-01-16 05:12:49 +04:00
static int __init mtdpart_setup ( char * s )
2005-04-17 02:20:36 +04:00
{
cmdline = s ;
return 1 ;
}
__setup ( " mtdparts= " , mtdpart_setup ) ;
static struct mtd_part_parser cmdline_parser = {
. parse_fn = parse_cmdline_partitions ,
. name = " cmdlinepart " ,
} ;
static int __init cmdline_parser_init ( void )
{
2013-01-16 05:12:49 +04:00
if ( mtdparts )
mtdpart_setup ( mtdparts ) ;
2013-12-01 15:01:06 +04:00
register_mtd_parser ( & cmdline_parser ) ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
2013-01-16 05:12:50 +04:00
static void __exit cmdline_parser_exit ( void )
{
deregister_mtd_parser ( & cmdline_parser ) ;
}
2005-04-17 02:20:36 +04:00
module_init ( cmdline_parser_init ) ;
2013-01-16 05:12:50 +04:00
module_exit ( cmdline_parser_exit ) ;
2005-04-17 02:20:36 +04:00
2013-01-16 05:12:49 +04:00
MODULE_PARM_DESC ( mtdparts , " Partitioning specification " ) ;
module_param ( mtdparts , charp , 0 ) ;
2005-04-17 02:20:36 +04:00
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Marius Groeger <mag@sysgo.de> " ) ;
MODULE_DESCRIPTION ( " Command line configuration of MTD partitions " ) ;