小米路由系统启动过程
(→最终文件系统结构) |
(→内核启动参数) |
||
第54行: | 第54行: | ||
</source> | </source> | ||
− | 从这可以看到,内核启动时首先挂载的是一个 | + | 从这可以看到,内核启动时首先挂载的是一个 initramfs 内存文件系统,执行的第一个程序是 /init 这个脚本 |
<br><br> | <br><br> |
2014年6月4日 (三) 15:03的最后版本
目录 |
[编辑] 1 Bootloader
Broadcom 出的片子,一直用自己的 Bootloader,叫 CFE,全称 Common Firmware Environment
原来写过一个 CFE Quick Start,在这:http://www.jackslab.org/?portfolio=cfe-quick-start
小米路由系统 0.5.41 的 CFE 启动过程:
CFE version v1.0.4 BSP: 6.37.14.34 (r415984) based on BBP 1.0.37 for BCM947XX (32bit,SP,) Build Date: Wed Apr 30 18:03:21 CST 2014 (szy@shenzhiyong-ct) Copyright (C) 2000-2008 Broadcom Corporation. Init Arena Init Devs. Boot up from NOR flash... Boot partition size = 262144(0x40000) Can't find nandflash! ccrev = 42, chipst= 0 DDR Clock: 800 MHz Info: DDR frequency set from clkfreq=1000,*800* et0: Broadcom BCM47XX 10/100/1000 Mbps Ethernet Controller 6.37.14.34 (r415984) CPU type 0x0: 1000MHz Tot mem: 262144 KBytes CFE mem: 0x00F00000 - 0x0179FE38 (9043512) Data: 0x00F58390 - 0x00F588C8 (1336) BSS: 0x00F588D8 - 0x00F9DE38 (284000) Heap: 0x00F9DE38 - 0x0179DE38 (8388608) Stack: 0x0179DE38 - 0x0179FE38 (8192) Text: 0x00F00000 - 0x00F4CCC4 (314564) Boot: 0x017A0000 - 0x017E0000 Reloc: I:00000000 - D:00000000 Device eth0: hwaddr 8C-BE-BE-20-B7-48, ipaddr 192.168.1.1, mask 255.255.255.0 gateway not set, nameserver not set bootargs: boot -raw -z -addr=0x8000 -max=0xef8000 flash0.os: Loader:raw Filesys:raw Dev:flash0.os File: Options:(null) Loading: ..... 5372256 bytes read Entry at 0x00008000 Closing network. Starting program at 0x00008000
可以看到其启动后,直接加载 flash0.os 分区(/dev/mtdblock2 名为 'os')的 raw 格式的内核文件到地址 0x8000 处,然后直接跳转到这个地址开始执行
[编辑] 2 内核启动参数
# cat /proc/cmdline root=/dev/ram rw console=ttyS0,115200 init=/init libata.force=3.0Gbps earlyprintk debug
从这可以看到,内核启动时首先挂载的是一个 initramfs 内存文件系统,执行的第一个程序是 /init 这个脚本
[编辑] 3 /init 启动脚本
#!/bin/sh # Copyright (C) 2006-2012 OpenWrt.org # # klogger(){ local msg="$@" test -z "$msg" && return 1 echo "$msg" >> /dev/kmsg 2>/dev/null } usb_check_for_rescure(){ local gpiobtn gpiobtn=`cat /proc/xiaoqiang/reset` rst=`gpio $gpiobtn | awk '{print $3}'` if [ "$rst" = "<0>" ]; then nvram set flag_reboot_system=2 exec /sbin/init fi } getmtdblockdevbyname(){ local name name="$1" test -z "$name" && return 1 devnum=$(cat /proc/mtd | awk "/\"$name\"/"| awk -F':' '{print $1}' | awk -F'mtd' '{print $2}') test -z "$devnum" && return 1 if [ -e "/dev/mtdblock${devnum}" ] then echo "/dev/mtdblock${devnum}" return 0 else return 1 fi } reboot() { nvram set flag_reboot_system=1 exec /sbin/init } updone() { nvram set flag_package_update=0 nvram set telnet_en=1 nvram commit klogger "Update successfully!!! Reboot the System..." reboot } upnet() { #not vlan support in bootloader and ramfs, using trunk mode #set to "0 1 2 5u" for ramfs network only, do not commit rmmod et 2>/dev/null nvram set vlan1ports="0 2 5u" nvram set vlan2ports="4 5" mac=`nvram get et0macaddr` if [ "$mac" = "00:90:4C:0F:F2:a7" ]; then uuid=`cat /proc/sys/kernel/random/uuid` b1=`echo $uuid | cut -b 1-2` b2=`echo $uuid | cut -b 3-4` b3=`echo $uuid | cut -b 5-6` mac=`echo 00:90:4C:$b1:$b2:$b3` nvram set "pci/1/1/macaddr=$mac" uuid=`cat /proc/sys/kernel/random/uuid` b1=`echo $uuid | cut -b 1-2` b2=`echo $uuid | cut -b 3-4` b3=`echo $uuid | cut -b 5-6` mac=`echo 00:90:4C:$b1:$b2:$b3` nvram set "pci/2/1/macaddr=$mac" uuid=`cat /proc/sys/kernel/random/uuid` b1=`echo $uuid | cut -b 1-2` b2=`echo $uuid | cut -b 3-4` b3=`echo $uuid | cut -b 5-6` mac=`echo 00:90:4C:$b1:$b2:$b3` nvram set "et0macaddr=$mac" fi insmod /lib/modules/et.ko lip=`nvram get flag_local_ip` if [ "$lip" = "auto" ]; then /sbin/udhcpc -t 10 -n if [ $? -eq 1 ]; then lip="192.168.31.1" ifconfig eth0 $lip up fi else [ -n "$lip" ] || lip="192.168.31.1" ifconfig eth0 $lip up fi echo 1 > /proc/sys/net/ipv6/conf/eth0/forwarding ifconfig lo up nvram set vlan1ports="0 2 5*" nvram set vlan2ports="4 5" } uperror() { klogger "SQUASHFS Filesystem Error!!!" klogger "Switch to RAMFS..." upnet exec /sbin/init } squashfs() { klogger "Switch to SQUASHFS..." reboot rescuerootdev=$(getmtdblockdevbyname squashfs) if [ -n "$rescuerootdev" ] then mount -t squashfs $rescuerootdev /mnt if [ $? -ne 0 ] then klogger "ERROR: mount -t squashfs $rescuerootdev /mnt failed." else # boot_status: 1 sata mode # 2 flash mode echo 2 > /sys/power/boot_status upmount if [ -x /mnt/etc/rescueinit ] then klogger "switch to rescue system($rescuerootdev) by /etc/rescueinit ..." exec switch_root /mnt /etc/rescueinit fi if [ -x /mnt/lib/preinit.sh ] then klogger "switch to rescue system($rescuerootdev) by /lib/preinit.sh ..." exec switch_root /mnt /lib/preinit.sh fi umount -f $rescuerootdev fi fi uperror } upmount() { mount -n -o move /tmp /mnt/tmp mount -n -o move /sys /mnt/sys mount -n -o move /proc /mnt/proc mount -n -o move /dev /mnt/dev } [ -d /dev ] || mkdir -m 0755 /dev [ -d /mnt ] || mkdir -m 0700 /mnt [ -d /sys ] || mkdir /sys [ -d /proc ] || mkdir /proc [ -d /tmp ] || mkdir /tmp mkdir -p /var/lock mount -t sysfs -o nodev,noexec,nosuid none /sys mount -t proc -o nodev,noexec,nosuid none /proc klogger "Loading, please wait..." # Note that this only becomes /dev on the real filesystem if udev's scripts # are used; which they will be, but it's worth pointing out if ! mount -t devtmpfs -o mode=0755 none /dev; then mount -t tmpfs -o mode=0755 none /dev mknod -m 0600 /dev/console c 5 1 mknod /dev/null c 1 3 fi mount -t tmpfs -o "nosuid,size=20%,mode=0755" tmpfs /tmp klogger "Loading essential drivers..." flag_usb_rootdisk_enable="$(nvram get flag_usb_rootdisk_enable)" for i in `cat /lib/modules/mod.lst`; do [ "$i" = "usb-storage.ko" -a "$flag_usb_rootdisk_enable" != 'true' ] && continue if [ "$i" != "et.ko" ] ; then insmod /lib/modules/$i || nvram set flag_package_update=5 fi done # Check for USB rescure klogger "Check for USB rescure..." usb_check_for_rescure model=`nvram get model` if [ -z "$model" ]; then model=`cat /proc/xiaoqiang/model` fi if [ "$model" = "R1AC" ]; then insmod /lib/modules/usb-storage.ko 2>/dev/null sleep 3 fi #support tftpboot from network if [ "$(nvram get flag_tftp_bootup)" = 'on' -a "$(nvram get flag_tftp_booted)" = 'true' ] then . /tftpboot.init tftpboot_mount_rootfs fi klogger "Press Ctrl+C to enter RAMFS..." rd -w 1 && nvram set flag_package_update=5 if [ ! -b /dev/sda ]; then klogger "Warning: No Hard Disk" nvram set flag_no_hdd=1 nvram commit squashfs fi update=`nvram get flag_package_update` ft_mode=`cat /sys/power/ft_mode` if [ -n "$update" -a "$update" != "0" ]; then # klogger "Prepare the system to update..." upnet if [ $update = "5" ]; then exec /sbin/init else /usr/sbin/autofd if [ $? -eq 1 ]; then gpio 2 1 gpio 3 1 gpio 1 0 if [ "$ft_mode" = "1" ]; then exec /sbin/init else while true do /usr/sbin/update retcode=$? [ $retcode -eq 2 ] && exec /sbin/init [ $retcode -eq 0 ] && updone done fi else updone fi fi else klogger "Bringup the system..." flag_try_sys1=`nvram get flag_try_sys1_failed` flag_try_sys2=`nvram get flag_try_sys2_failed` if [ "$flag_try_sys1" != "1" ] || [ "$flag_try_sys2" != "1" ]; then # default: rootfs1 flag=`nvram get flag_boot_rootfs` if [ "$flag" = "1" ]; then mntdev=/dev/sda2 else mntdev=/dev/sda1 fi [ -b $mntdev ] && mount -o ro $mntdev /mnt # flag_boot_type: 1 system in SATA version # 2 system in SQUASH version # 9 system in tftp version nvram set flag_boot_type=1 if [ -x /mnt/lib/preinit.sh ]; then # boot_status: 1 sata mode # 2 flash mode upmount exec switch_root /mnt /lib/preinit.sh else if [ "$ft_mode" = "1" ]; then # skip failed flag setting in FT mode reboot else if [ "$flag" = "1" ]; then nvram set flag_try_sys2_failed=1 else nvram set flag_try_sys1_failed=1 fi nvram set flag_ota_reboot=0 nvram commit reboot fi fi fi # reset all failed flag in FT mode if [ "$ft_mode" = "1" ]; then nvram set flag_try_sys1_failed=0 nvram set flag_try_sys2_failed=0 nvram commit reboot fi squashfs fi
可以看到正常流程下,/dev/sda1 是作为根文件系统,/dev/sda2 作为一个备份的根文件系统。从 "Bringup the system..." 开始,其流程为:
先将 /dev/sda1 挂载到 ramfs 的 /mnt 目录下,然后 switch_root (类 chroot) 到 /mnt 下,同时执行 /mnt/lib/preinit.sh
[编辑] 4 /lib/preinit.sh
#!/bin/sh # Copyright (C) 2006 OpenWrt.org # Copyright (C) 2010 Vertical Communications export PATH=/bin:/sbin:/usr/bin:/usr/sbin pi_ifname= pi_ip=192.168.31.1 pi_broadcast=192.168.31.255 pi_netmask=255.255.255.0 fs_failsafe_ifname= fs_failsafe_ip=192.168.31.1 fs_failsafe_broadcast=192.168.31.255 fs_failsafe_netmask=255.255.255.0 fs_failsafe_wait_timeout=2 pi_suppress_stderr= pi_init_suppress_stderr= pi_init_path="/bin:/sbin:/usr/bin:/usr/sbin" pi_init_cmd="/sbin/init" . /lib/functions.sh . /lib/functions/boot.sh boot_hook_init preinit_essential boot_hook_init preinit_main boot_hook_init failsafe boot_hook_init initramfs boot_hook_init preinit_mount_root for pi_source_file in /lib/preinit/*; do . $pi_source_file done boot_run_hook preinit_essential pi_jffs2_mount_success=false pi_failsafe_net_message=false boot_run_hook preinit_main ulimit -Hn 50000 ulimit -Sn 50000
可以看到,主要的工作脚本都在 /lib/preinit/ 这个目录下的脚本集:
# ls lib/preinit/ 00_extroot.conf 10_indicate_failsafe 30_failsafe_wait 40_run_failsafe_hook 55_determine_extroot_sysupgrade 90_init_console 02_default_set_state 10_indicate_preinit 31_check_for_boottype 41_merge_overlay_hooks 60_init_hotplug 90_mount_bind_etc 03_init_hotplug_failsafe_brcm 15_mount_proc_brcm 31_restore_nvram 42_format_ext_part 60_pivot_usb_root 90_mount_no_jffs2 05_mount_skip 20_check_jffs2_ready 40_init_shm 50_choose_console 70_initramfs_test 99_10_failsafe_login 10_check_for_mtd 20_device_fs_mount 40_mount_devpts 50_determine_usb_root 70_pivot_jffs2_root 99_10_mount_no_mtd 10_essential_fs 30_device_fs_daemons 40_mount_jffs2 50_indicate_regular_preinit 80_mount_root 99_10_run_init
[编辑] 5 最终文件系统结构
文件 lib/preinit/42_format_ext_part 显示:
/dev/sda3 挂载到 /data /dev/sda4 挂载到 /userdisk
文件 lib/preinit/90_mount_bind_etc 显示:
97 mount --bind /data/etc /etc
即:/dev/sda3/etc 是 'mount --bind' 到 /etc 下的
同时当用户在 web 管理界面里格式化磁盘时,实际格式化的是 /dev/sda3 和 /dev/sda4,格式化完了后,其会将 /dev/sda1/etc 同步到 /dev/sda3/etc
最终的目录结构是这样的:
/dev/sda1 on / type ext4 (ro,relatime,barrier=1,data=ordered) /dev/sda3 on /data type ext4 (rw,noatime,barrier=1,data=ordered) /dev/sda4 on /userdisk type ext4 (rw,noatime,barrier=1,data=ordered) /dev/sda3 on /etc type ext4 (rw,noatime,barrier=1,data=ordered)