• openwrt spi flash 分区适配过程


    openwrt spi flash 分区适配过程

    这里基于 openwrt mt7620a 平台来跟踪,主要是想理清 dts 里的分区描述是如何一步步转化成内核分区行为。

    先来看看 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 的流程如下:

    1. plat_of_setup() 遍历 palmbus 上的设备,并为每一个动态创建 platform_device,添加到系统总线上 device_add()。对于 spi 这里会创建一个名为 "ralink,rt2880-spi" 的 platfrom_device 并添加到系统中。
    2. drivers/spi/spi-rt2880.c 中会注册 spi 的 platform_driver,与上一步的 platfrom_device match 上了之后,触发调用 rt2880_spi_probe() 。
    3. spi_register_master() 向系统注册 spi 主控制器,并最后调用 of_register_spi_devices(master) 看看 dts 中在 spi 总线上有哪些设备。
    4. 对 dts 中描述的每一个 spi 总线下的设备,为其创建相应的 spi_device,同时根据 dts 中描述的 reg, spi-cpha, spi-cpol, spi-cs-high, spi-3wire, spi-max-frequency 等属性来配置该 spi 设备。对于这里,创建了一个名为 “m25p80” 的 spi_device。
    5. drivers/mtd/device/m25p80.c 中有名为 “m25p80" 的 spi_driver,于是 match 上了。触发执行 m25p_probe()。
    6. m25p_probe() 中读到了这颗 spi flash 的 id 后,确认了一些基本信息(如页大小、块大小), 最后调用 mtd_device_parse_register() 开始真正的分区。

    分区解析器

    part_parser 用来按照某种规则将分区信息解析出来。这些规则可以有很多,内核里调用 register_mtd_parser() 即可注册一个新的解析器。

    drivers/mtd/mtdpart.c 中维护了一个链表 part_parsers,解析器按注册顺序添加到这个链表里。

    parse_mtd_partitions() 中,如果未指定解析器的话,则默认只允许用 cmdlinepart, ofpart 两种解析器。对于我们这里,实际上起作用的是 ofpart。

    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 分区。

    分区的情况可以待系统启动后在 /proc/mtd 文件中查看到。

    # 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。这两个分区是何时添加的呢?

    看看添加 mtd 分区的函数:

    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":
    ", 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);
    }
    

    如果:

    1. rootfs 还没有被找到
    2. 当前分区名是 "firmware"
    3. 内核配置时开启了 CONFIG_MTD_SPLIT_FIRMWARE

    则调用 split_firmware() 来解析。在该函数中做了以下几件事:

    1. 找 type 为 MTD_PARSER_TYPE_FIRMWARE 的分区解析器来分析。
    2. "uimage-fw" 解析器读出 firmware 分区的头部,成功找到一个 uImage。
    3. 跃过 uImage,紧接着成功找到 squashfs 的头信息,于是找到了格式为 squashfs 的 rootfs。
    4. 解析器在找到一个分区后,会调用 __mtd_add_partition() 将此分区添加到系统中。
    5. __mtd_add_partition() 最后又调用 mtd_partition_split(),因为此时 rootfs 已经找到,所以会调用 split_rootfs_data() 找 rootfs_data 分区。
    6. rootfs 为 squashfs 分区,该格式的文件系统只读,且头信息里有标记分区大小。所以很容易就可以找到 rootfs_data 的起始位置。
  • 相关阅读:
    PHP学习九--会话控制session和cookie
    PHP学习八--正则表达式
    PHP学习六--类和对象
    PHP学习五--函数
    PHP学习四--数组
    《30天自制操作系统》day08——鼠标控制与32位模式切换
    java 中 静态块的作用
    学习(一)
    asp中 grideview 更新 无法获取值 解决办法
    ASP.NET中的GridView自带的编辑更新功能
  • 原文地址:https://www.cnblogs.com/sammei/p/4530647.html
Copyright © 2020-2023  润新知