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");