在调试linux时,经常需要翻阅串口的信息,而串口通常需要串口线引出来,而整机一般情况下串口是封闭的。为了调试更好的看的串口信息,可以借助netconsole功能。
CONFIG_NETCONSOLE=y
这里默认对serverip进行ping操作,如果serverip在线,则自动开始netconsole,否则正常流程boot
setenv serverip 172.25.80.123 setenv ipaddr 172.25.80.124 setenv if_netconsole 'ping $serverip' setenv start_netconsole 'setenv ncip $serverip; setenv stdin nc; setenv stdout nc; setenv stderr nc;' run if_netconsole start_netconsole
netconsole命令来自uboot源码tools/netconsole。
./netconsole 172.25.80.124 6666
在某些情况下,如果linux内核破坏,uboot正常。系统无法开机,并且uboot的下载按键无法使用时。可以通过默认的服务器ip地址来对uboot进行命令行调试。例如download命令进入下载模式。
但是在一般情况下,只要保证uboot的下载按键正常时,此方法并无大用。仅留作备选方案之一考虑
CONFIG_NETCONSOLE=m CONFIG_NETCONSOLE_DYNAMIC=y
编译的netconsole.ko,在需要使用的时候,有两种方式来使用
insmod netconsole.ko netconsole=6666@172.25.80.125/eth0,6666@172.25.80.123/00:0c:29:d7:44:0b
这里netconsole参数需要带如下几个信息
netconsole=[+][src-port]@[src-ip]/[<dev>],[tgt-port]@<tgt-ip>/[tgt-macaddr] where + if present, enable extended console support src-port source for UDP packets (defaults to 6665) src-ip source IP to use (interface address) dev network interface (eth0) tgt-port port for logging agent (6666) tgt-ip IP address for logging agent tgt-macaddr ethernet MAC address for logging agent (broadcast)
通俗点也就是
端口地址@本机IP地址/网卡名称,端口地址@服务器IP地址/MAC地址 这里 本地IP为172.25.80.125,网卡名称为eth0,默认使用端口6666,远程IP为172.25.80.123,网卡mac地址为00:0c:29:d7:44:0b,默认使用端口为6666
cd /sys/kernel/config/netconsole/ mkdir target echo 6666 > local_port echo 172.25.80.125 > local_ip echo eth0 > dev_name echo 6666 > remote_port echo 172.25.80.123 > remote_ip echo 00:0c:29:d7:44:0b > remote_mac echo 1 > enabled
卸载
echo 0 > enabled rmdir target rmmod netconsole
默认内核的cmdline没有指定loglevel的情况下,可以通过dmesg手动调试日志
dmesg -n 8
这样在netconsole就能看的内核日志信息了
在设备上,如果不想登录服务器地址,可以通过arp查看mac
ping -c 1 172.25.80.123 ; /sbin/arp -n | grep 172.25.80.123
nc -lup 6666
内核打开netconsole的作用比较明显,内核本身的/var/log/kern.log带缓冲,信息不及时。串口接出来不方便。但是需要实时的查看内核日志的时候,可以使用这个办法
https://github.com/u-boot/u-boot/blob/master/doc/usage/netconsole.rst https://www.kernel.org/doc/html/latest/networking/netconsole.html
在新的内核中,可以发现在RK平台上出现了打包成fit格式的内核镜像。在盛博的项目中,我们内核提供了标准的安卓打包方式,但是客户SDK通过fit打包,导致我们支持overlayfs的ramdisk.img无法正常打包。其主要原因是fit打包的内核没有把ramdisk打包进来。再者,内核boot更换了打包方式,居然无需修改uboot启动方式即可正常启动,于是抱着一定的兴趣追了一下代码,再参考了一下官方文档,了解了fit镜像的制作,引导过程。这里分享出来
在说fit image之前,有必要简单带过一下传统的uboot image引导。通常我们知道uboot 的引导过程是需要知道三个条件的
kernel addr fdt addr ramdisk addr(非必须)
然后再根据booti/bootm启动内核。
所以一般调试linux系统的第一步,就是想方设法将内核,设备树,initrd加载到内存地址上,然后通过bootm A B C 的方式直接加载内存地址,或者通过booti 加载uimage的方式。
这些动作通常要么是uboot内指定写死,要么是手动uboot写好,然后通过saveenv的命令保存来确保下次正常启动。
但是发现了没有,这种方式它不灵活,如果我更换内核,设备树,initrd的话,只能通过同名的文件替换来实现。加载步骤是不能变化的。
如果有一个新的启动方式,它能在嵌入式设备中灵活的选择任意的内核,设备树,initrd是不是更好。
结合上面的需求,uboot开发了新的image格式方式加载(uImage.FIT is the new format used for uImage payloads developed by U-boot.)。
CONFIG_FIT=y CONFIG_CMD_BOOT_FIT=y
上面打开uboot支持fit启动,下一个打开boot_fit命令,实际上如果boot_fit命令不打开也可以手动bootm启动。
对应在bootcmd中,需要添加boot_fit命令,可以安排启动顺序如下:
bootcmd=boot_android ${devtype} ${devnum};boot_fit;bootrkp;run distro_bootcmd;
这样机器默认先找安卓打包方式的内核,再找fit方式的内核。也就解释了为什么内核更换打包方式无需做修改就能直接启动了
its文件即描述多个镜像启动的配置文件,其语法本质就是设备树fdt,所以直接按照设备树语法编写即可
RK平台默认的its文件如下
/* * Copyright (C) 2021 Rockchip Electronics Co., Ltd * * SPDX-License-Identifier: GPL-2.0 */ /dts-v1/; / { description = "FIT image with Linux kernel, FDT blob and resource"; images { fdt { data = /incbin/("fdt"); type = "flat_dt"; arch = ""; compression = "none"; load = <0xffffff00>; hash { algo = "sha256"; }; }; kernel { data = /incbin/("kernel"); type = "kernel"; arch = ""; os = "linux"; compression = ""; entry = <0xffffff01>; load = <0xffffff01>; hash { algo = "sha256"; }; }; resource { data = /incbin/("resource"); type = "multi"; arch = ""; compression = "none"; hash { algo = "sha256"; }; }; ramdisk { description = "Compressed Initramfs"; data = /incbin/("ramdisk"); type = "ramdisk"; arch = "arm64"; os = "linux"; compression = "none"; load = <0x00000000>; entry = <0x00000000>; hash { algo = "sha256"; }; }; }; configurations { default = "conf"; conf { rollback-index = <0x00>; fdt = "fdt"; kernel = "kernel"; multi = "resource"; ramdisk = "ramdisk"; signature { algo = "sha256,rsa2048"; padding = "pss"; key-name-hint = "dev"; sign-images = "fdt", "kernel", "multi"; }; }; }; };
可以留意到的是,这个文件就两个node,一个是images,一个是configurations
其实通过英文就能大概猜到了。images描述了镜像(多个),configurations描述了配置(多个),那么就意味着可以针对多个镜像来搭配出多个配置,每种配置均可通过uboot的boot_fit启动。
下面支持ramdisk打包的its修改方法。这里load和entry填写0是因为uboot会根据实际地址ramdisk_addr_r来复写。所以没有关系
kernel# git diff HEAD boot.its diff --git a/boot.its b/boot.its index 755005c..885bfd1 100644 --- a/boot.its +++ b/boot.its @@ -45,6 +45,21 @@ algo = "sha256"; }; }; + + ramdisk { + description = "Compressed Initramfs"; + data = /incbin/("ramdisk"); + type = "ramdisk"; + arch = "arm64"; + os = "linux"; + compression = "none"; + load = <0x00000000>; + entry = <0x00000000>; + + hash { + algo = "sha256"; + }; + }; }; configurations { @@ -55,6 +70,7 @@ fdt = "fdt"; kernel = "kernel"; multi = "resource"; + ramdisk = "ramdisk"; signature { algo = "sha256,rsa2048";
mkimage -E -p 0x800 -f out/boot.its boot.img
通过如上命令即可将its打包到boot镜像内,这样在uboot的boot_fit命令就可以直接找到boot.img内的boot.itb文件
./mkimage -l boot.img FIT description: FIT image with Linux kernel, FDT blob and resource Created: Thu Nov 2 18:41:56 2023 Image 0 (fdt) Description: unavailable Created: Thu Nov 2 18:41:56 2023 Type: Flat Device Tree Compression: uncompressed Data Size: 151491 Bytes = 147.94 KiB = 0.14 MiB Architecture: AArch64 Load Address: 0xffffff00 Hash algo: sha256 Hash value: cedd1011209678d3b1acbe79ecb93c869f716df3e43049722c5c83b4382bab41 Image 1 (kernel) Description: unavailable Created: Thu Nov 2 18:41:56 2023 Type: Kernel Image Compression: lz4 compressed Data Size: 18758725 Bytes = 18319.07 KiB = 17.89 MiB Architecture: AArch64 OS: Linux Load Address: 0xffffff01 Entry Point: 0xffffff01 Hash algo: sha256 Hash value: e363a997ae83daeb70c130c30b32c1b00655a515d2a5b96ea251884c00655d9c Image 2 (resource) Description: unavailable Created: Thu Nov 2 18:41:56 2023 Type: Multi-File Image Compression: uncompressed Data Size: 793600 Bytes = 775.00 KiB = 0.76 MiB Hash algo: sha256 Hash value: bfb3aaf4cf63f736406e5e3cf54a0924056cf1e16d35eceb1f7ce041777f12b6 Image 3 (ramdisk) Description: Compressed Initramfs Created: Thu Nov 2 18:41:56 2023 Type: RAMDisk Image Compression: uncompressed Data Size: 5188818 Bytes = 5067.21 KiB = 4.95 MiB Architecture: AArch64 OS: Linux Load Address: 0x00000000 Entry Point: 0x00000000 Hash algo: sha256 Hash value: 4cfac4cf0b7fdee76a2b3ef847cedaacdd1ab28b290faff9164fbbef051b6d57 Default Configuration: 'conf' Configuration 0 (conf) Description: unavailable Kernel: kernel Init Ramdisk: ramdisk FDT: fdt
这里可以看到,boot.img内正常包含所以需要its启动的文件和itb文件
如果its想要验证,可在uboot中直接引导,如下方式, 前置条件是需要提前将boot.its描述的文件加载到内存。否则找不到文件的
tftp 0X10000000 boot.its bootm 0X10000000
这里展示fit格式的镜像启动日志,如下
=> boot_fit ## Booting FIT Image at 0xea3eff40 with size 0x01209600 ## Loading kernel from FIT Image at ea3eff40 ... Using 'conf' configuration optee api revision: 2.0 TEEC: Waring: Could not find security partition ## Verified-boot: 0 Trying 'kernel' kernel subimage Description: unavailable Type: Kernel Image Compression: lz4 compressed Data Start: 0xea415740 Data Size: 18758725 Bytes = 17.9 MiB Architecture: AArch64 OS: Linux Load Address: 0x00400000 Entry Point: 0x00400000 Hash algo: sha256 Hash value: e363a997ae83daeb70c130c30b32c1b00655a515d2a5b96ea251884c00655d9c Verifying Hash Integrity ... sha256+ OK ## Loading fdt from FIT Image at ea3eff40 ... Using 'conf' configuration Trying 'fdt' fdt subimage Description: unavailable Type: Flat Device Tree Compression: uncompressed Data Start: 0xea3f0740 Data Size: 151448 Bytes = 147.9 KiB Architecture: AArch64 Load Address: 0x08300000 Hash algo: sha256 Hash value: c49109c3ca4ebe1d68c2da345e1737e92adc5ff0bfc05ab2c20a2184a927b600 Verifying Hash Integrity ... sha256+ OK Loading fdt from 0x08300000 to 0x08300000 Booting using the fdt blob at 0x08300000 Uncompressing LZ4 Kernel Image from 0xea415740 to 0x00400000 ... with 029cea00 bytes OK kernel loaded at 0x00400000, end = 0x02dcea00 Using Device Tree in place at 0000000008300000, end 0000000008327f97 WARNING: could not set reg FDT_ERR_BADOFFSET. ## reserved-memory: drm-logo@00000000: addr=edf00000 size=136000 ramoops@110000: addr=110000 size=f0000 Adding bank: 0x00200000 - 0x08400000 (size: 0x08200000) Adding bank: 0x09400000 - 0xf0000000 (size: 0xe6c00000) Adding bank: 0x1f0000000 - 0x200000000 (size: 0x10000000) Total: 382304.976/382374.272 ms Starting kernel ...
这里默认加载了名字为conf的配置,也就加载了conf配置中指定的文件,至此fit image正常启动。
为何启动fit image如此轻松,主要原因是fit image已经合入主线,linux和uboot都默认支持fit。下面是参考文档
因为有一些uboot机器的环境变量不一致,导致标准的distro安装不能通用,所以需要在uboot的shell界面设置一下变量,从而使得机器支持uboot正常安装和启动。这里分享如何自定义uboot启动来支持uboot安装镜像
进入uboot环境中,需要确定bootcmd变量是否为sysboot加载的extlinux文件,如果是,则无需修改,如果不是,则需要自行配置。
如:
distro_bootcmd=run load_kernel; run load_initrd; run load_fdt; run boot_os boot_os=bootm $kernel_addr -:- $ft_fdt_addr
上述启动方式为直接引导内核地址,initrd地址和fdt地址,然后调用bootm的方式直接启动。故此方式不是标准的distro系统启动方式,需要修改
如:
bootcmd=run distro_bootcmd; distro_bootcmd=for target in ${boot_targets}; do run bootcmd_${target}; done .....;. boot_extlinux=sysboot ${devtype} ${devnum}:${distro_bootpart} any ${scriptaddr} ${prefix}extlinux/extlinux.conf
则此方式为distro系统的uboot启动方式,uboot无需额外修改
对于直接启动的方式,我们需要将其设置为标准distro方式启动。故需要如下设置, 20231027新增支持mmc安装,mmc启动优先级在scsi,nvme之前。
setenv uboot_env "setenv kernel_addr_r 0x90100000;setenv ramdisk_addr_r 0x95000000;setenv fdt_addr_r 0x90000000;" setenv find_and_boot 'if $devtype dev 0; then if fstype $devtype 0:1 bootfstype;then for prefix in / /boot/; do sysboot $devtype 0:1 any 0x90000000 $prefix/extlinux/extlinux.conf; done; fi; fi' setenv usb_boot "usb start; run uboot_env; setenv devtype usb; run find_and_boot" setenv scsi_boot "setenv devtype scsi; run find_and_boot" setenv nvme_boot "setenv devtype nvme; run find_and_boot" setenv mmc_boot "setenv devtype mmc; run find_and_boot" setenv bootcmd_kylin "setenv board e2000q-test-b-ddr4; run uboot_env; run usb_boot; run mmc_boot; run scsi_boot; run nvme_boot" setenv bootcmd "run bootcmd_kylin" saveenv
uboot_env变量: 设置内核加载地址为0x90100000,ramdisk加载地址为0x95000000,设备树加载地址为0x90000000,此三个地址和直接启动方式地址一致 find_and_boot变量: 对每个类型(usb scsi nvme)的设备的第一个设备进行判断,如果其第一个分区是bootfstype(ext4、vfat)则遍历/和/boot目录,寻找其extlinux/extlinux.conf目录,如果存在,则通过sysboot加载extlinux.conf来加载uImage和uInitrd usb_boot变量:U盘启动盘的启动方式,uboot阶段打开usb,设置启动方式为usb后启动 mmc_boot变量:设置启动方式为mmc后启动 scsi_boot变量:设置启动方式为scsi后启动 nvme_boot变量:设置启动方式为nvme后启动 bootcmd_kylin变量:设置板卡为e2000q-test-b-ddr4(这里以32s的板卡为例),配置uboot环境,配置usb启动,配置scsi启动,配置nvme启动 bootcmd变量:修改当前的启动方式为bootcmd_kylin saveenv:保存当前配置信息,以便后续启动生效
通过如上信息可以知道,uboot本身可以直接通过地址引导,也就是如下
ext4load usb 0:1 $kernel_addr boot/uImage-5.4.18-63.52-e2000-rc15-generic; ext4load usb 0:1 $ft_fdt_addr boot/dtb/e2000q-test-b-ddr4.dtb; bootm $kernel_addr -:- $ft_fdt_addr
它通过ext4load的方式直接将内核,设备树加载到对应地址来启动。这也是通常uboot的启动方式。但它不适合常用的发行版系统的安装。
而通过extlinux方式启动能够为常用发行版安装提供良好的方式。
这里配置文件内容如下
default install menu title Installer prompt 0 timeout 10 label install menu label kylinos installer linux /boot/uImage initrd /boot/uInitrd fdtdir /boot/dtb append root=LABEL=kylinos earlycon=pl011,0x2800d000 console=ttyAMA1,115200 rw boot=vkylin uboottype cma=96M security=none audit=1
上述配置步骤概述如下:
starting USB... Bus usb3@31a00000: Register 2000110 NbrPorts 2 Starting the controller USB XHCI 1.10 Bus usb3@31a20000: Register 2000110 NbrPorts 2 Starting the controller USB XHCI 1.10 scanning bus usb3@31a00000 for devices... 2 USB Device(s) found scanning bus usb3@31a20000 for devices... 4 USB Device(s) found scanning usb for storage devices... 1 Storage Device(s) found Device 0: Vendor: Kingston Rev: PMAP Prod: DataTraveler 3.0 Type: Removable Hard Disk Capacity: 14784.0 MB = 14.4 GB (30277632 x 512) ... is now current device Scanning usb 0:1... Found /boot/extlinux/extlinux.conf Retrieving file: /boot/extlinux/extlinux.conf 1: kylinos installer Retrieving file: /boot/uInitrd Retrieving file: /boot/uImage append: root=LABEL=kylinos earlycon=pl011,0x2800d000 console=tty0,115200 rw boot=vkylin uboottype cma=96M security=none audit=1 Retrieving file: /boot/dtb/e2000q-test-b-ddr4.dtb ## Booting kernel from Legacy Image at 90100000 ... Image Name: Linux-5.4.18-63.52-e2000-rc17-ge Image Type: AArch64 Linux Kernel Image (uncompressed) Data Size: 25207296 Bytes = 24 MiB Load Address: 80080000 Entry Point: 80080000 Verifying Checksum ... OK ## Loading init Ramdisk from Legacy Image at 95000000 ... Image Name: uInitrd Image Type: ARM Linux RAMDisk Image (gzip compressed) Data Size: 103216845 Bytes = 98.4 MiB Load Address: 00000000 Entry Point: 00000000 Verifying Checksum ... OK ## Flattened Device Tree blob at 90000000 Booting using the fdt blob at 0x90000000 Loading Kernel Image Loading Ramdisk to f39ca000, end f9c396cd ... OK Loading Device Tree to 00000000f39c1000, end 00000000f39c975a ... OK run in ft_board_setup fdt_addr 00000000f39c1000 N: Phytium System Service Call: 0xc2000005 mb_count = 0x2 mb_blocks[0].mb_size = 0x7c000000 mb_blocks[1].mb_size = 0x80000000 fdt : can not find /memory@01 node fdt : add node memory@01 fdt : dram size 0x100000000 update successfully Starting kernel ...
通过上述可以判断其正常加载了uImage,uInitrd,dtb。
uboot启动需要设备树完全匹配,这里设备树分安装时需要的设备树和启动时需要的设备树。
安装时的设备树需要放在iso的boot/dtb目录下存在和板子要求设备树一致的
板子设备树可以通过如下查询:
print board
这里会提供设备树名字,如e2000q-test-b-ddr4,此时需要在u盘的boot/dtb/e2000q-test-b-ddr4.dtb文件保证存在,否则U盘无法启动
启动时的设备树需要放在系统的/boot/dtb/下,如果系统安装在nvme下,则需要在nvme的下的boot分区内有/dtb/e2000q-test-b-ddr4.dtb。否则安装了也无法启动
记住,这里board对应的设备树需要和机器硬件功能完全一致
如上述配置可以发现,这里遍历无论是usb,scsi还是nvme,都是选择第一个设备来启动,如果系统存在多个usb,scsi,nvme设备,则需要安装系统到第一个设备上,否则启动不了。
对于此缺陷,可以尝试修复,主要方式为遍历所有的存储设备,而不是指定设备号为0
如上述配置可以发现,只能启动0:1分区的内容,也就是boot分区必须要在第一个分区。如果系统存在多个分区,启动分区不在第一个,则系统启动不了
对于此缺陷,可以尝试修复,主要方式为通过part list {devnum} -bootable devplist 来遍历所有的分区,然后对每个分区来进行for循环查找extlinux文件
如上述配置可以发现,usb>mmc>scsi>nvme,usb优先主要用于安装,mmc>scsi>nvme是默认配置的,如果mmc,scsi和nvme上都有系统,则默认启动mmc的系统
对于此缺陷,可以尝试修复,调整bootcmd_kylin的启动顺序即可
在debian package编译包的过程中,有时候需要使用dch来直接提供一个模板编写changlog文件,默认的dch命令填充的是ubuntu和UNRELEASED字段,这些字段对于我们经常需要额外修改。这里为了更方便一点,默认了一下字段,从而使得dch修改changlog更加方便。
在源码目录输入dch,自动填充如下
libdrm (2.4.101-2kylin1rk3ubuntu1) UNRELEASED; urgency=medium * -- root <root@Kylin> Wed, 22 Nov 2023 10:46:31 +0800
这里可以看到,版本号默认添加的是ubuntu1,代号模式是UNRELEASED,提交者是root,提交者邮箱是主机的hosts。这四个地方需要每次额外的修改。
libdrm (2.4.101-2kylin1rk4) v101; urgency=medium * -- tangfeng <tangfeng@kylinos.cn> Wed, 22 Nov 2023 10:50:11 +0800
期望得到的是如有版本后缀则自动填充,如无版本后缀则自动追加kylin1,或可自定义,代号默认为v101。并且提交者和邮箱地址是本人真实地址。
这里修改了devscript来实现上述期望的dch样式,包如下链接
https://dev.kylinos.cn/~rk3588/+archive/kylin-desktop/common/+packages
版本如下
devscripts (2.20.2kylin3rk1) v101; urgency=medium * Make dch easy to use -- tangfeng <tangfeng@kylinos.cn> Tue, 21 Nov 2023 14:14:36 +0800
为了达到期望的changlog样式,这里需要自行设置两个环境变量
export EMAIL=tangfeng@kylinos.cn >> ~/.bashrc export DEBFULLNAME=tangfeng >> ~/.bashrc source ~/.bashrc
如自己的环境则按照自己的条件配置,配置EMAIL和DEBFULLNAME后,dch默认填充的changlog会按照此变量修改
dch有如下四个通常命令,分别介绍如下
dch dch -m dch -U dch -l
直接输入dch会默认填充changlog,如果不加参数,这里默认直接填充kylin1字段,如下
devscripts (2.20.2kylin1) v101; urgency=medium * -- tangfeng <tangfeng@kylinos.cn> Wed, 22 Nov 2023 11:02:39 +0800
这里会在2.20.2版本上追加kylin1版本后缀,比较适合第一次编译给版本加上kylin1后缀
-m会默认复用上次提交的maintainer的信息
devscripts (2.20.2kylin1) v101; urgency=medium * -- Xie Wei <xiewei@kylinos.cn> Wed, 22 Nov 2023 11:07:06 +0800
这里不修改上次提交者(maintainer)的修改细节。上次是xiewei提交的,这里不会再复写成tangfeng
-U会自动对版本后缀追加1
devscripts (2.20.2kylin3rk2) v101; urgency=medium * -- tangfeng <tangfeng@kylinos.cn> Wed, 22 Nov 2023 11:08:25 +0800 devscripts (2.20.2kylin3rk1) v101; urgency=medium
这里会自动把版本追加1,原来是rk1,现在是rk2
-l 后添加字串是新增版本后缀,如果如下
dch -l ft 这里后面追加后缀ft1,如下所示
devscripts (2.20.2kylin3rk1ft1) v101; urgency=medium * -- tangfeng <tangfeng@kylinos.cn> Wed, 22 Nov 2023 11:10:00 +0800
这里从原始版本2.20.2kylin3rk1上追加了ft1字段后缀。
有些项目需要做根文件系统只读,也需要保证系统本身是可写的。这里通过使用联合文件系统overlayfs来处理这种需求。
同样的,客户还需要做U盘更新,备份,还原等操作。为了方便处理,这些操作均通过ramdisk里的overlayfs脚本来实现。这里简单讲一下实现步骤
功能主要为两个地方,一个是支持ramdisk/initrd,一个是支持overlayfs
其次需要开启的为各类挂载系统的功能支持,如ext4,ntfs,exfat,squashfs,以及U盘的识别
如下所示:
CONFIG_BLK_DEV_INITRD CONFIG_OVERLAY_FS CONFIG_EXT4_FS CONFIG_NTFS_FS CONFIG_FAT_FS CONFIG_VFAT_FS CONFIG_SQUASHFS CONFIG_USB_STORAGE CONFIG_SCSI CONFIG_BLK_DEV_SD
我们知道,系统开机由内核的cmdline来体现,为了使能overlayfs,可以显示的将overlayfs的配置体现在cmdline。如下
root=PARTLABEL=rootfs rootfstype=squashfs rw overlayroot=device:dev=PARTLABEL=userdata,fstype=ext4,mkfs=1
可以知道如下信息:
a.root分区默认挂载在PARTLABEL=rootfs,类型为squashfs b.overlayroot分区默认一个设备,为PARTLABEL=userdata,类型为ext4
ramdisk脚本可以通过如下获取
https://gitlab2.kylin.com/shanghai-team/ramdisk/-/blob/ramdisk-reduce/ramdisk/scripts/init-bottom/kylinoverlay
脚本在init-bottom下运行,需要在ORDER下追加
/scripts/init-bottom/kylinoverlay "$@"
这样在系统加载ramdisk/initrd时,kylinoverlay运行会通过mount命令,将原始的root挂载修改为overlay挂载
如直接克隆ramdisk参考,上述步骤无需关心
root@kylin:~# lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT mmcblk0 179:0 0 29.1G 0 disk ├─mmcblk0p3 179:3 0 10.5G 0 part /media/root-ro └─mmcblk0p6 179:6 0 13G 0 part /media/root-rw
其中 /media/root-ro 和 /media/root-rw 是对应的root和userdata分区
root@kylin:~# blkid /dev/mmcblk0p3: TYPE="squashfs" PARTLABEL="rootfs" PARTUUID="614e0000-0000-4b53-8000-1d28000054a9" /dev/mmcblk0p6: PARTLABEL="userdata" UUID="62af73c5-bd8c-4cc0-af0a-10abc9574f21" TYPE="ext4" PARTLABEL="userdata" PARTUUID="2d1d0000-0000-4e74-8000-770200003b4e" overlay的挂载情况 root@kylin:~# mount /dev/mmcblk0p3 on /media/root-ro type squashfs (ro,relatime) /dev/mmcblk0p6 on /media/root-rw type ext4 (rw,relatime) overlayroot on / type overlay (rw,relatime,lowerdir=/media/root-rw/commit:/media/root-ro,upperdir=/media/root-rw/rootfs_overlay,workdir=/media/root-rw/rootfs_overlay-workdir/_)
这里把commit和root-ro作为lower,root-ro是最底层,rootfs_overlay作为upper,rootfs_overlay-workdir作为work
这里需要说明的是:
overlayfs是将多个分区/目录进行联合挂载,对挂载点进行分层,作为lower层的只读,upper层的可写,从而合并成overlayroot目录作为根文件系统,在根文件系统上的读写实际上是通过读写upper层的内容。
rm -rf "${root_rw}/${dir_prefix}/*"
开机自动清理即直接删除upper层的所有文件内容
cp -rpf ${root_rw}/${dir_prefix}/* ${root_rw}/commit/ rm -rf ${root_rw}/${dir_prefix}/*
提交改动即将overlay_root目录的内容,原封不动拷贝到commit下。
值得注意的是,overlay会标记已删除的文件和目录为字符文件,需要针对其做如下处理
先遍历dir_prefix的字符文件,然后跑到commit下删掉 再遍历commit的字符文件,然后跑到dir_prefix下删掉
func_restore(){ rm -rf ${root_rw}/* mkdir -p ${root_rw}/${dir_prefix} mkdir -p $workdir mkdir -p ${root_rw}/commit }
系统还原即删除所有root-rw目录的内容,并重新创建upper层,work层,和lower层的commit 目录
umount ${root_ro} umount ${root_rw} rootfs=`blkid -s PARTLABEL | grep PARTLABEL=\"rootfs\" | awk -F: '{print $1}'` userdata=`blkid -s PARTLABEL | grep PARTLABEL=\"userdata\" | awk -F: '{print $1}'` dd if=/update/filesystem.squashfs of=${rootfs} dd if=/update/ghost.squashfs of=${rootfs} dd if=/update/overlay.ext4 of=${userdata} mount ${rootfs} ${root_ro} mount ${userdata} ${root_rw}
U盘更新即找到U盘后,卸载root-rw和root-ro,将分区进行dd到系统上,然后再重新mount起来
因为直接使用的dd,所以dd时不能断电和拔出U盘,否则系统会变砖,后续可以考虑,系统变砖后,自动恢复rootfs的功能
mksquashfs ${root_ro}/ /ghost/ghost.squashfs commit_size=`/usr/bin/du --block-size=1M -s ${root_rw}/commit/ | awk '{print $1}'` commit_size=$(($commit_size+100)) dd if=/dev/zero of=/ghost/overlay.ext4 bs=512 count=2048 mkfs.ext4 -F /ghost/overlay.ext4 resize2fs /ghost/overlay.ext4 ${commit_size}M inode=`expr ${commit_size} \* 1024 / 16` mkfs.ext4 -F -L userdata -N ${inode} /ghost/overlay.ext4 cp -rpf ${root_rw}/commit/ /tmp umount /tmp sync -f /ghost/overlay.ext4 e2fsck -f /ghost/overlay.ext4 resize2fs /ghost/overlay.ext4 -M
备份系统即打包root分区和打包overlay分区下的commit目录。生成ghost.squashfs和overlay.ext4
使用通过overlaymanager.sh脚本,通过内置到操作系统下即可使用,具体操作情况如下
usage(){ info "Usage: " info " $0 -commit/-c #提交" info " $0 -ghost/-g #一键ghost" info " $0 -restore/-r #系统还原" info " $0 -update/-u #更新系统" info " $0 -y #打开自动清理" info " $0 -n #关闭自动清理" info " $0 -boot/-b #升级内核" }