kexec on openwrt - linux boots linux, kernel boots kernel on openwrt

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

  1. 打开内核的kexec
$ make menuconfig
Utilities --> kexec-tools
  1. 加入kexec-tools用户空间工具
$ make menuconfig
Utilities --> kexec-tools
  1. 将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

  1. 修改openwrt配置文件以产生ramdisk
$ make menuconfig
Target Images --> ramdisk
  1. LEDE 17.01上需要修改KERNEL_SIZE,支持生成更大完整的initramfs, AA 12.09是不需要的
$ target/linux/mips/ramips/image/mt7621.mk
  1. 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.elf
kernel.bin.dtb
  1. LEDE170.01上需要补丁bin.dtb,AA12.09不需要
$ ./staging_dir/host/bin/patch-dtb vmlinux-initramfs.elf ubnt-erx-kernel.bin.dtb
  1. 为了能够访问到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 Communications

initramfs_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
}

  1. 压缩elf文件
$ gzip -c vmlinux-initramfs.elf > initramfs.elf.gz

参考文献

  1. kexec on MIPS
  2. kexec manual
  3. kexec with lede
  4. Enhanced kexec patch
  5. Device Tree
  6. kexec ar71xx issue

遇到的问题

  1. 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
...
  1. okos is unstable and gets panic sometimes after hot boot
    原因:一些外围芯片比如usb controller, wmac正在处理中断过程中被hot boot, 造成下次启动中断大量上报,内核stuck。
    解决方案:尽量保证sysloader的简洁,裁剪掉不需要的外设驱动,只保留Flash的访问。
    修改sysloader的.config文件,剔除不需要的模块选择。

  2. Overlayfs not enabled in initramfs
    原因:overlayfs是通过一个底层只读文件系统squashfs加上一个上层可写文件系统JFFS2/UBI构建出来的。
    在initramfs下不能使用squashfs下的不再更新的rootfs.
    解决方案:修改preinit过程,挂载rootfs_data分区

  3. 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 Communications

initramfs_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
}


nicephil@gmail.com – 2017-12-3

posted on 2017-12-03 12:07  nicephil  阅读(1290)  评论(0编辑  收藏  举报

导航