cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

cmdlinepart.c (11181B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * Read flash partition table from command line
      4 *
      5 * Copyright © 2002      SYSGO Real-Time Solutions GmbH
      6 * Copyright © 2002-2010 David Woodhouse <dwmw2@infradead.org>
      7 *
      8 * The format for the command line is as follows:
      9 *
     10 * mtdparts=<mtddef>[;<mtddef]
     11 * <mtddef>  := <mtd-id>:<partdef>[,<partdef>]
     12 * <partdef> := <size>[@<offset>][<name>][ro][lk][slc]
     13 * <mtd-id>  := unique name used in mapping driver/device (mtd->name)
     14 * <size>    := standard linux memsize OR "-" to denote all remaining space
     15 *              size is automatically truncated at end of device
     16 *              if specified or truncated size is 0 the part is skipped
     17 * <offset>  := standard linux memsize
     18 *              if omitted the part will immediately follow the previous part
     19 *              or 0 if the first part
     20 * <name>    := '(' NAME ')'
     21 *              NAME will appear in /proc/mtd
     22 *
     23 * <size> and <offset> can be specified such that the parts are out of order
     24 * in physical memory and may even overlap.
     25 *
     26 * The parts are assigned MTD numbers in the order they are specified in the
     27 * command line regardless of their order in physical memory.
     28 *
     29 * Examples:
     30 *
     31 * 1 NOR Flash, with 1 single writable partition:
     32 * edb7312-nor:-
     33 *
     34 * 1 NOR Flash with 2 partitions, 1 NAND with one
     35 * edb7312-nor:256k(ARMboot)ro,-(root);edb7312-nand:-(home)
     36 */
     37
     38#define pr_fmt(fmt)	"mtd: " fmt
     39
     40#include <linux/kernel.h>
     41#include <linux/slab.h>
     42#include <linux/mtd/mtd.h>
     43#include <linux/mtd/partitions.h>
     44#include <linux/module.h>
     45#include <linux/err.h>
     46
     47/* debug macro */
     48#if 0
     49#define dbg(x) do { printk("DEBUG-CMDLINE-PART: "); printk x; } while(0)
     50#else
     51#define dbg(x)
     52#endif
     53
     54
     55/* special size referring to all the remaining space in a partition */
     56#define SIZE_REMAINING ULLONG_MAX
     57#define OFFSET_CONTINUOUS ULLONG_MAX
     58
     59struct cmdline_mtd_partition {
     60	struct cmdline_mtd_partition *next;
     61	char *mtd_id;
     62	int num_parts;
     63	struct mtd_partition *parts;
     64};
     65
     66/* mtdpart_setup() parses into here */
     67static struct cmdline_mtd_partition *partitions;
     68
     69/* the command line passed to mtdpart_setup() */
     70static char *mtdparts;
     71static char *cmdline;
     72static int cmdline_parsed;
     73
     74/*
     75 * Parse one partition definition for an MTD. Since there can be many
     76 * comma separated partition definitions, this function calls itself
     77 * recursively until no more partition definitions are found. Nice side
     78 * effect: the memory to keep the mtd_partition structs and the names
     79 * is allocated upon the last definition being found. At that point the
     80 * syntax has been verified ok.
     81 */
     82static struct mtd_partition * newpart(char *s,
     83				      char **retptr,
     84				      int *num_parts,
     85				      int this_part,
     86				      unsigned char **extra_mem_ptr,
     87				      int extra_mem_size)
     88{
     89	struct mtd_partition *parts;
     90	unsigned long long size, offset = OFFSET_CONTINUOUS;
     91	char *name;
     92	int name_len;
     93	unsigned char *extra_mem;
     94	char delim;
     95	unsigned int mask_flags, add_flags;
     96
     97	/* fetch the partition size */
     98	if (*s == '-') {
     99		/* assign all remaining space to this partition */
    100		size = SIZE_REMAINING;
    101		s++;
    102	} else {
    103		size = memparse(s, &s);
    104		if (!size) {
    105			pr_err("partition has size 0\n");
    106			return ERR_PTR(-EINVAL);
    107		}
    108	}
    109
    110	/* fetch partition name and flags */
    111	mask_flags = 0; /* this is going to be a regular partition */
    112	add_flags = 0;
    113	delim = 0;
    114
    115	/* check for offset */
    116	if (*s == '@') {
    117		s++;
    118		offset = memparse(s, &s);
    119	}
    120
    121	/* now look for name */
    122	if (*s == '(')
    123		delim = ')';
    124
    125	if (delim) {
    126		char *p;
    127
    128		name = ++s;
    129		p = strchr(name, delim);
    130		if (!p) {
    131			pr_err("no closing %c found in partition name\n", delim);
    132			return ERR_PTR(-EINVAL);
    133		}
    134		name_len = p - name;
    135		s = p + 1;
    136	} else {
    137		name = NULL;
    138		name_len = 13; /* Partition_000 */
    139	}
    140
    141	/* record name length for memory allocation later */
    142	extra_mem_size += name_len + 1;
    143
    144	/* test for options */
    145	if (strncmp(s, "ro", 2) == 0) {
    146		mask_flags |= MTD_WRITEABLE;
    147		s += 2;
    148	}
    149
    150	/* if lk is found do NOT unlock the MTD partition*/
    151	if (strncmp(s, "lk", 2) == 0) {
    152		mask_flags |= MTD_POWERUP_LOCK;
    153		s += 2;
    154	}
    155
    156	/* if slc is found use emulated SLC mode on this partition*/
    157	if (!strncmp(s, "slc", 3)) {
    158		add_flags |= MTD_SLC_ON_MLC_EMULATION;
    159		s += 3;
    160	}
    161
    162	/* test if more partitions are following */
    163	if (*s == ',') {
    164		if (size == SIZE_REMAINING) {
    165			pr_err("no partitions allowed after a fill-up partition\n");
    166			return ERR_PTR(-EINVAL);
    167		}
    168		/* more partitions follow, parse them */
    169		parts = newpart(s + 1, &s, num_parts, this_part + 1,
    170				&extra_mem, extra_mem_size);
    171		if (IS_ERR(parts))
    172			return parts;
    173	} else {
    174		/* this is the last partition: allocate space for all */
    175		int alloc_size;
    176
    177		*num_parts = this_part + 1;
    178		alloc_size = *num_parts * sizeof(struct mtd_partition) +
    179			     extra_mem_size;
    180
    181		parts = kzalloc(alloc_size, GFP_KERNEL);
    182		if (!parts)
    183			return ERR_PTR(-ENOMEM);
    184		extra_mem = (unsigned char *)(parts + *num_parts);
    185	}
    186
    187	/*
    188	 * enter this partition (offset will be calculated later if it is
    189	 * OFFSET_CONTINUOUS at this point)
    190	 */
    191	parts[this_part].size = size;
    192	parts[this_part].offset = offset;
    193	parts[this_part].mask_flags = mask_flags;
    194	parts[this_part].add_flags = add_flags;
    195	if (name)
    196		strlcpy(extra_mem, name, name_len + 1);
    197	else
    198		sprintf(extra_mem, "Partition_%03d", this_part);
    199	parts[this_part].name = extra_mem;
    200	extra_mem += name_len + 1;
    201
    202	dbg(("partition %d: name <%s>, offset %llx, size %llx, mask flags %x\n",
    203	     this_part, parts[this_part].name, parts[this_part].offset,
    204	     parts[this_part].size, parts[this_part].mask_flags));
    205
    206	/* return (updated) pointer to extra_mem memory */
    207	if (extra_mem_ptr)
    208		*extra_mem_ptr = extra_mem;
    209
    210	/* return (updated) pointer command line string */
    211	*retptr = s;
    212
    213	/* return partition table */
    214	return parts;
    215}
    216
    217/*
    218 * Parse the command line.
    219 */
    220static int mtdpart_setup_real(char *s)
    221{
    222	cmdline_parsed = 1;
    223
    224	for( ; s != NULL; )
    225	{
    226		struct cmdline_mtd_partition *this_mtd;
    227		struct mtd_partition *parts;
    228		int mtd_id_len, num_parts;
    229		char *p, *mtd_id, *semicol, *open_parenth;
    230
    231		/*
    232		 * Replace the first ';' by a NULL char so strrchr can work
    233		 * properly.
    234		 */
    235		semicol = strchr(s, ';');
    236		if (semicol)
    237			*semicol = '\0';
    238
    239		/*
    240		 * make sure that part-names with ":" will not be handled as
    241		 * part of the mtd-id with an ":"
    242		 */
    243		open_parenth = strchr(s, '(');
    244		if (open_parenth)
    245			*open_parenth = '\0';
    246
    247		mtd_id = s;
    248
    249		/*
    250		 * fetch <mtd-id>. We use strrchr to ignore all ':' that could
    251		 * be present in the MTD name, only the last one is interpreted
    252		 * as an <mtd-id>/<part-definition> separator.
    253		 */
    254		p = strrchr(s, ':');
    255
    256		/* Restore the '(' now. */
    257		if (open_parenth)
    258			*open_parenth = '(';
    259
    260		/* Restore the ';' now. */
    261		if (semicol)
    262			*semicol = ';';
    263
    264		if (!p) {
    265			pr_err("no mtd-id\n");
    266			return -EINVAL;
    267		}
    268		mtd_id_len = p - mtd_id;
    269
    270		dbg(("parsing <%s>\n", p+1));
    271
    272		/*
    273		 * parse one mtd. have it reserve memory for the
    274		 * struct cmdline_mtd_partition and the mtd-id string.
    275		 */
    276		parts = newpart(p + 1,		/* cmdline */
    277				&s,		/* out: updated cmdline ptr */
    278				&num_parts,	/* out: number of parts */
    279				0,		/* first partition */
    280				(unsigned char**)&this_mtd, /* out: extra mem */
    281				mtd_id_len + 1 + sizeof(*this_mtd) +
    282				sizeof(void*)-1 /*alignment*/);
    283		if (IS_ERR(parts)) {
    284			/*
    285			 * An error occurred. We're either:
    286			 * a) out of memory, or
    287			 * b) in the middle of the partition spec
    288			 * Either way, this mtd is hosed and we're
    289			 * unlikely to succeed in parsing any more
    290			 */
    291			 return PTR_ERR(parts);
    292		 }
    293
    294		/* align this_mtd */
    295		this_mtd = (struct cmdline_mtd_partition *)
    296				ALIGN((unsigned long)this_mtd, sizeof(void *));
    297		/* enter results */
    298		this_mtd->parts = parts;
    299		this_mtd->num_parts = num_parts;
    300		this_mtd->mtd_id = (char*)(this_mtd + 1);
    301		strlcpy(this_mtd->mtd_id, mtd_id, mtd_id_len + 1);
    302
    303		/* link into chain */
    304		this_mtd->next = partitions;
    305		partitions = this_mtd;
    306
    307		dbg(("mtdid=<%s> num_parts=<%d>\n",
    308		     this_mtd->mtd_id, this_mtd->num_parts));
    309
    310
    311		/* EOS - we're done */
    312		if (*s == 0)
    313			break;
    314
    315		/* does another spec follow? */
    316		if (*s != ';') {
    317			pr_err("bad character after partition (%c)\n", *s);
    318			return -EINVAL;
    319		}
    320		s++;
    321	}
    322
    323	return 0;
    324}
    325
    326/*
    327 * Main function to be called from the MTD mapping driver/device to
    328 * obtain the partitioning information. At this point the command line
    329 * arguments will actually be parsed and turned to struct mtd_partition
    330 * information. It returns partitions for the requested mtd device, or
    331 * the first one in the chain if a NULL mtd_id is passed in.
    332 */
    333static int parse_cmdline_partitions(struct mtd_info *master,
    334				    const struct mtd_partition **pparts,
    335				    struct mtd_part_parser_data *data)
    336{
    337	unsigned long long offset;
    338	int i, err;
    339	struct cmdline_mtd_partition *part;
    340	const char *mtd_id = master->name;
    341
    342	/* parse command line */
    343	if (!cmdline_parsed) {
    344		err = mtdpart_setup_real(cmdline);
    345		if (err)
    346			return err;
    347	}
    348
    349	/*
    350	 * Search for the partition definition matching master->name.
    351	 * If master->name is not set, stop at first partition definition.
    352	 */
    353	for (part = partitions; part; part = part->next) {
    354		if ((!mtd_id) || (!strcmp(part->mtd_id, mtd_id)))
    355			break;
    356	}
    357
    358	if (!part)
    359		return 0;
    360
    361	for (i = 0, offset = 0; i < part->num_parts; i++) {
    362		if (part->parts[i].offset == OFFSET_CONTINUOUS)
    363			part->parts[i].offset = offset;
    364		else
    365			offset = part->parts[i].offset;
    366
    367		if (part->parts[i].size == SIZE_REMAINING)
    368			part->parts[i].size = master->size - offset;
    369
    370		if (offset + part->parts[i].size > master->size) {
    371			pr_warn("%s: partitioning exceeds flash size, truncating\n",
    372				part->mtd_id);
    373			part->parts[i].size = master->size - offset;
    374		}
    375		offset += part->parts[i].size;
    376
    377		if (part->parts[i].size == 0) {
    378			pr_warn("%s: skipping zero sized partition\n",
    379				part->mtd_id);
    380			part->num_parts--;
    381			memmove(&part->parts[i], &part->parts[i + 1],
    382				sizeof(*part->parts) * (part->num_parts - i));
    383			i--;
    384		}
    385	}
    386
    387	*pparts = kmemdup(part->parts, sizeof(*part->parts) * part->num_parts,
    388			  GFP_KERNEL);
    389	if (!*pparts)
    390		return -ENOMEM;
    391
    392	return part->num_parts;
    393}
    394
    395
    396/*
    397 * This is the handler for our kernel parameter, called from
    398 * main.c::checksetup(). Note that we can not yet kmalloc() anything,
    399 * so we only save the commandline for later processing.
    400 *
    401 * This function needs to be visible for bootloaders.
    402 */
    403static int __init mtdpart_setup(char *s)
    404{
    405	cmdline = s;
    406	return 1;
    407}
    408
    409__setup("mtdparts=", mtdpart_setup);
    410
    411static struct mtd_part_parser cmdline_parser = {
    412	.parse_fn = parse_cmdline_partitions,
    413	.name = "cmdlinepart",
    414};
    415
    416static int __init cmdline_parser_init(void)
    417{
    418	if (mtdparts)
    419		mtdpart_setup(mtdparts);
    420	register_mtd_parser(&cmdline_parser);
    421	return 0;
    422}
    423
    424static void __exit cmdline_parser_exit(void)
    425{
    426	deregister_mtd_parser(&cmdline_parser);
    427}
    428
    429module_init(cmdline_parser_init);
    430module_exit(cmdline_parser_exit);
    431
    432MODULE_PARM_DESC(mtdparts, "Partitioning specification");
    433module_param(mtdparts, charp, 0);
    434
    435MODULE_LICENSE("GPL");
    436MODULE_AUTHOR("Marius Groeger <mag@sysgo.de>");
    437MODULE_DESCRIPTION("Command line configuration of MTD partitions");