在11-Openwrt配置编译烧录中,我们将编译好的
openwrt-ramips-mt7620-xiaomi-miwifi-mini-squashfs-sysupgrade.bin
烧录进去后,发现系统可以正常启动,但是出现了Kernel panic,Unable to mount root fs on unknown-block(0,0)
,貌似文件系统出现了问题。
查看openwrt的启动信息,找到MTD分区信息的位置,如下:
[ 0.710000] 7 ofpart partitions found on MTD device spi32766.0 [ 0.710000] Creating 7 MTD partitions on "spi32766.0": [ 0.720000] 0x000000000000-0x000000030000 : "u-boot" [ 0.730000] 0x000000030000-0x000000040000 : "u-boot-env" [ 0.730000] 0x000000040000-0x000000050000 : "factory" [ 0.740000] 0x000000050000-0x000000fd0000 : "firmware" [ 0.920000] 0x000000fd0000-0x000000fe0000 : "crash" [ 0.930000] 0x000000fe0000-0x000000ff0000 : "reserved" [ 0.930000] 0x000000ff0000-0x000001000000 : "Bdata" [ 0.940000] gsw: setting port4 to ephy mode [ 0.950000] ralink_soc_eth 10100000.ethernet eth0 (uninitialized): port 1 link up (10Mbps/Half duplex) [ 0.960000] ralink_soc_eth 10100000.ethernet: loaded mt7620 driver [ 0.960000] ralink_soc_eth 10100000.ethernet eth0: ralink at 0xb0100000, irq 5 [ 0.970000] rt2880_wdt 10000120.watchdog: Initialized [ 0.980000] TCP: cubic registered [ 0.980000] NET: Registered protocol family 17 [ 0.990000] bridge: automatic filtering via arp/ip/ip6tables has been deprecated. Update your scripts to load br_netfilter if you need this. [ 1.000000] 8021q: 802.1Q VLAN Support v1.8 [ 1.010000] VFS: Cannot open root device "(null)" or unknown-block(0,0): error -6 [ 1.020000] Please append a correct "root=" boot option; here are the available partitions: [ 1.020000] 1f00 192 mtdblock0 (driver?) [ 1.030000] 1f01 64 mtdblock1 (driver?) [ 1.030000] 1f02 64 mtdblock2 (driver?) [ 1.040000] 1f03 15872 mtdblock3 (driver?) [ 1.040000] 1f04 64 mtdblock4 (driver?) [ 1.050000] 1f05 64 mtdblock5 (driver?) [ 1.060000] 1f06 64 mtdblock6 (driver?) [ 1.060000] Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0) [ 1.060000] ---[ end Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0)
可以看到分成7个partition,对于partition的添加我在Linux mtd system里面已经有说了三种方式,这边使用的是dts添加的方式,该dts文件为target/linux/ramips/dts/XIAOMI-MIWIFI-MINI.dts
,打开dts可以看到有如下内容:
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>; }; 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 0xf80000>; }; partition@fd0000 { label = "crash"; reg = <0xfd0000 0x10000>; }; partition@fe0000 { label = "reserved"; reg = <0xfe0000 0x10000>; read-only; }; partition@ff0000 { label = "Bdata"; reg = <0xff0000 0x10000>; }; };
直接观察这7个分区,好像没看到linux和rootfs,其实这两部分被cat连接成一个.img文件,存放在firmware分区里面,对于openwrt的firmware生成过程后期会专门写一篇文章介绍。
既然分区文件系统什么都有了为什么rootfs没有引导成功呢?再查看下u-boot下个文件所存到的地址是否与kernel下的地址一致呢?
在08-U-boot启动数值具体说明里面有提到U-boot下文件的存储位置,在include/configs/rt2880.h中有如下定义:
#define CFG_BOOTLOADER_SIZE 0x20000 #define CFG_CONFIG_SIZE 0x10000 #define CFG_FACTORY_SIZE 0x10000 #define CFG_ENV_ADDR (CFG_FLASH_BASE + CFG_BOOTLOADER_SIZE) #define CFG_FACTORY_ADDR (CFG_FLASH_BASE + CFG_BOOTLOADER_SIZE + CFG_CONFIG_SIZE) #define CFG_KERN_ADDR (CFG_FLASH_BASE + (CFG_BOOTLOADER_SIZE + CFG_CONFIG_SIZE + CFG_FACTORY_SIZE)) #ifdef DUAL_IMAGE_SUPPORT #define CFG_KERN2_ADDR (CFG_FLASH2_BASE + (CFG_BOOTLOADER_SIZE + CFG_CONFIG_SIZE + CFG_FACTORY_SIZE))
发现u-boot阶段对u-boot的大小定义为0x20000,而kernel那边对u-boot的大小定义却为0x30000,地址已经错位了,我们将u-boot下的CFG_BOOTLOADER_SIZE
改成0x30000后,对u-boot、openwrt进行重新烧录测试。
重新烧录后,可以观察到一切都正常,可以引导文件系统了,启动完按下Enter键进入命令行模式,如下:
BusyBox v1.23.2 (2017-02-21 05:58:37 PST) built-in shell (ash) _______ ________ __ | |.-----.-----.-----.| | | |.----.| |_ | - || _ | -__| || | | || _|| _| |_______|| __|_____|__|__||________||__| |____| |__| W I R E L E S S F R E E D O M ----------------------------------------------------- CHAOS CALMER (Chaos Calmer, r49389) ----------------------------------------------------- * 1 1/2 oz Gin Shake with a glassful * 1/4 oz Triple Sec of broken ice and pour * 3/4 oz Lime Juice unstrained into a goblet. * 1 1/2 oz Orange Juice * 1 tsp. Grenadine Syrup ----------------------------------------------------- root@OpenWrt:/# root@OpenWrt:/# ls bin etc mnt proc root sys usr www dev lib overlay rom sbin tmp var
现在已经可以正常引导了,试着去分析下是为什么会出现内核正常启动,可是文件系统引导不成功呢?
看下新的启动信息中MTD的分区信息有了一些信息,如下:
[ 0.700000] m25p80 spi32766.0: w25q128 (16384 Kbytes) [ 0.710000] 7 ofpart partitions found on MTD device spi32766.0 [ 0.710000] Creating 7 MTD partitions on "spi32766.0": [ 0.720000] 0x000000000000-0x000000030000 : "u-boot" [ 0.730000] 0x000000030000-0x000000040000 : "u-boot-env" [ 0.730000] 0x000000040000-0x000000050000 : "factory" [ 0.740000] 0x000000050000-0x000000fd0000 : "firmware" [ 0.860000] 2 uimage-fw partitions found on MTD device firmware [ 0.870000] 0x000000050000-0x0000001668b5 : "kernel" [ 0.870000] 0x0000001668b5-0x000000fd0000 : "rootfs" [ 0.880000] mtd: device 5 (rootfs) set to be root filesystem [ 0.890000] 1 squashfs-split partitions found on MTD device rootfs [ 0.890000] 0x000000360000-0x000000fd0000 : "rootfs_data" [ 0.900000] 0x000000fd0000-0x000000fe0000 : "crash" [ 0.910000] 0x000000fe0000-0x000000ff0000 : "reserved" [ 0.910000] 0x000000ff0000-0x000001000000 : "Bdata" [ 0.920000] gsw: setting port4 to ephy mode
可以看到中间多了一些信息,上面有说过firmware里面其实包含了kernel和rootfs两个部分,所以其实是有两个partition在里面,内核在调用add_mtd_partitions()
函数里面会调用mtd分离函数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")) { int num = run_parsers_by_type(part, MTD_PARSER_TYPE_ROOTFS); if (num <= 0 && config_enabled(CONFIG_MTD_ROOTFS_SPLIT)) split_rootfs_data(master, part); rootfs_found = 1; } 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); }
该函数会先判断有没有rootfs该partition,如果有直接对该partition进行解析,如果没有那就说明在firmware里面,所以会先调用split_firmware()
函数,简单说明该函数就是将firmware里面的kernel和rootfs分离出来,再对rootfs的partition进行解析。
该函数具体做了以下几件事:
找type为
MTD_PARSER_TYPE_FIRMWARE
的分区解析器来分析。"uimage-fw" 解析器读出 firmware 分区的头部,成功找到一个 uImage。
跃过uImage,紧接着成功找到 squashfs 的头信息,于是找到了格式为 squashfs 的 rootfs。
解析器在找到一个分区后,会调用 __mtd_add_partition() 将此分区添加到系统中。
__mtd_add_partition() 最后又调用 mtd_partition_split(),因为此时 rootfs已经找到,所以会调用 split_rootfs_data() 找 rootfs_data 分区。
rootfs 为 squashfs 分区,该格式的文件系统只读,且头信息里有标记分区大小。所以很容易就可以找到 rootfs_data 的起始位置。
通过上面这么一分析其实我们就大概知道没有引导文件系统的原因了:
1.kernel能够启动那是因为kernel的启动与mtd分区是没有关系的,只跟u-boot的引导地址有关,又因为在u-boot阶段,kernel所存储的位于与启动的引导地址肯定是一致的,所以kernel正常启动。
2.rootfs的启动与mtd分区是有关的,因为后面是通过读取分区块的内容来载入数据的,所以当所存放的地址和所要取的地址错误时,调用split_firmware()
函数没能找出rootfs所处的位置,没能为rootfs建立分区块,所以会提示Unable to mount root fs on unknown-block(0,0)
无法挂载rootfs文件系统。
Openwrt文件系统读取失败问题解决的分析就到这边,有感悟时会持续会更新。