kexec on openwrt - linux boots linux, kernel boots kernel on openwrt
背景
对于嵌入式设备比如路由器,每次版本升级,总是需要image的升级,所以需要烧写Flash。这种方式速度慢,并且还可能损坏Flash里的引导image,造成设备无法启动,只能返厂维修。
如果能够做一个小系统,出厂时烧录到Flash里,然后它用来查询和下载新版本image到内存,从内存启动这个新的image,那么就不用再烧录Flash,并且可以把image升级放到云端控制。技术分析
可以通过添加功能到bootloader支持这种需求,但是修改原路由器bootloader开发难度比较大,并且将原bootloader替换掉也不容易恢复到原厂image。
Linux里kexec-tools特性,支持linux启动linux,或kernel引导kernel,刚好可以被用到这里。我们可以实现一个小的开启kexec特性的linux系统。然后通过它来下载和引导新的initramfs的全特性的linux系统。
kexec的原理是内核启动时会保留一段内存用来加载和解析新的linux系统,内核提供了kexec的系统调用,来加载制定段到物理内存。
支持kexec特性的openwrt image
- 打开内核的kexec
$ make menuconfigUtilities --> kexec-tools
- 加入kexec-tools用户空间工具
$ make menuconfigUtilities --> kexec-tools
- 将sysupgrade.bin烧录到Flash里
$ sysupgrade -n openwrt-*-sysupgrade.bin
kexec用法
$ kexec -d --command-line="$(cat /proc/cmdlin)" -l vmlinux-initramfs.elf.gz$ kexec -d -e
Note: kexec-tools在MIPS下–type只支持elf-mips, 并且elf必须经过gzip压缩
支持initramfs的openwrt image
- 修改openwrt配置文件以产生ramdisk
$ make menuconfigTarget Images --> ramdisk
- LEDE 17.01上需要修改KERNEL_SIZE,支持生成更大完整的initramfs, AA 12.09是不需要的
$ target/linux/mips/ramips/image/mt7621.mk
- LEDE 17.01上image位置:
llwang@compiler~/repos/master_for_lede-17.01/osdk_repos $ ls build_dir/target-mipsel_24kc_musl-1.1.16/linux-ramips_mt7621/vmlinux-initramfs.elfkernel.bin.dtb
- LEDE170.01上需要补丁bin.dtb,AA12.09不需要
$ ./staging_dir/host/bin/patch-dtb vmlinux-initramfs.elf ubnt-erx-kernel.bin.dtb
- 为了能够访问到Flash里的calibration data,需要修改preinit过程,加载caldata和挂载rootfs_data
llwang@compiler~/repos/master_for_AA-12.09/osdk_repos/package_repos/ok_base-files/lib/preinit $ cat 70_initramfs_test #!/bin/sh# Copyright (C) 2006 OpenWrt.org# Copyright (C) 2010 Vertical Communicationsinitramfs_test() { echo "---> initramfs_test $INITRAMFS" if [ -n "$INITRAMFS" ]; then boot_run_hook initramfs preinit_ip_deconfig # OK_PATCH do_load_ath10k_board_bin mount "$(find_mtd_part rootfs_data)" /overlay -t jffs2 mtd -qq unlock rootfs_data # end of OK_PATCH break fi}
- 压缩elf文件
$ gzip -c vmlinux-initramfs.elf > initramfs.elf.gz
参考文献
遇到的问题
- No valid device tree found, kernel panic on Failed to find mtk,mt7621-sync node 原因: 系统device Tree没有加载正确 openwrt在mips上dtb的处理如下: 在vmlinux的elf里,预留了0x400, 16K的区域用来保存dtb信息,然后使用全局变量__image_dtb来索引这个区域,从而读取dtb新信息。并且在这个区域预存了特定字符"OWRTDTB:",生成image时使用。 在生成vmlinux文件后,查找"OWRTDTB:"关键字,将dtb的bin,写入到这个区域。 解决方案:在生成vmlinux.elf后,将dtb.bin补丁到elf文件里。
...[ 0.000000] No valid device tree found, continuing without //initialize device tree is incorrect[ 0.000000] PERCPU: Embedded 10 pages/cpu @81203000 s8608 r8192 d24160 u40960[ 0.000000] Built 1 zonelists in Zone order, mobility grouping on. Total pages: 65024[ 0.000000] Kernel command line: kexec console=ttyS0,57600 rootfstype=squashfs,jffs2 rootfstype=squashfs,jffs2 //command line should be changed during build...[ 0.000000] Kernel panic - not syncing: Failed to find mtk,mt7621-sysc node[ 0.000000] Rebooting in 1 seconds..[ 0.000000] Reboot failed -- System halted...
-
okos is unstable and gets panic sometimes after hot boot
原因:一些外围芯片比如usb controller, wmac正在处理中断过程中被hot boot, 造成下次启动中断大量上报,内核stuck。 解决方案:尽量保证sysloader的简洁,裁剪掉不需要的外设驱动,只保留Flash的访问。 修改sysloader的.config文件,剔除不需要的模块选择。 -
Overlayfs not enabled in initramfs
原因:overlayfs是通过一个底层只读文件系统squashfs加上一个上层可写文件系统JFFS2/UBI构建出来的。 在initramfs下不能使用squashfs下的不再更新的rootfs. 解决方案:修改preinit过程,挂载rootfs_data分区 -
caldata加载失败
原因: preinit过程中如果发现是initramfs就会跳过加载caldata和flash分区 解决方案:为了能够访问到Flash里的calibration data,和挂载rootfs_data,需要修改preinit过程,加载caldata和挂载rootfs_data
llwang@compiler~/repos/master_for_AA-12.09/osdk_repos/package_repos/ok_base-files/lib/preinit $ cat 70_initramfs_test #!/bin/sh# Copyright (C) 2006 OpenWrt.org# Copyright (C) 2010 Vertical Communicationsinitramfs_test() { echo "---> initramfs_test $INITRAMFS" if [ -n "$INITRAMFS" ]; then boot_run_hook initramfs preinit_ip_deconfig # OK_PATCH do_load_ath10k_board_bin mount "$(find_mtd_part rootfs_data)" /overlay -t jffs2 mtd -qq unlock rootfs_data # end of OK_PATCH break fi}
– 2017-12-3