MTD支持三种创建分区表的方式:
- cmdline:通过bootargs传入。
- dts:在dts中编写。
- struct mtd_partition结构体代码:代码中固定配置。
他们的优先级是:cmdline > dts > 结构体代码。
mtd_device_parse_register--解析MTD分区表,并注册MTD设备。
parse_mtd_partitions--根据types指定的parserr名称解析分区表到parser_data中。
mtd_part_of_parse--解析DTS中分区表。
mtd_part_do_parse
mtd_part_parser_get--获取parser,比如cmdline parser。
mtd_part_do_parse--调用parser_fn完成解析,比如parse_cmdline_partitions()。
add_mtd_partitions
add_mtd_partitions--在cmdline和dts partition都失败的情况下,使用代码中配置MTD分区表。
MTD设备注册包括两种,区别是:
mtd_device_parse_register相对于mtd_device_register多了types和parser_data的设置。
extern int mtd_device_parse_register(struct mtd_info *mtd, const char * const *part_probe_types, struct mtd_part_parser_data *parser_data, const struct mtd_partition *defparts, int defnr_parts); #define mtd_device_register(master, parts, nr_parts) \ mtd_device_parse_register(master, NULL, NULL, parts, nr_parts) extern int mtd_device_unregister(struct mtd_info *master);
mtd_device_parse_register()是MTD注册的入口,并负责解析创建分区表:
int mtd_device_parse_register(struct mtd_info *mtd, const char * const *types, struct mtd_part_parser_data *parser_data, const struct mtd_partition *parts, int nr_parts) { ... ret = parse_mtd_partitions(mtd, types, parser_data);--根据types指定的parser,或者遍历parser解析出MTD分区表,并创建分区。 if (ret > 0) ret = 0; else if (nr_parts) ret = add_mtd_partitions(mtd, parts, nr_parts);--根据提供的parts/nr_parts分区信息创建分区。 else if (!device_is_registered(&mtd->dev)) ret = add_mtd_device(mtd); else ret = 0; ... }
parse_mtd_partitions()负责解析cmdline和dts配置的MTD分区表:
static const char * const default_mtd_part_types[] = { "cmdlinepart", "ofpart", NULL }; /* Check DT only when looking for subpartitions. */ static const char * const default_subpartition_types[] = { "ofpart", NULL }; int parse_mtd_partitions(struct mtd_info *master, const char *const *types, struct mtd_part_parser_data *data) { struct mtd_partitions pparts = { }; struct mtd_part_parser *parser; int ret, err = 0; if (!types) types = mtd_is_partition(master) ? default_subpartition_types : default_mtd_part_types;--如果没有指定types名称,则选择系统指定的parser列表。 for ( ; *types; types++) {--遍历types列表,逐个进行解析。正确找到分区表后返回。对于default_mtd_part_types,则依次进行cmdline和dts的分区解析。 if (!strcmp(*types, "ofpart")) { ret = mtd_part_of_parse(master, &pparts);--解析dts的MTD分区表。 } else { parser = mtd_part_parser_get(*types);--解析非dts的MTD分区表,已知的有cmdline。 ... ret = mtd_part_do_parse(parser, master, &pparts, data); if (ret <= 0) mtd_part_parser_put(parser); } /* Found partitions! */ if (ret > 0) { err = add_mtd_partitions(master, pparts.parts, pparts.nr_parts);--解析成功后即添加MTD设备和分区属性。 mtd_part_parser_cleanup(&pparts); return err ? err : pparts.nr_parts; } ... } return err; }
1 代码中创建分区表
在代码中编写struct mtd_partition结构体,比如:
static const struct mtd_partition partition_info[] = { { .name = "Kernel", .offset = 0, .size = 3 * SZ_1M + SZ_512K }, { .name = "u-boot", .offset = 3 * SZ_1M + SZ_512K, .size = SZ_256K }, ... };
然后调用mtd_device_register()/mtd_device_parse_register()注册即可。
2 dts中创建分区表
dts中分区表如下:
nand: nand@0x12345678 {
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
partition@xx {
label = "xxx";
reg = <start size>;
read-only;
};
...
}
}
mtd_of_parser()根据dts指定的parser进行解析,否则使用默认的fixed-partitions parser。
static int mtd_part_of_parse(struct mtd_info *master, struct mtd_partitions *pparts) {
... const char *fixed = "fixed-partitions"; int ret, err = 0; ... of_property_for_each_string(np, "compatible", prop, compat) {--读取partitions的compatible属性,获取parser名称。 parser = mtd_part_get_compatible_parser(compat); if (!parser) continue; ret = mtd_part_do_parse(parser, master, pparts, NULL); if (ret > 0) { of_node_put(np); return ret; } mtd_part_parser_put(parser); if (ret < 0 && !err) err = ret; } of_node_put(np); parser = mtd_part_parser_get(fixed);--如果partitions中没有指定parser名称,或者解析失败。则使用默认的fixed-partition parser。 if (!parser && !request_module("%s", fixed))--请求加载fixed-partitions模组。 parser = mtd_part_parser_get(fixed); if (parser) { ret = mtd_part_do_parse(parser, master, pparts, NULL); if (ret > 0) return ret; mtd_part_parser_put(parser); if (ret < 0 && !err) err = ret; } return err; }
ofpart_parser_init注册dts parser:
MODULE_ALIAS("fixed-partitions"); MODULE_ALIAS("ofoldpart"); static int __init ofpart_parser_init(void) { register_mtd_parser(&ofpart_parser); register_mtd_parser(&ofoldpart_parser); return 0; } static void __exit ofpart_parser_exit(void) { deregister_mtd_parser(&ofpart_parser); deregister_mtd_parser(&ofoldpart_parser); } module_init(ofpart_parser_init); module_exit(ofpart_parser_exit); static struct mtd_part_parser ofpart_parser = { .parse_fn = parse_fixed_partitions, .name = "fixed-partitions", .of_match_table = parse_ofpart_match_table, };
parse_fixed_partitions()解析dts中每一个partition,包括offset、size、label、read-only、lock等:
static int parse_fixed_partitions(struct mtd_info *master, const struct mtd_partition **pparts, struct mtd_part_parser_data *data) { struct mtd_partition *parts; struct device_node *mtd_node; struct device_node *ofpart_node; const char *partname; struct device_node *pp; int nr_parts, i, ret = 0; bool dedicated = true; /* Pull of_node from the master device node */ mtd_node = mtd_get_of_node(master); if (!mtd_node) return 0; ofpart_node = of_get_child_by_name(mtd_node, "partitions"); ... nr_parts = 0; for_each_child_of_node(ofpart_node, pp) { if (!dedicated && node_has_compatible(pp)) continue; nr_parts++;--获取总分区数。 } if (nr_parts == 0) return 0; parts = kcalloc(nr_parts, sizeof(*parts), GFP_KERNEL);--为每个分区分配结构体。 if (!parts) return -ENOMEM; i = 0; for_each_child_of_node(ofpart_node, pp) { ... reg = of_get_property(pp, "reg", &len);--reg包括两个成员,依次是offset和size。 ... a_cells = of_n_addr_cells(pp); s_cells = of_n_size_cells(pp); if (len / 4 != a_cells + s_cells) { pr_debug("%s: ofpart partition %pOF (%pOF) error parsing reg property.\n", master->name, pp, mtd_node); goto ofpart_fail; } parts[i].offset = of_read_number(reg, a_cells); parts[i].size = of_read_number(reg + a_cells, s_cells); parts[i].of_node = pp; partname = of_get_property(pp, "label", &len);--分区名称设置。 if (!partname) partname = of_get_property(pp, "name", &len); parts[i].name = partname; if (of_get_property(pp, "read-only", &len))--read-only属性设置。 parts[i].mask_flags |= MTD_WRITEABLE; if (of_get_property(pp, "lock", &len))--lock属性设置。 parts[i].mask_flags |= MTD_POWERUP_LOCK; i++; } ... }
3 cmdline创建分区表
内核中支持cmdline分区表,需要如下配置:
Device Drivers ->Memory Technology Device(MTD) support ->Partition parsers ->Command line partition table parsing
在内核启动时通过register_mtd_parser()注册cmdline_parser,parser的名称为cmdlinepart:
static int __init mtdpart_setup(char *s) { cmdline = s; return 1; } __setup("mtdparts=", mtdpart_setup);--当bootargs设置了mtdparts=xxx后被调用,并将xxx赋给cmdline。 static struct mtd_part_parser cmdline_parser = { .parse_fn = parse_cmdline_partitions, .name = "cmdlinepart", }; static int __init cmdline_parser_init(void) { if (mtdparts) mtdpart_setup(mtdparts); register_mtd_parser(&cmdline_parser); return 0; } static void __exit cmdline_parser_exit(void) { deregister_mtd_parser(&cmdline_parser); } module_init(cmdline_parser_init); module_exit(cmdline_parser_exit);
parse_cmdline_partitions()从cmdline中解析出MTD分区信息,填充到pparts中:
static int parse_cmdline_partitions(struct mtd_info *master, const struct mtd_partition **pparts, struct mtd_part_parser_data *data) { ... /* parse command line */ if (!cmdline_parsed) { err = mtdpart_setup_real(cmdline);--根据cmdline内容,解析MTD分区信息到partitions中。 if (err) return err; } ...for (i = 0, offset = 0; i < part->num_parts; i++) {--对offset/size做检查。 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; if (offset + part->parts[i].size > master->size) { pr_warn("%s: partitioning exceeds flash size, truncating\n", part->mtd_id); part->parts[i].size = master->size - offset; } offset += part->parts[i].size; if (part->parts[i].size == 0) { pr_warn("%s: skipping zero sized partition\n", part->mtd_id); part->num_parts--; memmove(&part->parts[i], &part->parts[i + 1], sizeof(*part->parts) * (part->num_parts - i)); i--; } } *pparts = kmemdup(part->parts, sizeof(*part->parts) * part->num_parts, GFP_KERNEL); ... }
mtdpart_setup_real()调用netpart()解析每一个分区:
static struct mtd_partition * newpart(char *s, char **retptr, int *num_parts, int this_part, unsigned char **extra_mem_ptr, int extra_mem_size) { ... /* fetch the partition size */ if (*s == '-') {--表示分区占用剩下所有空间。 /* assign all remaining space to this partition */ size = SIZE_REMAINING; s++; } else { size = memparse(s, &s);--获取分区大小。 ... } /* fetch partition name and flags */ mask_flags = 0; /* this is going to be a regular partition */ delim = 0; /* check for offset */ if (*s == '@') { s++; offset = memparse(s, &s);--获取offset大小。 } ... /* record name length for memory allocation later */ extra_mem_size += name_len + 1; /* test for options */ if (strncmp(s, "ro", 2) == 0) {--对应dts的read-only属性。 mask_flags |= MTD_WRITEABLE; s += 2; } if (strncmp(s, "lk", 2) == 0) {--对应dts的lock属性。 mask_flags |= MTD_POWERUP_LOCK; s += 2; } /* test if more partitions are following */ if (*s == ',') { if (size == SIZE_REMAINING) { pr_err("no partitions allowed after a fill-up partition\n"); return ERR_PTR(-EINVAL); } /* more partitions follow, parse them */ parts = newpart(s + 1, &s, num_parts, this_part + 1, &extra_mem, extra_mem_size); if (IS_ERR(parts)) return parts; } else { /* this is the last partition: allocate space for all */ int alloc_size; *num_parts = this_part + 1; alloc_size = *num_parts * sizeof(struct mtd_partition) + extra_mem_size; parts = kzalloc(alloc_size, GFP_KERNEL); if (!parts) return ERR_PTR(-ENOMEM); extra_mem = (unsigned char *)(parts + *num_parts); } /* * enter this partition (offset will be calculated later if it is * OFFSET_CONTINUOUS at this point) */ parts[this_part].size = size; parts[this_part].offset = offset; parts[this_part].mask_flags = mask_flags; ... /* return partition table */ return parts; }
3.1 bootargs中mtdparts设置
bootargs="mtdparts=edb7312-nor:256k(ARMboot)ro,-(root);edb7312-nand:-(home)"