0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038 #define pr_fmt(fmt) "mtd: " fmt
0039
0040 #include <linux/kernel.h>
0041 #include <linux/slab.h>
0042 #include <linux/mtd/mtd.h>
0043 #include <linux/mtd/partitions.h>
0044 #include <linux/module.h>
0045 #include <linux/err.h>
0046
0047
0048 #if 0
0049 #define dbg(x) do { printk("DEBUG-CMDLINE-PART: "); printk x; } while(0)
0050 #else
0051 #define dbg(x)
0052 #endif
0053
0054
0055
0056 #define SIZE_REMAINING ULLONG_MAX
0057 #define OFFSET_CONTINUOUS ULLONG_MAX
0058
0059 struct cmdline_mtd_partition {
0060 struct cmdline_mtd_partition *next;
0061 char *mtd_id;
0062 int num_parts;
0063 struct mtd_partition *parts;
0064 };
0065
0066
0067 static struct cmdline_mtd_partition *partitions;
0068
0069
0070 static char *mtdparts;
0071 static char *cmdline;
0072 static int cmdline_parsed;
0073
0074
0075
0076
0077
0078
0079
0080
0081
0082 static struct mtd_partition * newpart(char *s,
0083 char **retptr,
0084 int *num_parts,
0085 int this_part,
0086 unsigned char **extra_mem_ptr,
0087 int extra_mem_size)
0088 {
0089 struct mtd_partition *parts;
0090 unsigned long long size, offset = OFFSET_CONTINUOUS;
0091 char *name;
0092 int name_len;
0093 unsigned char *extra_mem;
0094 char delim;
0095 unsigned int mask_flags, add_flags;
0096
0097
0098 if (*s == '-') {
0099
0100 size = SIZE_REMAINING;
0101 s++;
0102 } else {
0103 size = memparse(s, &s);
0104 if (!size) {
0105 pr_err("partition has size 0\n");
0106 return ERR_PTR(-EINVAL);
0107 }
0108 }
0109
0110
0111 mask_flags = 0;
0112 add_flags = 0;
0113 delim = 0;
0114
0115
0116 if (*s == '@') {
0117 s++;
0118 offset = memparse(s, &s);
0119 }
0120
0121
0122 if (*s == '(')
0123 delim = ')';
0124
0125 if (delim) {
0126 char *p;
0127
0128 name = ++s;
0129 p = strchr(name, delim);
0130 if (!p) {
0131 pr_err("no closing %c found in partition name\n", delim);
0132 return ERR_PTR(-EINVAL);
0133 }
0134 name_len = p - name;
0135 s = p + 1;
0136 } else {
0137 name = NULL;
0138 name_len = 13;
0139 }
0140
0141
0142 extra_mem_size += name_len + 1;
0143
0144
0145 if (strncmp(s, "ro", 2) == 0) {
0146 mask_flags |= MTD_WRITEABLE;
0147 s += 2;
0148 }
0149
0150
0151 if (strncmp(s, "lk", 2) == 0) {
0152 mask_flags |= MTD_POWERUP_LOCK;
0153 s += 2;
0154 }
0155
0156
0157 if (!strncmp(s, "slc", 3)) {
0158 add_flags |= MTD_SLC_ON_MLC_EMULATION;
0159 s += 3;
0160 }
0161
0162
0163 if (*s == ',') {
0164 if (size == SIZE_REMAINING) {
0165 pr_err("no partitions allowed after a fill-up partition\n");
0166 return ERR_PTR(-EINVAL);
0167 }
0168
0169 parts = newpart(s + 1, &s, num_parts, this_part + 1,
0170 &extra_mem, extra_mem_size);
0171 if (IS_ERR(parts))
0172 return parts;
0173 } else {
0174
0175 int alloc_size;
0176
0177 *num_parts = this_part + 1;
0178 alloc_size = *num_parts * sizeof(struct mtd_partition) +
0179 extra_mem_size;
0180
0181 parts = kzalloc(alloc_size, GFP_KERNEL);
0182 if (!parts)
0183 return ERR_PTR(-ENOMEM);
0184 extra_mem = (unsigned char *)(parts + *num_parts);
0185 }
0186
0187
0188
0189
0190
0191 parts[this_part].size = size;
0192 parts[this_part].offset = offset;
0193 parts[this_part].mask_flags = mask_flags;
0194 parts[this_part].add_flags = add_flags;
0195 if (name)
0196 strlcpy(extra_mem, name, name_len + 1);
0197 else
0198 sprintf(extra_mem, "Partition_%03d", this_part);
0199 parts[this_part].name = extra_mem;
0200 extra_mem += name_len + 1;
0201
0202 dbg(("partition %d: name <%s>, offset %llx, size %llx, mask flags %x\n",
0203 this_part, parts[this_part].name, parts[this_part].offset,
0204 parts[this_part].size, parts[this_part].mask_flags));
0205
0206
0207 if (extra_mem_ptr)
0208 *extra_mem_ptr = extra_mem;
0209
0210
0211 *retptr = s;
0212
0213
0214 return parts;
0215 }
0216
0217
0218
0219
0220 static int mtdpart_setup_real(char *s)
0221 {
0222 cmdline_parsed = 1;
0223
0224 for( ; s != NULL; )
0225 {
0226 struct cmdline_mtd_partition *this_mtd;
0227 struct mtd_partition *parts;
0228 int mtd_id_len, num_parts;
0229 char *p, *mtd_id, *semicol, *open_parenth;
0230
0231
0232
0233
0234
0235 semicol = strchr(s, ';');
0236 if (semicol)
0237 *semicol = '\0';
0238
0239
0240
0241
0242
0243 open_parenth = strchr(s, '(');
0244 if (open_parenth)
0245 *open_parenth = '\0';
0246
0247 mtd_id = s;
0248
0249
0250
0251
0252
0253
0254 p = strrchr(s, ':');
0255
0256
0257 if (open_parenth)
0258 *open_parenth = '(';
0259
0260
0261 if (semicol)
0262 *semicol = ';';
0263
0264 if (!p) {
0265 pr_err("no mtd-id\n");
0266 return -EINVAL;
0267 }
0268 mtd_id_len = p - mtd_id;
0269
0270 dbg(("parsing <%s>\n", p+1));
0271
0272
0273
0274
0275
0276 parts = newpart(p + 1,
0277 &s,
0278 &num_parts,
0279 0,
0280 (unsigned char**)&this_mtd,
0281 mtd_id_len + 1 + sizeof(*this_mtd) +
0282 sizeof(void*)-1 );
0283 if (IS_ERR(parts)) {
0284
0285
0286
0287
0288
0289
0290
0291 return PTR_ERR(parts);
0292 }
0293
0294
0295 this_mtd = (struct cmdline_mtd_partition *)
0296 ALIGN((unsigned long)this_mtd, sizeof(void *));
0297
0298 this_mtd->parts = parts;
0299 this_mtd->num_parts = num_parts;
0300 this_mtd->mtd_id = (char*)(this_mtd + 1);
0301 strlcpy(this_mtd->mtd_id, mtd_id, mtd_id_len + 1);
0302
0303
0304 this_mtd->next = partitions;
0305 partitions = this_mtd;
0306
0307 dbg(("mtdid=<%s> num_parts=<%d>\n",
0308 this_mtd->mtd_id, this_mtd->num_parts));
0309
0310
0311
0312 if (*s == 0)
0313 break;
0314
0315
0316 if (*s != ';') {
0317 pr_err("bad character after partition (%c)\n", *s);
0318 return -EINVAL;
0319 }
0320 s++;
0321 }
0322
0323 return 0;
0324 }
0325
0326
0327
0328
0329
0330
0331
0332
0333 static int parse_cmdline_partitions(struct mtd_info *master,
0334 const struct mtd_partition **pparts,
0335 struct mtd_part_parser_data *data)
0336 {
0337 unsigned long long offset;
0338 int i, err;
0339 struct cmdline_mtd_partition *part;
0340 const char *mtd_id = master->name;
0341
0342
0343 if (!cmdline_parsed) {
0344 err = mtdpart_setup_real(cmdline);
0345 if (err)
0346 return err;
0347 }
0348
0349
0350
0351
0352
0353 for (part = partitions; part; part = part->next) {
0354 if ((!mtd_id) || (!strcmp(part->mtd_id, mtd_id)))
0355 break;
0356 }
0357
0358 if (!part)
0359 return 0;
0360
0361 for (i = 0, offset = 0; i < part->num_parts; i++) {
0362 if (part->parts[i].offset == OFFSET_CONTINUOUS)
0363 part->parts[i].offset = offset;
0364 else
0365 offset = part->parts[i].offset;
0366
0367 if (part->parts[i].size == SIZE_REMAINING)
0368 part->parts[i].size = master->size - offset;
0369
0370 if (offset + part->parts[i].size > master->size) {
0371 pr_warn("%s: partitioning exceeds flash size, truncating\n",
0372 part->mtd_id);
0373 part->parts[i].size = master->size - offset;
0374 }
0375 offset += part->parts[i].size;
0376
0377 if (part->parts[i].size == 0) {
0378 pr_warn("%s: skipping zero sized partition\n",
0379 part->mtd_id);
0380 part->num_parts--;
0381 memmove(&part->parts[i], &part->parts[i + 1],
0382 sizeof(*part->parts) * (part->num_parts - i));
0383 i--;
0384 }
0385 }
0386
0387 *pparts = kmemdup(part->parts, sizeof(*part->parts) * part->num_parts,
0388 GFP_KERNEL);
0389 if (!*pparts)
0390 return -ENOMEM;
0391
0392 return part->num_parts;
0393 }
0394
0395
0396
0397
0398
0399
0400
0401
0402
0403 static int __init mtdpart_setup(char *s)
0404 {
0405 cmdline = s;
0406 return 1;
0407 }
0408
0409 __setup("mtdparts=", mtdpart_setup);
0410
0411 static struct mtd_part_parser cmdline_parser = {
0412 .parse_fn = parse_cmdline_partitions,
0413 .name = "cmdlinepart",
0414 };
0415
0416 static int __init cmdline_parser_init(void)
0417 {
0418 if (mtdparts)
0419 mtdpart_setup(mtdparts);
0420 register_mtd_parser(&cmdline_parser);
0421 return 0;
0422 }
0423
0424 static void __exit cmdline_parser_exit(void)
0425 {
0426 deregister_mtd_parser(&cmdline_parser);
0427 }
0428
0429 module_init(cmdline_parser_init);
0430 module_exit(cmdline_parser_exit);
0431
0432 MODULE_PARM_DESC(mtdparts, "Partitioning specification");
0433 module_param(mtdparts, charp, 0);
0434
0435 MODULE_LICENSE("GPL");
0436 MODULE_AUTHOR("Marius Groeger <mag@sysgo.de>");
0437 MODULE_DESCRIPTION("Command line configuration of MTD partitions");