這裏基於 openwrt mt7620a 平臺來跟蹤,主要是想理清 dts 裏的分區描述是如何一步步轉化成內核分區行爲。linux
先來看看 dts 中關於分區的描述:數組
palmbus@10000000 { spi@b00 { status = "okay"; m25p80@0 { #address-cells = <1>; #size-cells = <1>; compatible = "w25q128"; reg = <0 0>; linux,modalias = "m25p80", "w25q128"; spi-max-frequency = <10000000>; partition@0 { label = "u-boot"; reg = <0x0 0x30000>; read-only; }; partition@30000 { label = "u-boot-env"; reg = <0x30000 0x10000>; read-only; }; factory: partition@40000 { label = "factory"; reg = <0x40000 0x10000>; read-only; }; partition@50000 { label = "firmware"; reg = <0x50000 0xfb0000>; }; }; };
dts 描述的是一個樹狀結構。spi 控制器掛在 platform 總線上,spi flash (w25q128) 掛在 spi 總線上。 探測到 spi flash 的流程以下:函數
part_parser 用來按照某種規則將分區信息解析出來。這些規則能夠有不少,內核裏調用 register_mtd_parser() 便可註冊一個新的解析器。ui
drivers/mtd/mtdpart.c 中維護了一個鏈表 part_parsers,解析器按註冊順序添加到這個鏈表裏。code
parse_mtd_partitions() 中,若是未指定解析器的話,則默認只容許用 cmdlinepart, ofpart 兩種解析器。對於咱們這裏,實際上起做用的是 ofpart。orm
static struct mtd_part_parser ofpart_parser = { .owner = THIS_MODULE, .parse_fn = parse_ofpart_partitions, .name = "ofpart", };
parse_ofpart_partitions() 遍歷 dts 中 spi flash 設備下的分區描述信息,取出其中的 reg, label, name, read-only, lock 等信息以填充一個 struct mtd_partition 結構體。上面 dts 裏描述了 4 個分區, 就有一個大小爲 4 的 struct mtd_partition 數組,最後由 add_mtd_partitions() 添加爲各 mtd 分區。cmd
分區的狀況能夠待系統啓動後在 /proc/mtd 文件中查看到。flash
# cat /proc/mtd dev: size erasesize name mtd0: 00030000 00010000 "u-boot" mtd1: 00010000 00010000 "u-boot-env" mtd2: 00010000 00010000 "factory" mtd3: 00fb0000 00010000 "firmware" mtd4: 00ea9283 00010000 "rootfs" mtd5: 00b30000 00010000 "rootfs_data"
上面 /proc/mtd 的內容中相比 dts 中的描述多了兩個分區 rootfs, rootfs_data。這兩個分區是什麼時候添加的呢?it
看看添加 mtd 分區的函數:io
int add_mtd_partitions(struct mtd_info *master, const struct mtd_partition *parts, int nbparts) { struct mtd_part *slave; uint64_t cur_offset = 0; int i; printk(KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nbparts, master->name); for (i = 0; i < nbparts; i++) { slave = allocate_partition(master, parts + i, i, cur_offset); if (IS_ERR(slave)) return PTR_ERR(slave); mutex_lock(&mtd_partitions_mutex); list_add(&slave->list, &mtd_partitions); mutex_unlock(&mtd_partitions_mutex); add_mtd_device(&slave->mtd); mtd_partition_split(master, slave); cur_offset = slave->offset + slave->mtd.size; } return 0; }
最後調用了 mtd_partition_split()。
static void mtd_partition_split(struct mtd_info *master, struct mtd_part *part) { static int rootfs_found = 0; if (rootfs_found) return; if (!strcmp(part->mtd.name, "rootfs")) { rootfs_found = 1; if (config_enabled(CONFIG_MTD_ROOTFS_SPLIT)) split_rootfs_data(master, part); } if (!strcmp(part->mtd.name, SPLIT_FIRMWARE_NAME) && config_enabled(CONFIG_MTD_SPLIT_FIRMWARE)) split_firmware(master, part); arch_split_mtd_part(master, part->mtd.name, part->offset, part->mtd.size); }
若是:
則調用 split_firmware() 來解析。在該函數中作了如下幾件事: