编辑
2025-03-03
记录知识
0

在新的内核中,可以发现在RK平台上出现了打包成fit格式的内核镜像。在盛博的项目中,我们内核提供了标准的安卓打包方式,但是客户SDK通过fit打包,导致我们支持overlayfs的ramdisk.img无法正常打包。其主要原因是fit打包的内核没有把ramdisk打包进来。再者,内核boot更换了打包方式,居然无需修改uboot启动方式即可正常启动,于是抱着一定的兴趣追了一下代码,再参考了一下官方文档,了解了fit镜像的制作,引导过程。这里分享出来

一、 fit image说明

在说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.)。

二、 uboot使能fit

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文件

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启动。

3.1、 默认支持ramdisk

下面支持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";

3.2 编译its

mkimage -E -p 0x800 -f out/boot.its boot.img

通过如上命令即可将its打包到boot镜像内,这样在uboot的boot_fit命令就可以直接找到boot.img内的boot.itb文件

3.3 查看加载信息

./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文件

3.4 直接引导

如果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。下面是参考文档

  1. embendded linux wiki :https://elinux.org/images/f/f4/Elc2013_Fernandes.pdf

  2. uboot doc:https://github.com/u-boot/u-boot/blob/97962260cb93e74e3a8cbbb62e7581830503810a/doc/usage/fit/howto.rst

编辑
2025-03-03
记录知识
0

因为有一些uboot机器的环境变量不一致,导致标准的distro安装不能通用,所以需要在uboot的shell界面设置一下变量,从而使得机器支持uboot正常安装和启动。这里分享如何自定义uboot启动来支持uboot安装镜像

一:查看bootcmd变量

进入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启动方式新增distro启动

对于直接启动的方式,我们需要将其设置为标准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

2.1 解析自定义的distro方式启动

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

上述配置步骤概述如下:

  • 开机默认开启usb控制器
  • 对usb的第一个分区进行查询,判断是否存在/extlinux/extlinux.conf或/boot/extlinux/extlinux.conf目录
  • 通过sysboot加载extlinux.conf文件
  • extlinux.conf文件描述了uImage和uInitrd的位置,这里位置默认在/boot/uImage,/boot/uInitrd,/boot/dtb目录下
  • sysboot会直接启动uImage和uInitrd来运行安装界面
  • 安装完成之后,拔掉u盘,下次启动会遍历usb,scsi,nvme来加载系统,这时因为安装过程会把系统安装到scsi或nvme上,则系统会正常启动

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

四、注意事项

4.1 设备树匹配

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对应的设备树需要和机器硬件功能完全一致

4.2 启动缺陷

4.2.1 仅能安装在第一个设备上

如上述配置可以发现,这里遍历无论是usb,scsi还是nvme,都是选择第一个设备来启动,如果系统存在多个usb,scsi,nvme设备,则需要安装系统到第一个设备上,否则启动不了。

对于此缺陷,可以尝试修复,主要方式为遍历所有的存储设备,而不是指定设备号为0

4.2.2 仅能启动第一个分区

如上述配置可以发现,只能启动0:1分区的内容,也就是boot分区必须要在第一个分区。如果系统存在多个分区,启动分区不在第一个,则系统启动不了

对于此缺陷,可以尝试修复,主要方式为通过part list devtype{devtype} {devnum} -bootable devplist 来遍历所有的分区,然后对每个分区来进行for循环查找extlinux文件

4.2.3 mmc系统优于scsi系统并优于nvme

如上述配置可以发现,usb>mmc>scsi>nvme,usb优先主要用于安装,mmc>scsi>nvme是默认配置的,如果mmc,scsi和nvme上都有系统,则默认启动mmc的系统

对于此缺陷,可以尝试修复,调整bootcmd_kylin的启动顺序即可

编辑
2025-03-03
记录知识
0

在debian package编译包的过程中,有时候需要使用dch来直接提供一个模板编写changlog文件,默认的dch命令填充的是ubuntu和UNRELEASED字段,这些字段对于我们经常需要额外修改。这里为了更方便一点,默认了一下字段,从而使得dch修改changlog更加方便。

一、原始dch样式

在源码目录输入dch,自动填充如下

libdrm (2.4.101-2kylin1rk3ubuntu1) UNRELEASED; urgency=medium * -- root <root@Kylin> Wed, 22 Nov 2023 10:46:31 +0800

这里可以看到,版本号默认添加的是ubuntu1,代号模式是UNRELEASED,提交者是root,提交者邮箱是主机的hosts。这四个地方需要每次额外的修改。

二、期望dch样式

libdrm (2.4.101-2kylin1rk4) v101; urgency=medium * -- tangfeng <tangfeng@kylinos.cn> Wed, 22 Nov 2023 10:50:11 +0800

期望得到的是如有版本后缀则自动填充,如无版本后缀则自动追加kylin1,或可自定义,代号默认为v101。并且提交者和邮箱地址是本人真实地址。

三、修改devscripts的版本

这里修改了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

5.1 命令:dch

直接输入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后缀

5.2 命令:dch -m

-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

5.3 命令:dch -U

-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

5.4 命令:dch -l

-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字段后缀。

编辑
2025-03-03
记录知识
0

有些项目需要做根文件系统只读,也需要保证系统本身是可写的。这里通过使用联合文件系统overlayfs来处理这种需求。

同样的,客户还需要做U盘更新,备份,还原等操作。为了方便处理,这些操作均通过ramdisk里的overlayfs脚本来实现。这里简单讲一下实现步骤

一:功能开启

1.1 内核功能

功能主要为两个地方,一个是支持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

1.2 开机参数

我们知道,系统开机由内核的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

1.3 ramdisk脚本

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参考,上述步骤无需关心

二:启动效果

2.1 挂载情况

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层的内容。

三:追加功能

3.1 自动清理

rm -rf "${root_rw}/${dir_prefix}/*"

开机自动清理即直接删除upper层的所有文件内容

3.2 提交改动

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下删掉

3.3 系统还原

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 目录

3.4 U盘更新

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的功能

3.5 备份系统

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

四:使用

4.1 脚本

使用通过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 #升级内核" }
编辑
2025-03-03
记录知识
0

GPT全称是GUID Partition Table,它是一个分区表,用于描述系统内分区信息情况。通常的,机器在上电启动后,首先加载自己芯片本身的romcode,或者BIOS代码,然后由这些代码直接读取存储器上的gpt或mbr分区信息。在有了分区信息之后,代码就能正常的跳转到存储器的分区地址进行真正的引导(因为romcode会实现对gpt的解析),跳到存储器上运行的第一个程序,也就是bootloader或者grub。然后再由bootloader引导Linux内核再到系统上。

一:GPT的分布

image.png

1.1 LBA0

查看原始数据

root@kylin:~# dd if=/dev/mmcblk1 count=1 2>/dev/zero | hexdump -C 00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 000001c0 00 00 ee 00 00 00 01 00 00 00 ff ff ff ff 00 00 |................| 000001d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 000001f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 aa |..............U.|

GPT的第一个逻辑块叫做保护MBR,用于防止基于 MBR 的引导程序的错误识别。也就是说,一些引导程序还是通过首先检查MBR分区来识别分区类型。

MBR的主要分布如下:

image.png

可以看到下面数据属于第一个分区条目

00 00 00 00 ee 00 00 00 01 00 00 00 ff ff ff ff

关于分区条目如下描述

image.png

由上可以知道,第一个0xee实际上就是分区种类,在《https://en.wikipedia.org/wiki/Partition_type#PID_EEh》可以查到

image.png

也就是说,GPT会被MBR识别程序通过0x1c2地址上的值为0xee识别为GPT的保护MBR

同样,01 00 00 00 是分区中第一个绝对扇区的LBA。ff ff ff ff 是该分区的扇区数目,55 aa 是MBR的最后签名。

1.2 LBA1

查看原始数据

root@kylin:~# dd if=/dev/mmcblk1 count=1 skip=1 2>/dev/zero | hexdump -C 00000000 45 46 49 20 50 41 52 54 00 00 01 00 5c 00 00 00 |EFI PART....\...| 00000010 63 0f bd fd 00 00 00 00 01 00 00 00 00 00 00 00 |c...............| 00000020 ff ef d1 01 00 00 00 00 22 00 00 00 00 00 00 00 |........".......| 00000030 de ef d1 01 00 00 00 00 00 00 47 f9 00 00 53 41 |..........G...SA| 00000040 80 00 24 75 00 00 12 a2 02 00 00 00 00 00 00 00 |..$u............| 00000050 80 00 00 00 80 00 00 00 fa d0 f2 c2 00 00 00 00 |................| 00000060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|

image.png

需要留意:

备份分区地址: ff ef d1 01 00 00 00 00 也就是0x01d1efff,而最后一个可用LBA是 de ef d1 01 00 00 00 00 也就是 0x01d1efde。把这两者相减可得,最后留了34个扇区
root@kylin:~# echo $((0x01d1efff-0x01d1efde+1)) 34

dd可以查看备份LBA的GPT头如下

root@kylin:~# dd if=/dev/mmcblk1 skip=$((0x01d1efff)) count=1 2>/dev/null| hexdump -C 00000000 45 46 49 20 50 41 52 54 00 00 01 00 5c 00 00 00 |EFI PART....\...| 00000010 8d 9a 64 6b 00 00 00 00 ff ef d1 01 00 00 00 00 |..dk............| 00000020 01 00 00 00 00 00 00 00 22 00 00 00 00 00 00 00 |........".......| 00000030 de ef d1 01 00 00 00 00 00 00 47 f9 00 00 53 41 |..........G...SA| 00000040 80 00 24 75 00 00 12 a2 df ef d1 01 00 00 00 00 |..$u............| 00000050 80 00 00 00 80 00 00 00 fa d0 f2 c2 00 00 00 00 |................| 00000060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|

结合GPT分区的分布如下:

image.png

也就是说,最后从-1到-34个扇区,是备份的GPT分区。

磁盘UUID在0x38-0x48。也就是如下数据

00 00 47 f9 00 00 53 41 80 00 24 75 00 00 12 a2

在上层可以通过命令对应起来

root@kylin:~# gdisk /dev/mmcblk1 -l 2>/dev/zero| grep GUID | awk -F : '{print $2}' | sed "s/ //g" F9470000-0000-4153-8000-2475000012A2

1.3 LBA2-33

查看原始数据

root@kylin:~# dd if=/dev/mmcblk1 skip=2 count=32 2>/dev/null | hexdump -C 00000000 00 00 23 6e 00 00 42 49 80 00 37 43 00 00 53 af |..#n..BI..7C..S.| 00000010 00 00 7f 46 00 00 28 4f 80 00 35 3d 00 00 04 d9 |...F..(O..5=....| 00000020 00 80 00 00 00 00 00 00 ff 7f 02 00 00 00 00 00 |................| 00000030 00 00 00 00 00 00 00 00 62 00 6f 00 6f 00 74 00 |........b.o.o.t.| 00000040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00000080 00 00 1c e2 00 00 08 43 80 00 0e 64 00 00 41 73 |.......C...d..As| 00000090 00 00 4e 61 00 00 53 4b 80 00 1d 28 00 00 54 a9 |..Na..SK...(..T.| 000000a0 00 80 03 00 00 00 00 00 de ef d1 01 00 00 00 00 |................| 000000b0 00 00 00 00 00 00 00 00 72 00 6f 00 6f 00 74 00 |........r.o.o.t.| 000000c0 66 00 73 00 00 00 00 00 00 00 00 00 00 00 00 00 |f.s.............| 000000d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|

相信从右边的ascii可以大概猜得出来了,这就是真正的具体分区内容了。其中具体信息解析如下:

image.png

首先的16字节是分区的GUID,这个是用来标识分区类型的ID,是标准分配的。

第二个16字节是分区存在于系统中的GUID,可以与如下命令对应

root@kylin:~# ls -lh /dev/disk/by-partuuid/ | grep mmcblk1p1 | awk '{print $9}' 467f0000-0000-4f28-8000-353d000004d9

接下来16字节是 LBA的开始和结束地址

00 80 00 00 00 00 00 00 ff 7f 02 00 00 00 00 00

这里可以看到起始地址为0x00008000,结束地址为0x00027fff

root@kylin:~# gdisk -l /dev/mmcblk1 2>/dev/null | grep Number -A2 | tail -n 2 1 32768 163839 64.0 MiB FFFF boot 2 229376 30535646 14.5 GiB FFFF rootfs

这里boot分区的起始扇区和结束扇区是对应的。

root@kylin:~# echo $((0x8000)) $((0x00027fff)) 32768 163839

最后从0x38开始的72字节为分区的名字。这里可以直接通过ascii查看到。

image.png

二:Uboot中操作GPT

2.1 修改分区信息

env set partitions "name=boot,size=64M,start=16M;name=rootfs,start=112M,size=-,uuid=614e0000-0000-4b53-8000-1d28000054a9;" gpt write mmc 0 $partitions gpt verify mmc 0 $partitions

设置两个分区,boot分区从16M开始大小为64M,rootfs分区从112M开始,大小为分区的所有空间,uuid为614e0000-0000

修改后,uboot读取gpt分区信息如下。

=> part list mmc 0 Partition Map for MMC device 0 -- Partition Type: EFI Part Start LBA End LBA Name Attributes Type GUID Partition GUID 1 0x00008000 0x00027fff "boot" attrs: 0x0000000000000000 type: ebd0a0a2-b9e5-4433-87c0-68b6b72699c7 guid: da6ed6e6-16f0-415f-95bf-e0721f8721ea 2 0x00038000 0x01d1efde "rootfs" attrs: 0x0000000000000000 type: ebd0a0a2-b9e5-4433-87c0-68b6b72699c7 guid: 614e0000-0000-4b53-8000-1d28000054a9

上述操作对应的RK paramter分区表为如下:

CMDLINE:mtdparts=rk29xxnand:0x0020000@0x00008000(boot),-@0x00038000(rootfs:grow) uuid:rootfs=614e0000-0000-4b53-8000-1d28000054a9

三: Linux中操作GPT

3.1 修改分区名字

cat /proc/cmdline storagemedia=emmc androidboot.storagemedia=emmc androidboot.mode=normal androidboot.verifiedbootstate=orange androidboot.slot_suffix= androidboot.serialno=42036bf58040a13f rw rootwait earlycon=uart8250,mmio32,0xff1a0000 swiotlb=1 console=tty0 console=ttyFIQ0 root=PARTUUID=614e0000-0000 rootfstype=ext4 coherent_pool=1m

可以发现系统启动通过root=PARTUUID=614e0000-0000,所以修改分区名字不会导致系统无法正常启动

gdisk /dev/mmcblk1 Command (? for help): c Partition number (1-2): 2 Enter name: test_rootfs Command (? for help): p Disk /dev/mmcblk1: 30535680 sectors, 14.6 GiB Sector size (logical/physical): 512/512 bytes Disk identifier (GUID): E3550000-0000-443E-8000-27FC000002AA Partition table holds up to 128 entries Main partition table begins at sector 2 and ends at sector 33 First usable sector is 34, last usable sector is 30535646 Partitions will be aligned on 2048-sector boundaries Total free space is 98270 sectors (48.0 MiB) Number Start (sector) End (sector) Size Code Name 1 32768 163839 64.0 MiB FFFF boot 2 229376 30535646 14.5 GiB FFFF test_rootfs Command (? for help): w Final checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING PARTITIONS!! Do you want to proceed? (Y/N): y OK; writing new GUID partition table (GPT) to /dev/mmcblk1. Warning: The kernel is still using the old partition table. The new table will be used at the next reboot or after you run partprobe(8) or kpartx(8) The operation has completed successfully. partprobe /dev/mmcblk1

重启后,查看分区名字信息

root@kylin:/home/kylin# gdisk -l /dev/mmcblk1 GPT fdisk (gdisk) version 1.0.5 Partition table scan: MBR: protective BSD: not present APM: not present GPT: present Found valid GPT with protective MBR; using GPT. Disk /dev/mmcblk1: 30535680 sectors, 14.6 GiB Sector size (logical/physical): 512/512 bytes Disk identifier (GUID): E3550000-0000-443E-8000-27FC000002AA Partition table holds up to 128 entries Main partition table begins at sector 2 and ends at sector 33 First usable sector is 34, last usable sector is 30535646 Partitions will be aligned on 2048-sector boundaries Total free space is 98270 sectors (48.0 MiB) Number Start (sector) End (sector) Size Code Name 1 32768 163839 64.0 MiB FFFF boot 2 229376 30535646 14.5 GiB FFFF test_rootfs

除了可以修改名字之外,实际上还可以修改,分区大小,分区UUID等各种处理。但这些处理并不能保证系统在修改之后仍可以无故障的启动。这里不做演示了。

四:参考链接:

GPT的wiki:https://en.wikipedia.org/wiki/GUID_Partition_Table

Uboot中对gpt的描述:https://github.com/u-boot/u-boot/blob/master/doc/README.gpt

RK平台本身的GPT描述:http://opensource.rock-chips.com/wiki_Partitions

MBR格式介绍:https://wiki.osdev.org/MBR_(x86)