Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v2.6.14-rc2 371 lines 9.2 kB view raw
1/* 2 * $Id: cmdlinepart.c,v 1.18 2005/06/07 15:04:26 joern Exp $ 3 * 4 * Read flash partition table from command line 5 * 6 * Copyright 2002 SYSGO Real-Time Solutions GmbH 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] 13 * <mtd-id> := unique name used in mapping driver/device (mtd->name) 14 * <size> := standard linux memsize OR "-" to denote all remaining space 15 * <name> := '(' NAME ')' 16 * 17 * Examples: 18 * 19 * 1 NOR Flash, with 1 single writable partition: 20 * edb7312-nor:- 21 * 22 * 1 NOR Flash with 2 partitions, 1 NAND with one 23 * edb7312-nor:256k(ARMboot)ro,-(root);edb7312-nand:-(home) 24 */ 25 26#include <linux/kernel.h> 27#include <linux/slab.h> 28 29#include <linux/mtd/mtd.h> 30#include <linux/mtd/partitions.h> 31#include <linux/bootmem.h> 32 33/* error message prefix */ 34#define ERRP "mtd: " 35 36/* debug macro */ 37#if 0 38#define dbg(x) do { printk("DEBUG-CMDLINE-PART: "); printk x; } while(0) 39#else 40#define dbg(x) 41#endif 42 43 44/* special size referring to all the remaining space in a partition */ 45#define SIZE_REMAINING 0xffffffff 46 47struct cmdline_mtd_partition { 48 struct cmdline_mtd_partition *next; 49 char *mtd_id; 50 int num_parts; 51 struct mtd_partition *parts; 52}; 53 54/* mtdpart_setup() parses into here */ 55static struct cmdline_mtd_partition *partitions; 56 57/* the command line passed to mtdpart_setupd() */ 58static char *cmdline; 59static int cmdline_parsed = 0; 60 61/* 62 * Parse one partition definition for an MTD. Since there can be many 63 * comma separated partition definitions, this function calls itself 64 * recursively until no more partition definitions are found. Nice side 65 * effect: the memory to keep the mtd_partition structs and the names 66 * is allocated upon the last definition being found. At that point the 67 * syntax has been verified ok. 68 */ 69static struct mtd_partition * newpart(char *s, 70 char **retptr, 71 int *num_parts, 72 int this_part, 73 unsigned char **extra_mem_ptr, 74 int extra_mem_size) 75{ 76 struct mtd_partition *parts; 77 unsigned long size; 78 unsigned long offset = 0; 79 char *name; 80 int name_len; 81 unsigned char *extra_mem; 82 char delim; 83 unsigned int mask_flags; 84 85 /* fetch the partition size */ 86 if (*s == '-') 87 { /* assign all remaining space to this partition */ 88 size = SIZE_REMAINING; 89 s++; 90 } 91 else 92 { 93 size = memparse(s, &s); 94 if (size < PAGE_SIZE) 95 { 96 printk(KERN_ERR ERRP "partition size too small (%lx)\n", size); 97 return NULL; 98 } 99 } 100 101 /* fetch partition name and flags */ 102 mask_flags = 0; /* this is going to be a regular partition */ 103 delim = 0; 104 /* check for offset */ 105 if (*s == '@') 106 { 107 s++; 108 offset = memparse(s, &s); 109 } 110 /* now look for name */ 111 if (*s == '(') 112 { 113 delim = ')'; 114 } 115 116 if (delim) 117 { 118 char *p; 119 120 name = ++s; 121 if ((p = strchr(name, delim)) == 0) 122 { 123 printk(KERN_ERR ERRP "no closing %c found in partition name\n", delim); 124 return NULL; 125 } 126 name_len = p - name; 127 s = p + 1; 128 } 129 else 130 { 131 name = NULL; 132 name_len = 13; /* Partition_000 */ 133 } 134 135 /* record name length for memory allocation later */ 136 extra_mem_size += name_len + 1; 137 138 /* test for options */ 139 if (strncmp(s, "ro", 2) == 0) 140 { 141 mask_flags |= MTD_WRITEABLE; 142 s += 2; 143 } 144 145 /* test if more partitions are following */ 146 if (*s == ',') 147 { 148 if (size == SIZE_REMAINING) 149 { 150 printk(KERN_ERR ERRP "no partitions allowed after a fill-up partition\n"); 151 return NULL; 152 } 153 /* more partitions follow, parse them */ 154 if ((parts = newpart(s + 1, &s, num_parts, 155 this_part + 1, &extra_mem, extra_mem_size)) == 0) 156 return NULL; 157 } 158 else 159 { /* this is the last partition: allocate space for all */ 160 int alloc_size; 161 162 *num_parts = this_part + 1; 163 alloc_size = *num_parts * sizeof(struct mtd_partition) + 164 extra_mem_size; 165 parts = kmalloc(alloc_size, GFP_KERNEL); 166 if (!parts) 167 { 168 printk(KERN_ERR ERRP "out of memory\n"); 169 return NULL; 170 } 171 memset(parts, 0, alloc_size); 172 extra_mem = (unsigned char *)(parts + *num_parts); 173 } 174 /* enter this partition (offset will be calculated later if it is zero at this point) */ 175 parts[this_part].size = size; 176 parts[this_part].offset = offset; 177 parts[this_part].mask_flags = mask_flags; 178 if (name) 179 { 180 strlcpy(extra_mem, name, name_len + 1); 181 } 182 else 183 { 184 sprintf(extra_mem, "Partition_%03d", this_part); 185 } 186 parts[this_part].name = extra_mem; 187 extra_mem += name_len + 1; 188 189 dbg(("partition %d: name <%s>, offset %x, size %x, mask flags %x\n", 190 this_part, 191 parts[this_part].name, 192 parts[this_part].offset, 193 parts[this_part].size, 194 parts[this_part].mask_flags)); 195 196 /* return (updated) pointer to extra_mem memory */ 197 if (extra_mem_ptr) 198 *extra_mem_ptr = extra_mem; 199 200 /* return (updated) pointer command line string */ 201 *retptr = s; 202 203 /* return partition table */ 204 return parts; 205} 206 207/* 208 * Parse the command line. 209 */ 210static int mtdpart_setup_real(char *s) 211{ 212 cmdline_parsed = 1; 213 214 for( ; s != NULL; ) 215 { 216 struct cmdline_mtd_partition *this_mtd; 217 struct mtd_partition *parts; 218 int mtd_id_len; 219 int num_parts; 220 char *p, *mtd_id; 221 222 mtd_id = s; 223 /* fetch <mtd-id> */ 224 if (!(p = strchr(s, ':'))) 225 { 226 printk(KERN_ERR ERRP "no mtd-id\n"); 227 return 0; 228 } 229 mtd_id_len = p - mtd_id; 230 231 dbg(("parsing <%s>\n", p+1)); 232 233 /* 234 * parse one mtd. have it reserve memory for the 235 * struct cmdline_mtd_partition and the mtd-id string. 236 */ 237 parts = newpart(p + 1, /* cmdline */ 238 &s, /* out: updated cmdline ptr */ 239 &num_parts, /* out: number of parts */ 240 0, /* first partition */ 241 (unsigned char**)&this_mtd, /* out: extra mem */ 242 mtd_id_len + 1 + sizeof(*this_mtd) + 243 sizeof(void*)-1 /*alignment*/); 244 if(!parts) 245 { 246 /* 247 * An error occurred. We're either: 248 * a) out of memory, or 249 * b) in the middle of the partition spec 250 * Either way, this mtd is hosed and we're 251 * unlikely to succeed in parsing any more 252 */ 253 return 0; 254 } 255 256 /* align this_mtd */ 257 this_mtd = (struct cmdline_mtd_partition *) 258 ALIGN((unsigned long)this_mtd, sizeof(void*)); 259 /* enter results */ 260 this_mtd->parts = parts; 261 this_mtd->num_parts = num_parts; 262 this_mtd->mtd_id = (char*)(this_mtd + 1); 263 strlcpy(this_mtd->mtd_id, mtd_id, mtd_id_len + 1); 264 265 /* link into chain */ 266 this_mtd->next = partitions; 267 partitions = this_mtd; 268 269 dbg(("mtdid=<%s> num_parts=<%d>\n", 270 this_mtd->mtd_id, this_mtd->num_parts)); 271 272 273 /* EOS - we're done */ 274 if (*s == 0) 275 break; 276 277 /* does another spec follow? */ 278 if (*s != ';') 279 { 280 printk(KERN_ERR ERRP "bad character after partition (%c)\n", *s); 281 return 0; 282 } 283 s++; 284 } 285 return 1; 286} 287 288/* 289 * Main function to be called from the MTD mapping driver/device to 290 * obtain the partitioning information. At this point the command line 291 * arguments will actually be parsed and turned to struct mtd_partition 292 * information. It returns partitions for the requested mtd device, or 293 * the first one in the chain if a NULL mtd_id is passed in. 294 */ 295static int parse_cmdline_partitions(struct mtd_info *master, 296 struct mtd_partition **pparts, 297 unsigned long origin) 298{ 299 unsigned long offset; 300 int i; 301 struct cmdline_mtd_partition *part; 302 char *mtd_id = master->name; 303 304 if(!cmdline) 305 return -EINVAL; 306 307 /* parse command line */ 308 if (!cmdline_parsed) 309 mtdpart_setup_real(cmdline); 310 311 for(part = partitions; part; part = part->next) 312 { 313 if ((!mtd_id) || (!strcmp(part->mtd_id, mtd_id))) 314 { 315 for(i = 0, offset = 0; i < part->num_parts; i++) 316 { 317 if (!part->parts[i].offset) 318 part->parts[i].offset = offset; 319 else 320 offset = part->parts[i].offset; 321 if (part->parts[i].size == SIZE_REMAINING) 322 part->parts[i].size = master->size - offset; 323 if (offset + part->parts[i].size > master->size) 324 { 325 printk(KERN_WARNING ERRP 326 "%s: partitioning exceeds flash size, truncating\n", 327 part->mtd_id); 328 part->parts[i].size = master->size - offset; 329 part->num_parts = i; 330 } 331 offset += part->parts[i].size; 332 } 333 *pparts = part->parts; 334 return part->num_parts; 335 } 336 } 337 return -EINVAL; 338} 339 340 341/* 342 * This is the handler for our kernel parameter, called from 343 * main.c::checksetup(). Note that we can not yet kmalloc() anything, 344 * so we only save the commandline for later processing. 345 * 346 * This function needs to be visible for bootloaders. 347 */ 348int mtdpart_setup(char *s) 349{ 350 cmdline = s; 351 return 1; 352} 353 354__setup("mtdparts=", mtdpart_setup); 355 356static struct mtd_part_parser cmdline_parser = { 357 .owner = THIS_MODULE, 358 .parse_fn = parse_cmdline_partitions, 359 .name = "cmdlinepart", 360}; 361 362static int __init cmdline_parser_init(void) 363{ 364 return register_mtd_parser(&cmdline_parser); 365} 366 367module_init(cmdline_parser_init); 368 369MODULE_LICENSE("GPL"); 370MODULE_AUTHOR("Marius Groeger <mag@sysgo.de>"); 371MODULE_DESCRIPTION("Command line configuration of MTD partitions");