程序项目代做,有需求私信(vue、React、Java、爬虫、电路板设计、嵌入式linux等)

Rockchip RK3588 - uboot引导方式介绍

----------------------------------------------------------------------------------------------------------------------------

开发板 :NanoPC-T6开发板
eMMC256GB
LPDDR416GB
显示屏 :15.6英寸HDMI接口显示屏
u-boot2017.09
linux6.1
----------------------------------------------------------------------------------------------------------------------------

在介绍完uboot引导方式后,本节主要介绍recovery.img镜像的制作,以及如何实现debian/ubuntu系统的在线升级功能。

一、recovery.img制作

我们首先需要去下载友善官方提供的固件和源码,具体参考《Rockchip RK3588 - 移植uboot 2017.09 & linux 6.1(友善之家脚本方式)》前两章。

本节我们将参考Rockchip Linux SDK尝试自行制作一个recovery.img系统镜像,并将其烧录到NanoPC-T6开发板,用来实现该开发板的OTA升级功能。

recovery.img系统镜像由kernel + dtb + ramdisk三部分组成。

我们在/work/sambashare/rk3588下创建一个recovery目录,用于制作recovery.img

root@ubuntu:/work/sambashare/rk3588$ sudo mkdir recovery

1.1 kernel

1.1.1 支持RAM块设备

如果我们需要使用ramdisk根文件系统,就需要配置RAM块设备驱动,否者不用配置这个。

ram disk顾名思义,内存磁盘。我们平常接触的一些存储介质,如:Nor FlashNand FlasheMMC、ufs、以及机械硬盘固态硬盘等,都是用来存储数据的,同理内存也是可以当成磁盘来存储数据的,唯一不同的就是ram是掉电不保存的,而前面提到的那些存储介质掉电都是保存数据的。

我们都知道,在linux中,上面介绍的Flash这些存储介质,都是需要有对应的驱动,注册成块设备。并向上层提供接口。如NorNand等都抽象成mtdblock块设备。

同理,linux中的ram disk意思就是拿出ram中的一部分大小,用对应的驱动注册层块设备,提供给上层调用。一般的ram disk注册成的块设备,在文件系统中设备节点体现为/dev/ramx

我们需要配置内核支持ramdisk

root@ubuntu:/work/sambashare/rk3588/friendly/sd-fuse_rk3588# cd kernel-rk3588
root@ubuntu:/work/sambashare/rk3588/friendly/sd-fuse_rk3588/kernel-rk3588# sudo  make menuconfig

配置:

General setup  ---> 
    [*] Initial RAM filesystem and RAM disk (initramfs/initrd) support

这里的意思是配置内核初始化时,去寻找initramfsinitrdinitrd就是我们的ramdisk文件系统,至于initramfs,是一个可以编译进内核的根文件映像。

Device Drivers  ---> 
     [*] Block devices  --->
          <*>   RAM block device support
          (1)     Default number of RAM disks
        (131072) Default RAM disk size (kbytes)    

勾选第一项RAM block device support,才会出来第二项和第三项的选项;

  • 第一项的意思是内核支持ram disk块设备驱动;
  • 第二项是注册的块设备数量,内核默认是8,我这里就写了1,内核启动后会有/dev/ram0块设备节点,配置16则会有/dev/ram0~ram15
  • 第三项的意思是ram disk占得最大内存,这里我写的是128M,一般来说可以写的再小点。实际在使用/dev/ramx设备时,并不是一下子就占系统128MB内存,而是根据实际使用情况而分配的。

注意:这里配置的大小要超过我们制作的ramdisk文件系统的大小。

配置完成,会在.config生成如下配置:

CONFIG_BLK_DEV_RAM_COUNT=1
CONFIG_BLK_DEV_RAM_SIZE=131072

./build-kernel.sh构建内核的时候要想使得该配置生效,我们必须修改./arch/arm64/configs/nanopi6_linux_defconfig文件中的配置。

这一步我们一定要配置,不然内核挂载ramsidk文件系统可能出现如下错误:

[    6.558702] RAMDISK: ext2 filesystem found at block 0
[    6.558709] RAMDISK: image too big! (74136KiB/4096KiB)
[    6.559257] /dev/root: Can't open blockdev
[    6.559266] VFS: Cannot open root device "(null)" or unknown-block(0,0): error -6
[    6.559271] Please append a correct "root=" boot option; here are the available partitions:
[    6.559279] 0100            4096 ram0
[    6.559283]  (driver?)
[    6.559293] 0101            4096 ram1
[    6.559296]  (driver?)
[    6.559304] 0102            4096 ram2
[    6.559306]  (driver?)
[    6.559315] 0103            4096 ram3
[    6.559316]  (driver?)
[    6.559324] 0104            4096 ram4
[    6.559326]  (driver?)
[    6.559335] 0105            4096 ram5
[    6.559336]  (driver?)
[    6.559344] 0106            4096 ram6
[    6.559346]  (driver?)
[    6.559353] 0107            4096 ram7
[    6.559355]  (driver?)
[    6.559370] Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0)
1.1.2 编译内核

内核编译执行如下命令:

root@ubuntu:/work/sambashare/rk3588/friendly/sd-fuse_rk3588# sudo KERNEL_SRC=$PWD/kernel-rk3588 ./build-kernel.sh debian-bullseye-desktop-arm64

注意:编译之前需要安装交叉编译工具以及配置build-kernel.sh脚本,具体参考《Rockchip RK3588 - 移植uboot 2017.09 & linux 6.1(友善之家脚本方式)》。

1.1.3 Image.gz

编译完成之后,内核镜像我们采用Image文件即可,位于kernel-rk3588/arch/arm64/boot/目录;

root@ubuntu:/work/sambashare/rk3588/friendly/sd-fuse_rk3588$ ll kernel-rk3588/arch/arm64/boot/Image
-rw-r--r-- 1 root root 35420168  7月 20 17:51 kernel-rk3588/arch/arm64/boot/Image

Image是内核镜像原生二进制文件,可以直接在芯片上运行的,由于Image文件较大,我们将Image压缩转换为Image.gz

root@ubuntu:/work/sambashare/rk3588/friendly/sd-fuse_rk3588$ sudo bash -c 'cat kernel-rk3588/arch/arm64/boot/Image | gzip -n -f -9 > kernel-rk3588/arch/arm64/boot/Image.gz'
root@ubuntu:/work/sambashare/rk3588/friendly/sd-fuse_rk3588$ ll kernel-rk3588/arch/arm64/boot/Image.gz
-rw-r--r-- 1 root root 12742364  7月 20 19:52 kernel-rk3588/arch/arm64/boot/Image.gz

拷贝文件到recovery目录:

root@ubuntu:/work/sambashare/rk3588/friendly/sd-fuse_rk3588$ sudo cp ./kernel-rk3588/arch/arm64/boot/Image.gz /work/sambashare/rk3588/recovery/

1.2 dtb

NanoPC-T6开发板默认使用的dtbrk3588-nanopi6-rev01.dtb

root@ubuntu:/work/sambashare/rk3588/friendly/sd-fuse_rk3588$ ll kernel-rk3588/arch/arm64/boot/dts/rockchip/rk3588-nanopi6-rev01.dtb
-rw-r--r-- 1 root root 270114 12月 27  2023 kernel-rk3588/arch/arm64/boot/dts/rockchip/rk3588-nanopi6-rev01.dtb

拷贝文件到recovery目录:

root@ubuntu:/work/sambashare/rk3588$ sudo cp friendly/sd-fuse_rk3588/kernel-rk3588/arch/arm64/boot/dts/rockchip/rk3588-nanopi6-rev01.dtb ./recovery/

1.3 ramdisk

关于ramdisk文件系统的制作我们参考《Rockchip RK3399 - busybox 1.36.0制作根文件系统》,不过在制作过程中有以下几处需要调整。

1.3.1 交叉编译编译工具链

我们在recovery目录下制作ramdisk文件系统,需要注意的是交叉编译工具链的选择,这里我们使用《Rockchip RK3588 - 移植uboot 2017.09 & linux 6.1(友善之家脚本方式)》文章中安装的gcc-10-aarch64-linux-gnu,即和编译内核使用的交叉编译工具链一致。

root@ubuntu:/work/sambashare/rk3588/recovery$ aarch64-linux-gnu-gcc -v
Using built-in specs.
COLLECT_GCC=aarch64-linux-gnu-gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc-cross/aarch64-linux-gnu/10/lto-wrapper
Target: aarch64-linux-gnu
......
Thread model: posix
Supported LTO compression algorithms: zlib zstd
gcc version 10.5.0 (Ubuntu 10.5.0-1ubuntu1~22.04)

以下是我所使用 gcc-aarch64-linux-gnug++-aarch64-linux-gnu 的默认安装路径:

库文件:

  • 标准C库文件:/usr/aarch64-linux-gnu/lib
  • 标准C++库文件:/usr/aarch64-linux-gnu/lib
  • 其他第三方库文件:/usr/aarch64-linux-gnu/lib/usr/aarch64-linux-gnu/lib/<library_name>

头文件:

  • C头文件:/usr/aarch64-linux-gnu/include
  • C++头文件:/usr/aarch64-linux-gnu/include/c++/<version>
1.3.2 编译安装

首先我们需要下载busybox-1.36.0.tar.bz2

root@ubuntu:/work/sambashare/rk3588/recovery/# wget https://busybox.net/downloads/busybox-1.36.0.tar.bz2
tar
root@ubuntu:/work/sambashare/rk3588/recovery/# sudo tar -jxf busybox-1.36.0.tar.bz2

按照《Rockchip RK3399 - busybox 1.36.0制作根文件系统》制作根文件系统有以下几处需要调整。

1.3.2.1 配置

make menuconfig配置时做如下配置参数:

root@ubuntu:/work/sambashare/rk3588/recovery# cd busybox-1.36.0/
root@ubuntu:/work/sambashare/rk3588/recovery/busybox-1.36.0# sudo make defconfig   
root@ubuntu:/work/sambashare/rk3588/recovery/busybox-1.36.0# sudo make menuconfig
    
Settings --->
    (aarch64-linux-gnu-) Cross compile prefix
    ( -march=armv8-a) additional CFLAGS 
    ( ) Path to sysroot                          # 根路径未指定
    [ ] Build static binary (no shared libs)
    [*] Build Shared libbusybox 
    (./_install) Destination path for 'make install' 
    
Networking Utilities  --->
 	[*] Support /etc/networks
	[*] udhcpc (24 kb)
	[*]   Verify that the offered address is free, using ARP ping
	[*]   Do not pass malformed host and domain names
    (/usr/share/udhcpc/default.script) Absolute path to config script
	(/usr/share/udhcpc/default6.script) Absolute path to config script for IPv6
1.3.2.2 编译安装

运行命令:

root@ubuntu:/work/sambashare/rk3588/recovery/busybox-1.36.0# sudo make

执行make install

root@ubuntu:/work/sambashare/rk3588/recovery/busybox-1.36.0# sudo make install
1.3.3 构建根文件系统

我们首先需要按照《Rockchip RK3399 - busybox 1.36.0制作根文件系统》文章教程配置:

  • 构建etc目录;
  • 修改/etc/inittab文件;
  • 修改/etc/fstab文件;
  • 修改/etc/profile文件;
  • 构建dev目录;
  • 构建其他文件;

除此之外,还需要配置以下内容。

1.3.3.1 库文件

在构建根文件系统拷贝动态库需要执行;

root@ubuntu:/work/sambashare/rk3588/recovery/busybox_install$ sudo cp -a /usr/aarch64-linux-gnu/lib/* ./lib/
1.3.3.2 S40network

需要创建/etc/init.d/S40network文件:

root@ubuntu:/work/sambashare/rk3588/recovery/busybox_install$ sudo vim etc/init.d/S40network

内容如下:

#!/bin/sh
#
# Start the network....
#

# Debian ifupdown needs the /run/network lock directory
mkdir -p /run/network

case "$1" in
  start)
        printf "Starting network: "
        /sbin/ifup -a
        [ $? = 0 ] && echo "OK" || echo "FAIL"
        ;;
  stop)
        printf "Stopping network: "
        /sbin/ifdown -a
        [ $? = 0 ] && echo "OK" || echo "FAIL"
        ;;
  restart|reload)
        "$0" stop
        "$0" start
        ;;
  *)
        echo "Usage: $0 {start|stop|restart}"
        exit 1
esac

exit $?
1.3.2.3 default.script

拷贝文件simple.script到根文件系统的/usr/share/udhcpc/目录下,更名为default.script

root@ubuntu:/work/sambashare/rk3588/recovery/busybox_install$ sudo mkdir -p  ./usr/share/udhcpc
root@ubuntu:/work/sambashare/rk3588/recovery/busybox_install$ sudo cp ../busybox-1.36.0/examples/udhcp/simple.script ./usr/share/udhcpc
root@ubuntu:/work/sambashare/rk3588/recovery/busybox_install$ sudo mv ./usr/share/udhcpc/simple.script ./usr/share/udhcpc/default.script

再将default.scriptRESOLV_CONF=”/etc/resolv.conf”更改为RESOLV_CONF=”/tmp/resolv.conf”

root@ubuntu:/work/sambashare/rk3588/recovery/busybox_install$ sudo vim ./usr/share/udhcpc/default.script
RESOLV_CONF="/tmp/resolv.conf"
......

我们配置一下网络DHCP,这样系统启动以后就会自动设置好网络;

root@ubuntu:/work/sambashare/rk3588/recovery/busybox_install$ sudo mkdir -p etc/network/interfaces.d
root@ubuntu:/work/sambashare/rk3588/recovery/busybox_install$ sudo vim etc/network/interfaces
# interface file auto-generated by buildroot

auto lo
iface lo inet loopback

auto eth0
iface eth0 inet dhcp
1.3.2.4 rcS

修改/etc/init.d/rcS文件;

root@ubuntu:/work/sambashare/rk3588/recovery/busybox_install$ sudo vim etc/init.d/rcS

内容如下:

#!/bin/sh

# 挂载 /etc/fstab 中定义的所有文件系统
/bin/mount -a

# 挂载虚拟的devpts文件系统用于用于伪终端设备
/bin/mkdir -p /dev/pts
/bin/mount -t devpts devpts /dev/pts

# 扫描并创建节点
/sbin/mdev -s

# 加载网卡驱动
/sbin/modprobe r8125

# 配置网络
/sbin/udhcpc

# Start all init scripts in /etc/init.d
# executing them in numerical order.
#
for i in /etc/init.d/S??* ;do

     # Ignore dangling symlinks (if any).
     [ ! -f "$i" ] && continue

     case "$i" in
        *.sh)
            # Source shell script for speed.
            (
                trap - INT QUIT TSTP
                set start
                . $i
            )
            ;;
        *)
            # No sh extension, so fork subprocess.
            $i start
            ;;
    esac
done

整个udhcpc的框架是可执行文件/sbin/udhcpc、脚本文件/usr/share/udhcpc/default.scriptDNS配置文件/tmp/resolv.conf。在rcS中启动udhcpc,默认使用default.script脚本已达到自动配置iproute等。

1.3.4 安装网卡驱动

接着我们需要拷贝debian-bullseye-desktop系统重网卡相关驱动和固件到busybox文件系统;

root@ubuntu:/work/sambashare/rk3588/recovery$ sudo mkdir lib

在宿主机执行如下命令:

root@ubuntu:/work/sambashare/rk3588/recovery$ sudo scp -r pi@192.168.0.100:/lib/modules ./lib
root@ubuntu:/work/sambashare/rk3588/recovery$ sudo scp -r pi@192.168.0.100:/lib/firmware ./lib

查看文件:

root@ubuntu:/work/sambashare/rk3588/recovery$ ls lib/modules/6.1.25/
cryptodev.ko               modules.builtin.bin      modules.softdep      rtl8821CU.ko   rtw_8723du.ko  rtw_8822bs.ko  rtw_sdio.ko
kernel                     modules.builtin.modinfo  modules.symbols      rtl8822bu.ko   rtw_8821ce.ko  rtw_8822ce.ko  rtw_usb.ko
modules.alias              modules.dep              modules.symbols.bin  rtl8822cs.ko   rtw_8821c.ko   rtw_8822c.ko
modules.alias.bin          modules.dep.bin          nft_fullcone.ko      rtw_8723de.ko  rtw_8821cs.ko  rtw_8822cs.ko
modules.builtin            modules.devname          r8125.ko             rtw_8723d.ko   rtw_8822be.ko  rtw_core.ko
modules.builtin.alias.bin  modules.order            rtl8812au.ko         rtw_8723ds.ko  rtw_8822b.ko   rtw_pci.ko
zhengyang@ubuntu:/work/sambashare/rk3588/recovery$ ls lib/firmware/
brcm   iwlwifi-cc-a0-59.ucode  mediatek    mt7662_rom_patch.bin  regulatory.db.p7s  rtl_bt   rtw88
intel  mali_csffw.bin          mt7662.bin  regulatory.db         rockchip           rtl_nic
1.3.5 目录结构

最终构建的根文件系统目录结构如下;

root@ubuntu:/work/sambashare/rk3588/recovery/busybox_install$ ll
drwxr-xr-x 13 root root 4096  7月 21 11:34 ./
drwxr-xr-x  8 root root 4096  7月 21 11:35 ../
drwxr-xr-x  2 root root 4096  7月 21 10:49 bin/
crw-r--r--  1 root root 5, 1  7月 21 11:34 console
drwxr-xr-x  2 root root 4096  7月 21 11:34 dev/
drwxr-xr-x  3 root root 4096  7月 21 11:34 etc/
drwxr-xr-x  2 root root 4096  7月 21 11:34 lib/
-rwxr-xr-x  1 root root 5936  7月 21 10:26 linuxrc*
drwxr-xr-x  2 root root 4096  7月 21 11:34 mnt/
crw-r--r--  1 root root 1, 3  7月 21 11:34 null
drwxr-xr-x  2 root root 4096  7月 21 11:34 proc/
drwxr-xr-x  2 root root 4096  7月 21 11:34 root/
drwxr-xr-x  2 root root 4096  7月 21 10:49 sbin/
drwxr-xr-x  2 root root 4096  7月 21 11:34 sys/
drwxr-xr-x  2 root root 4096  7月 21 11:34 tmp/
crw-r--r--  1 root root 4, 1  7月 21 11:34 tty1
1.3.6 mk-ramdisk.sh

我们将构建根文件系统和ramdisk镜像的过程,通过mk-ramdisk.sh脚本实现;

#!/bin/bash

[[ -n DEBUG ]] && set -x

err_handler()
{
       # 如果参数1为空,则获取最近一次执行命令的退出状态
        ret=${1:-$?}
        # 退出状态为0,直接返回
        [ "$ret" -eq 0 ] && return

        # 入参参数2为空,输出调用栈中的第二个函数名
        echo "ERROR: Running $0 - ${2:-${FUNCNAME[1]}} failed!"
        # 输出错误状态码,以及发生错误的行号
        echo "ERROR: exit code $ret from line ${BASH_LINENO[0]}:"
        # 如果参数3为空,输出当前正在执行的命令
        echo "    ${3:-$BASH_COMMAND}"
        echo "ERROR: call stack:"
        for i in $(seq 1 $((${#FUNCNAME[@]} - 1))); do
                SOURCE="${BASH_SOURCE[$i]}"
                LINE=${BASH_LINENO[$(( $i - 1 ))]}
                echo "    $(basename "$SOURCE"): ${FUNCNAME[$i]}($LINE)"
        done
        exit $ret
}
trap 'err_handler' ERR
set -eE

# 获取当前工作路径
WORK_DIR=$(dirname "$(realpath "$BASH_SOURCE")")

# busybox编译安装目录
BUSYBOX_SOURCR_DIR="$WORK_DIR/busybox-1.36.0"

# 保存busybox根文件系统目录
BUSYBOX_ROOTFS="$WORK_DIR/busybox_install"

# 判断当前目录下是否存在安装目录_install
if [[ ! -d "$BUSYBOX_SOURCR_DIR/_install" ]];then
        echo "请先编译安装Busybox"
        exit 1
fi

# 删除目录
rm -rf $BUSYBOX_ROOTFS

# 创建目录
echo "开始创建目录${BUSYBOX_ROOTS}"
mkdir $BUSYBOX_ROOTFS
cd $BUSYBOX_ROOTFS
cp -a $BUSYBOX_SOURCR_DIR/_install/* ./

# 找到交叉编译工具里的动态库复制到lib目录下
echo "开始拷贝lib文件"
cp -a /usr/aarch64-linux-gnu/lib/* ./lib
if [[ -d "${WORD_DIR}/lib" ]];then
		# 拷贝所有模块,不拷贝固件         =》异常
		# 只拷贝网卡模块,不拷贝固件       =》 正常启动
		# 只拷贝网卡模块,拷贝固件         =》 异常
		mkdir -p ./lib/modules/6.1.25
        cp -a $WORK_DIR/lib/modules/6.1.25/*.ko ./lib/modules/6.1.25
        # cp -a $WORK_DIR/lib/firmware ./lib
fi

# 1. 构建etc目录
echo "开始创建etc目录"
mkdir etc
mkdir -p etc/init.d/

echo "开始创建/etc/inittab文件"
# 1.1 构建/etc/inittab
cat>etc/inittab<<EOF
# 系统启动时
::sysinit:/etc/init.d/rcS

# 系统启动按下Enter键时
::askfirst:-/bin/sh

# 按下Ctrl+Alt+Del键时
::ctrlaltdel:/sbin/reboot

# 系统关机时
::shutdown:/sbin/swapoff -a
::shutdown:/bin/umount -a -r

# 系统重启时
::restart:/sbin/init
EOF
chmod 755 etc/inittab

echo "开始创建/etc/init.d/rcS文件"
# 1.2 构建/etc/init.d/rcS
cat>etc/init.d/rcS<<EOF
#!/bin/sh

# 挂载 /etc/fstab 中定义的所有文件系统
/bin/mount -a

# 挂载虚拟的devpts文件系统用于用于伪终端设备
/bin/mkdir -p /dev/pts
/bin/mount -t devpts devpts /dev/pts

# 扫描并创建节点
/sbin/mdev -s

# 加载网卡驱动
/sbin/modprobe r8125 

# 配置网络
/sbin/udhcpc

# Start all init scripts in /etc/init.d
# executing them in numerical order.
#
for i in /etc/init.d/S??* ;do

     # Ignore dangling symlinks (if any).
     [ ! -f "\$i" ] && continue

     case "\$i" in
        *.sh)
            # Source shell script for speed.
            (
                trap - INT QUIT TSTP
                set start
                . \$i
            )
            ;;
        *)
            # No sh extension, so fork subprocess.
            \$i start
            ;;
    esac
done
EOF

echo "开始创建/etc/fstab文件"
# 1.3 构建/etc/fstab
cat>etc/fstab<<EOF
# <file system>    <mount point>    <type>    <options>    <dump>    <pass>
proc                  /proc          proc     defaults       0         0
sysfs                 /sys           sysfs    defaults       0         0
tmpfs                 /tmp           tmpfs    defaults       0         0
tmpfs                 /dev           tmpfs    defaults       0         0
EOF

echo "开始创建/etc/profile文件"
# 1.4 构建/etc/profile
cat>etc/profile<<EOF
# 主机名
export HOSTNAME=NanoPC-T6

# 用户名
export USER=root

# 用户目录
export HOME=/root

# 终端默认提示符
export PS1="[\$USER@\$HOSTNAME:\\\$PWD]\\# "

# 环境变量
export PATH=/bin:/sbin:/usr/bin:/usr/sbin

# 动态库路径
export LD_LIBRARY_PATH=/lib:/usr/lib:\$LD_LIBRARY_PATH
EOF

echo "开始创建dev目录"
#1.5 构建dev目录
mkdir dev
mknod console c 5 1
mknod null c 1 3
mknod tty1 c 4 1

echo "开始创建/etc/init.d/S40network文件"
#1.6 构建/etc/init.d/S40network
cat>etc/init.d/S40network<<EOF
#!/bin/sh
#
# Start the network....
#

# Debian ifupdown needs the /run/network lock directory
mkdir -p /run/network

case "\$1" in
  start)
        printf "Starting network: "
        /sbin/ifup -a
        [ \$? = 0 ] && echo "OK" || echo "FAIL"
        ;;
  stop)
        printf "Stopping network: "
        /sbin/ifdown -a
        [ \$? = 0 ] && echo "OK" || echo "FAIL"
        ;;
  restart|reload)
        "\$0" stop
        "\$0" start
        ;;
  *)
        echo "Usage: \$0 {start|stop|restart}"
        exit 1
esac

exit $?
EOF

echo "开始创建/usr/share/udhcpc/default.script文件"
# 1.7 构建/usr/share/udhcpc/default.script
mkdir -p  ./usr/share/udhcpc
cp $BUSYBOX_SOURCR_DIR/examples/udhcp/simple.script ./usr/share/udhcpc
mv ./usr/share/udhcpc/simple.script ./usr/share/udhcpc/default.script
sed -i  's/\/etc\/resolv\.conf/\/tmp\/resolv\.conf/g'  ./usr/share/udhcpc/default.script

mkdir -p etc/network/interfaces.d
cat>etc/network/interfaces<<EOF
# interface file auto-generated by buildroot

auto lo
iface lo inet loopback

auto eth0
iface eth0 inet dhcp
EOF

# 修改权限
chmod -R 777 etc/init.d/*

# 构建其他文件
mkdir mnt proc tmp sys root

# 构建ramdisk镜像
echo "开始构建ramdisk镜像文件"
cd ..
mkdir busybox_rootfs
dd if=/dev/zero of=busybox_ext4_rootfs.img bs=1M count=300
mkfs.ext4 busybox_ext4_rootfs.img

mount busybox_ext4_rootfs.img busybox_rootfs
cp ${BUSYBOX_ROOTFS}/* ./busybox_rootfs/ -af
umount busybox_rootfs

rm -rf busybox_rootfs

e2fsck -p -f busybox_ext4_rootfs.img
resize2fs -M busybox_ext4_rootfs.img

gzip -v9 busybox_ext4_rootfs.img
mv busybox_ext4_rootfs.img.gz ramdisk.gz

编译安装busybox之后,执行如下命令;

root@ubuntu:/work/sambashare/rk3588/recovery$ sudo  ./mk-ramdisk.sh

1.4 构建recovery.img

1.4.1 boot4recovery.its

由于recovery.imgFIT uImage镜像格式,因此我们首先需要创建its文件。

这里我们直接将Rockchip Linux SDK中的boot4recovery.its文件拷贝过来;

root@ubuntu:/work/sambashare/rk3588/recovery$ sudo cp ../armsom/armsom-rk3588-bsp/device/rockchip/.chip/boot4recovery.its .

需要对文件进行适当修改,最终内容如下:

点击查看代码
/*
 * Copyright (C) 2021 Rockchip Electronics Co., Ltd
 *
 * SPDX-License-Identifier: GPL-2.0
 */

/dts-v1/;
/ {
    description = "U-Boot FIT source file for arm";

    images {
        fdt {
			description = "Flattened Device Tree blob";
            data = /incbin/("@KERNEL_DTB@");
            type = "flat_dt";
            arch = "arm64";
            compression = "none";
            load = <0xffffff00>;

            hash {
                algo = "sha256";
            };
        };

        kernel {
			description = "Vanilla Linux kernel";
            data = /incbin/("@KERNEL_IMG@");
            type = "kernel";
            arch = "arm64";
            os = "linux";
            compression = "gzip";
            entry = <0xffffff01>;
            load = <0xffffff01>;

            hash {
                algo = "sha256";
            };
        };

		ramdisk {
				description = "Ramdisk for project-x";
				data = /incbin/("@RAMDISK_IMG@");
				type = "ramdisk";
				arch = "arm64";
				os = "linux";
				compression = "gzip";
				load = <0xffffff02>;

				hash {
						algo = "sha256";
				};
		};
    };

    configurations {
        default = "conf";

        conf {
            rollback-index = <0x00>;
			description = "Boot Linux kernel with FDT blob";
            fdt = "fdt";
            kernel = "kernel";
            ramdisk = "ramdisk";

            signature {
                algo = "sha256,rsa2048";
                padding = "pss";
                key-name-hint = "dev";
                sign-images = "fdt", "kernel", "ramdisk";
            };
        };
    };
};

由于在该文件中我们设置的loadentry地址满足(addr & 0xffffff00) == 0xffffff00,因此在its文件中我们为每个节点指定的loadentry都会在boot_fit启动的时候将其替换成环境变量中指定的地址。其中:

  • kernel load/entry地址会被替换成0x00400000,该值来自环境变量kernel_addr_r
  • ramdisk load地址会被替换成0x0a200000,该值来自环境变量ramdisk_addr_r
  • fdtload地址会被替换成0x08300000,该值来自环境变量fdt_addr_r

具体可以参考《Rockchip RK3399 - 移植linux 5.2.8boot_fit启动方式介绍。

1.4.2 mk-fitimage.sh

我们需要根据its文件中的描述来打包镜像生成itb文件(FIT uImage)。

这里我们直接将Rockchip Linux SDK中的mk-fitimage.sh文件、以及mkimage工具拷贝过来;

root@ubuntu:/work/sambashare/rk3588/recovery$ sudo cp ../armsom/armsom-rk3588-bsp/device/rockchip/common/scripts/mk-fitimage.sh .
root@ubuntu:/work/sambashare/rk3588/recovery$ sudo cp ../armsom/armsom-rk3588-bsp/rkbin/tools/mkimage ./

需要对mk-fitimage.sh脚本进行适当修改,最终内容如下:

# FIT uImage镜像:recovery.img
TARGET_IMG="recovery.img"
# its文件: boot4recovery.its 
ITS="boot4recovery.its"
# 内核镜像:Image.gz
KERNEL_IMG="Image.gz"
# ramsidk根文件系统:ramdisk.gz
RAMDISK_IMG="ramdisk.gz"
# 内核设备树:rk3588-nanopi6-rev01.dtb
KERNEL_DTB="rk3588-nanopi6-rev01.dtb"

# 判断its文件存在
if [ ! -f "$ITS" ]; then
        echo "$ITS not exists!"
        exit 1
fi

# 判断ramdisk.gz文件存在
if [ ! -f "$RAMDISK_IMG" ]; then
        ./mk-ramdisk.sh
fi

# 生成临时文件,复制its到临时文件
TMP_ITS=$(mktemp)
cp "$ITS" "$TMP_ITS"

# 使用sed命令替换一个临时文件$TMP_ITS中的占位符, 比如替换@KERNEL_DTB@->rk3588-nanopi6-rev01.dtb
sed -i -e "s~@KERNEL_DTB@~$(realpath -q "$KERNEL_DTB")~" \
        -e "s~@KERNEL_IMG@~$(realpath -q "$KERNEL_IMG")~" \
        -e "s~@RAMDISK_IMG@~$(realpath -q "$RAMDISK_IMG")~" "$TMP_ITS"

# 使用mkimage工具编译,这里一定要指定-E参数
mkimage -f "$TMP_ITS"  -E -p 0x800 "$TARGET_IMG"

# 删除临时文件
rm -f "$TMP_ITS"
1.4.3 生成resource.img

然后在命令行使用mk-fitimage.sh脚本编译即可:

root@ubuntu:/work/sambashare/rk3588/recovery$ sudo ./mk-fitimage.sh
FIT description: U-Boot FIT source file for arm
Created:         Sat Jul 20 22:40:55 2024
 Image 0 (fdt)
  Description:  Flattened Device Tree blob
  Created:      Sat Jul 20 22:40:55 2024
  Type:         Flat Device Tree
  Compression:  uncompressed
  Data Size:    270114 Bytes = 263.78 KiB = 0.26 MiB
  Architecture: AArch64
  Load Address: 0xffffff00
  Hash algo:    sha256
  Hash value:   cd297b6895d00b4626dcd74ad9a66b4c90173077585dc7a0c63034921e5ecb8a
 Image 1 (kernel)
  Description:  Vanilla Linux kernel
  Created:      Sat Jul 20 22:40:55 2024
  Type:         Kernel Image
  Compression:  gzip compressed
  Data Size:    12742364 Bytes = 12443.71 KiB = 12.15 MiB
  Architecture: AArch64
  OS:           Linux
  Load Address: 0xffffff01
  Entry Point:  0xffffff01
  Hash algo:    sha256
  Hash value:   7b70ba6cae5487c135c24b66122cb63002ea1d12166bd99e456d3ff43061c8a2
 Image 2 (ramdisk)
  Description:  Ramdisk for project-x
  Created:      Sat Jul 20 22:40:55 2024
  Type:         RAMDisk Image
  Compression:  gzip compressed
  Data Size:    49162664 Bytes = 48010.41 KiB = 46.89 MiB
  Architecture: AArch64
  OS:           Linux
  Load Address: 0xffffff02
  Entry Point:  unavailable
  Hash algo:    sha256
  Hash value:   da66e02be8015a035d9a36ed649e13143fab0e02699e6be332ca540886e6feed
 Default Configuration: 'conf'
 Configuration 0 (conf)
  Description:  Boot Linux kernel with FDT blob
  Kernel:       kernel
  Init Ramdisk: ramdisk
  FDT:          fdt
  Sign algo:    sha256,rsa2048:dev
  Sign padding: pss
  Sign value:   unavailable
  Timestamp:    unavailable

编译完成后我们查看recovery.img镜像大小:

root@ubuntu:/work/sambashare/rk3588/recovery$ ll
-rw-r--r--  1 root root     1589  7月 18 20:41 boot4recovery.its
drwxr-xr-x 38 root root     4096  7月 20 21:43 busybox-1.36.0/
drwxr-xr-x 13 root root     4096  7月 18 00:45 busybox_install/
-rw-r--r--  1 root root 12742364  7月 20 19:54 Image.gz
-rwxr-xr-x  1 root root      957  7月 18 20:37 mk-fitimage.sh*
-rwxr-xr-x  1 root root   206888  7月 18 19:34 mkimage*
-rwxr-xr-x  1 root root      408  7月 20 22:38 mk-ramdisk.sh*
-rw-r--r--  1 root root 49162664  7月 20 22:39 ramdisk.gz
-rw-r--r--  1 root root 62177192  7月 20 22:40 recovery.img
-rw-r--r--  1 root root   270114  7月 17 23:42 rk3588-nanopi6-rev01.dtb

1.5 烧录recovery.img

1.5.1 系统分区调整

由于编译的recovery.img的大小为62MB,而官方默认的parameter中配置的recovery分区大小为32MB,空间不够,因此我们需要修改parameter.txt

root@ubuntu:/work/sambashare/rk3588/friendly/sd-fuse_rk3588/debian-bullseye-desktop-arm64$ sudo vim parameter.txt

内容如下:

FIRMWARE_VER: 12.0
MACHINE_MODEL: RK3588
MACHINE_ID: 007
MANUFACTURER: RK3588
MAGIC: 0x5041524B
ATAG: 0x00200800
MACHINE: NanoPi6
CHECK_MASK: 0x80
PWR_HLD: 0,0,A,0,1
TYPE: GPT
CMDLINE: mtdparts=rk29xxnand:0x00002000@0x00004000(uboot),0x00002000@0x00006000(misc),0x00002000@0x00008000(dtbo),0x00008000@0x0000a000(resource),0x00014000@0x00012000(kernel),0x00010000@0x00026000(boot),0x00040000@0x00036000(recovery),0x007c0000@0x00076000(rootfs),-@0x00836000(userdata:grow)

解析信息如下:

Number Name 镜像文件 Start (sector) End (sector) Size
1 uboot uboot.img 0x4000(16384) 0x5FFF 4M
2 misc misc.img 0x6000(24576) 0x7FFF 4M
3 dtbo dtbo.img 0x8000(32768) 0x9FFF 4M
4 resource resource.img 0xa000(40960) 0x11FFF 16MB
5 kernel kernel.img 0x12000(73728) 0x25FFF 40MB
6 boot boot.img 0x26000(155648) 0x35FFF 32MB
7 recovery recovery.img 0x36000(221184) 0x75FFF 128MB
8 rootfs rootfs.img 0x76000(483328) 0x835FFF 3.968GB
9 userdata userdata.img 0x836000(8609792) -
1.5.2 生成统一固件

recovery.img拷贝到debian-bullseye-desktop-arm64/目录;

root@ubuntu:/work/sambashare/rk3588/friendly/sd-fuse_rk3588$ sudo cp /work/sambashare/rk3588/recovery/recovery.img ./debian-bullseye-desktop-arm64

debian-bullseye-desktop-arm64目录下的镜像文件重新打包成SD卡固件:

root@@ubuntu:/work/sambashare/rk3588/friendly/sd-fuse_rk3588# sudo ./mk-sd-image.sh debian-bullseye-desktop-arm64/
1.5.3 制作SD启动卡

我们将SD卡插入PC上,SD卡对应的设备节点为/dev/sdb,开始制作SD启动卡:

root@ubuntu:/work/sambashare/rk3588/friendly/sd-fuse_rk3588# sudo dd if=out/out/rk3588-sd-debian-bullseye-desktop-6.1-arm64-20240720.img of=/dev/sdb bs=4M status=progress

烧录完成,将SD卡插入开发板,启动系统,查看分区信息;

pi@NanoPC-T6:/userdata$ sudo fdisk -l /dev/mmcblk0
Disk /dev/mmcblk0: 29.72 GiB, 31914983424 bytes, 62333952 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: 73987B6B-4974-4C94-A3E8-58AB2EB7A946

Device           Start      End  Sectors  Size Type
/dev/mmcblk0p1   16384    24575     8192    4M unknown    # uboot       uboot.img
/dev/mmcblk0p2   24576    32767     8192    4M unknown    # misc        misc.img
/dev/mmcblk0p3   32768    40959     8192    4M unknown    # dtbo        dtbo.img
/dev/mmcblk0p4   40960    73727    32768   16M unknown    # resource    resource.img
/dev/mmcblk0p5   73728   155647    81920   40M unknown    # kernel      kernel.img
/dev/mmcblk0p6  155648   221183    65536   32M unknown    # boot        boot.img
/dev/mmcblk0p7  221184   483327   262144  128M unknown    # recovery    recovery.img
/dev/mmcblk0p8  483328  8609791  8126464  3.9G unknown    # rootfs      rootfs.img	
/dev/mmcblk0p9 8609792 62333918 53724127 25.6G unknown    # userdata    userdata.img
1.5.4 单独烧录recovery.img

如果我们有单独烧录recovery.img到开发板recovery分区的诉求,可以按照如下步骤;

pi@NanoPC-T6:/userdata$ sudo scp -r root@192.168.0.200:/work/sambashare/rk3588/recovery/recovery.img .
pi@NanoPC-T6:/userdata$ sudo dd if=recovery.img  of=/dev/mmcblk0p7 bs=1M

二、update_engine/recovery编译

我们要实现recovery模式下系统的升级功能,有两种升级方案可以选择,在《Rockchip RK3588 - Rockchip Linux Recovery updateEngine源码分析》已经进行了深入介绍。

这里我们采用updateEngine升级方案,源码位于<Rockchip Linux SDK>/external/recovery目录下,源码有两部分组成:

  • external/recovery/update_engine:生成updateEngine二进制bin程序,解析update.img固件中各个分区数据,并执行对各分区升级的关键程序;
  • external/recovery:生成recovery二进制bin程序,其中recovery二进制bin程序部会根据编译配置调用updateEngine或者rkupdate进行升级。

接下来我们需要介绍源码编译,源码编译有两种方式:

  • 交叉编译:即在ubuntu宿主机编译,该方式编译我们需要获取第三方库的交叉编译版本;
  • 直接将源码拷贝到开发板debian-bullseye-desktop系统,进行编译运行;

这里我们选择第二种方式,我们直接将Rockchip Linux SDK中的external/recovery源码拷贝到开发板opt目录;

pi@NanoPC-T6:/opt$ sudo scp -r root@192.168.0.200:/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/external/recovery ./

2.1 recovery

2.2 updateEngine

2.2.1 源码修改

创建文件recovery_autogenerate.h

pi@NanoPC-T6:/opt$ cd recovery
pi@NanoPC-T6:/opt$ sudo vim recovery_autogenerate.h
#define GIT_COMMIT_INFO -g28f720bc5-240524-dirty

修改Makefile

%.o: %.c  recovery_version
        $(CC) -c $< -o $@ $(CFLAGS)
# 修改为
%.o: %.c   # recovery_version
        $(CC) -c $< -o $@ $(CFLAGS)

2.2.2 源码编译

在开发板执行如下代码;

pi@NanoPC-T6:/opt/recovery$ sudo apt-get update
pi@NanoPC-T6:/opt/recovery$ sudo apt install make gcc libcurl4-openssl-dev libssl-dev libbz2-dev
pi@NanoPC-T6:/opt/recovery$ sudo make updateEngine CFLAGS="-D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -Os -g0 -D_FORTIFY_SOURCE=1 -I. -fPIC -lpthread -lcurl -lssl -lcrypto -lbz2 -DUSE_UPDATEENGINE=ON -DSUCCESSFUL_BOOT=ON"

编译完成后查看可执行文件:

pi@NanoPC-T6:/opt/recovery$ ls -l  updateEngine
-rwxr-xr-x 1 root root 91920 Jul 18 14:51 updateEngine
pi@NanoPC-T6:/opt/recovery$ ./updateEngine --h
LOG_INFO: *** update_engine: V1.0.1-g28f720bc5-240524-dirty ***.
LOG_INFO: --misc=now             Linux A/B mode: Setting the current partition to bootable.
LOG_INFO: --misc=other           Linux A/B mode: Setting another partition to bootable.
LOG_INFO: --misc=update          Recovery mode: Setting the partition to be upgraded.
LOG_INFO: --misc=display         Display misc info.
LOG_INFO: --misc=wipe_userdata   Format data partition.
LOG_INFO: --misc_custom= < op >  Operation on misc for custom cmdlineLOG_INFO:         op:     read   Read custom cmdline to /tmp/custom_cmdlineLOG_INFO:                 write  Write /tmp/custom_cmdline to custom areaLOG_INFO:                 clean  clean custom areaLOG_INFO: --update               Upgrade mode.
LOG_INFO: --partition=0x3FFC00   Set the partition to be upgraded.(NOTICE: OTA not support upgrade loader and parameter)
LOG_INFO:                        0x3FFC00: 0011 1111 1111 1100 0000 0000.
LOG_INFO:                                  uboot trust boot recovery rootfs oem
LOG_INFO:                                  uboot_a uboot_b boot_a boot_b system_a system_b.
LOG_INFO:                        100000000000000000000000: Upgrade loader
LOG_INFO:                        010000000000000000000000: Upgrade parameter
LOG_INFO:                        001000000000000000000000: Upgrade uboot
LOG_INFO:                        000100000000000000000000: Upgrade trust
LOG_INFO:                        000010000000000000000000: Upgrade boot
LOG_INFO:                        000001000000000000000000: Upgrade recovery
LOG_INFO:                        000000100000000000000000: Upgrade rootfs
LOG_INFO:                        000000010000000000000000: Upgrade oem
LOG_INFO:                        000000001000000000000000: Upgrade uboot_a
LOG_INFO:                        000000000100000000000000: Upgrade uboot_b
LOG_INFO:                        000000000010000000000000: Upgrade boot_a
LOG_INFO:                        000000000001000000000000: Upgrade boot_b
LOG_INFO:                        000000000000100000000000: Upgrade system_a
LOG_INFO:                        000000000000010000000000: Upgrade system_b
LOG_INFO:                        000000000000001000000000: Upgrade misc
LOG_INFO:                        000000000000000100000000: Upgrade userdata
LOG_INFO: --reboot               Restart the machine at the end of the program.
LOG_INFO: --version_url=url      The path to the file of version.
LOG_INFO: --image_url=url        Path to upgrade firmware.
LOG_INFO: --savepath=url         save the update.img to url.
LOG_INFO: --version              the version of updateEngine
LOG_INFO: --rkdebug              Log output to serial port
LOG_INFO: --ui_rotation          UI rotation,has 4 angles(0-3).

将可执行文件复制到/usr/bin目录;

pi@NanoPC-T6:/opt/recovery$ sudo mv updateEngine /usr/bin/

2.3 ramdisk.img移植updateEngine

由于编译updateEngine需要一些依赖头文件和和库,为此不得不下载安装libcurllibbz2库。

我们在《Rockchip RK3588 - Rockchip Linux Recovery updateEngine测试》进行recovery编译时候,实际上下载了这些源码,并进行了编译。recovery编译完成后,在编译输出目录 output/rockchip_rk3588_recovery 会生成子目录,说明如下:

  • build: 除了交叉编译的工具链之外的所有组件,包括Buildroot所需主机工具和选择的软件包,这个目录包含所有软件包源码;
  • host :包含为主机编译的工具的安装,这些工具是正确执行Buildroot所必需的,包括交叉编译工具
    链;
  • images :所有镜像(文件系统,比如ext2/4squashfscpio等格式镜像)存储目录;
  • staging :其中的层次结构类似于根文件系统层次结构。这个目录包含了安装交叉编译工具链和为目标选择的所有用户空间包,但是,这个目录不是用来成为目标的根文件系统:它包含许多开发文件、未剥离的二进制文件和库,这些文件和库也非常多,对嵌入式系统来说是很大的;这些开发文件用于为所依赖的目标提供编译库和应用程序需要的其他库;
  • target: 几乎包含了目标的完整根文件系统:除了/dev/目录中的设备文件,所有需要的东西都是存在(Buildroot不能创建它们,因为Buildroot不会以root身份运行,也不希望以root身份运行)。此外,它没有正确的权限(例如,busybox二进制文件的setuid),因此,该目录不应该用于你的目标;相反,您应该使用images/目录中构建的映像之一;如果你需要在根文件系统中用NFS挂载外部镜像,必须用root用户在images/目录中生成镜像,相比对于staging/target/只包含运行所选目标应用程序所需的文件和库:开发文件(头文件)不存在,二进制文件被剥离。

staging目录下可以找到这些源码的头文件以及编译后的库文件,以libcurl为例;

root@ubuntu:/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/staging$ ls
bin  dev  etc  lib  lib64  media  mnt  opt  proc  root  run  sbin  sys  tmp  usr  var
bsp/buildroot/output/rockchip_rk3588_recovery/staging$ ll usr/include/ | grep curl.h
root@ubuntu:/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/staging$ ll usr/lib* | grep libcurl.so
lrwxrwxrwx 1 root root      16  6月 18 01:47 libcurl.so -> libcurl.so.4.7.0*
lrwxrwxrwx 1 root root      16  6月 18 01:47 libcurl.so.4 -> libcurl.so.4.7.0*
-rwxr-xr-x 1 root root  557120  6月 18 01:47 libcurl.so.4.7.0*

三、 OTA升级测试

在前面章节我们已经已经做了如下准备工作:

  • 制作了SD启动卡,烧录了rk3588-sd-debian-bullseye-desktop-6.1-arm64-20240714.img
  • SDdebian-bullseye-desktop系统recovery分区烧录了我们制作的recovery.img镜像文件;
  • 编译了/opt/recovery源码生成了updateEngine可执行文件。

接下来我们就通过updateEngine工具来升级固件,既然是固件升级,那我们首先要制作升级的固件。

注意:这里我们升级测试,升级的也是SD中的固件。

3.1 制作升级固件update.img

友善提供的系统固件主要分为三大类:

  • SD卡固件:可以烧录到SDK运行的固件,比如我们开篇制作的SD启动卡烧录的就是这种固件(rk3588-sd-debian-bullseye-desktop-6.1-arm64-20240714.img);
  • SD卡刷机固件:烧录到SD卡,SD卡启动后会将固件烧录到eMMC;比如友善提供的固件压缩包rk3588-eflasher-debian-bullseye-desktop-6.1-arm64-20231116.img.gz,解压后就可以得到这种固件镜像;
  • USB线刷固件:以友善提供的固件压缩包rk3588-usb-debian-bullseye-desktop-6.1-arm64-20231116.zip为例,解压后可以看到里面就是各个分区镜像以及RKDevTool.exe烧录工具;

然而上面的几种固件格式均不是updateEngine可执行文件能够识别的固件格式,接下来我们需要使用这些分区镜像来制作updateEngine可执行文件能够识别的固件。

关于固件的制作步骤我们是通过《Rockchip RK3588 - Rockchip Linux SDK脚本分析》中对90-updateimg.sh分析得来的,具体步骤如下。

3.1.1 准备分区镜像

/work/sambashare/rk3588/recovery目录下创建update文件夹;

root@ubuntu:/work/sambashare/rk3588/recovery$ sudo mkdir update

接着我们将友善提供的分区镜像文件压缩包上传到宿主机,比如这里我上传了03_分区镜像文件/ubuntu-jammy-x11-desktop-arm64-images.tgz,解压到Image文件夹;

root@ubuntu:/work/sambashare/rk3588/recovery/update$ tar -zxf ubuntu-jammy-x11-desktop-arm64-images.tgz -C . --strip-components=1
root@ubuntu:/work/sambashare/rk3588/recovery/update$ sudo rm -rf ubuntu-jammy-x11-desktop-arm64-images.tgz info.conf
root@ubuntu:/work/sambashare/rk3588/recovery/update$ ll
-rw-r--r-- 1 root root    8072140  5月 28  2023 boot.img
-rw-r--r-- 1 root root       1424  5月 28  2023 dtbo.img
-rw-r--r-- 1 root root     307200  9月  8  2023 idbloader.img
-rw-r--r-- 1 root root         71 11月 17  2023 info.conf
-rw-r--r-- 1 root root   35551252 11月 16  2023 kernel.img
-rw-r--r-- 1 root root     471488  9月  8  2023 MiniLoaderAll.bin
-rw-r--r-- 1 root root      49152  5月 28  2023 misc.img
-rw-r--r-- 1 root root        470 11月 17  2023 parameter.txt
-rw-r--r-- 1 root root    6227456 11月 16  2023 resource.img
-rw-r--r-- 1 root root 5933630704 11月 17  2023 rootfs.img
-rw-r--r-- 1 root root    4194304  9月  8  2023 uboot.img
-rw-r--r-- 1 root root     159868 11月 17  2023 userdata.img

同样我们需要修改 parameter.txt为:

FIRMWARE_VER: 12.0
MACHINE_MODEL: RK3588
MACHINE_ID: 007
MANUFACTURER: RK3588
MAGIC: 0x5041524B
ATAG: 0x00200800
MACHINE: NanoPi6
CHECK_MASK: 0x80
PWR_HLD: 0,0,A,0,1
TYPE: GPT
CMDLINE: mtdparts=rk29xxnand:0x00002000@0x00004000(uboot),0x00002000@0x00006000(misc),0x00002000@0x00008000(dtbo),0x00008000@0x0000a000(resource),0x00014000@0x00012000(kernel),0x00010000@0x00026000(boot),0x00040000@0x00036000(recovery),0x00b60000@0x00076000(rootfs),-@0x00bd6000(userdata:grow)

此外,我们需要将我们前面制作的recovery.img拷贝到该目录;

root@ubuntu:/work/sambashare/rk3588/recovery/update$ sudo cp ../recovery.img .

如果我们更改了ubootkernelresource等分区镜像,只需要替换该目录下的分区镜像文件即可。

3.1.2 准备package-file

/work/sambashare/rk3588/recovery/update目录下创建package-file,内容如下;

# NAME  PATH
package-file    package-file
parameter       parameter.txt
bootloader      MiniLoaderAll.bin
uboot           uboot.img
misc            misc.img
dtbo            dtbo.img
resource        resource.img
kernel          kernel.img
boot            boot.img
recovery        recovery.img
rootfs          rootfs.img
userdata        userdata.img

升级固件不一定要全分区升级,可修改 package-file 文件,将不要升级的分区去掉,这样可以减少升级包的大小。比如将 rootfs的相对路径改为 RESERVED,这样就不会打包根文件系统,即不升级根文件系统分区。

3.1.3 准备打包工具

这里我们直接将Rockchip Linux SDK中的afptoolrkImageMaker工具拷贝过来;

root@ubuntu:/work/sambashare/rk3588/recovery/update$ sudo cp ../../armsom/armsom-rk3588-bsp/tools/linux/Linux_Pack_Firmware/rockdev/afptool ./
root@ubuntu:/work/sambashare/rk3588/recovery/update$ sudo cp ../../armsom/armsom-rk3588-bsp/tools/linux/Linux_Pack_Firmware/rockdev/rkImageMaker ./
3.1.4 打包

执行afptool命令开始打包生成update.raw.img

root@ubuntu:/work/sambashare/rk3588/recovery/update$ sudo ./afptool -pack . update.raw.img
Android Firmware Package Tool v2.2
------ PACKAGE ------
Add file: ./package-file
package-file,Add file: ./package-file done,offset=0x800,size=0x15c,userspace=0x1
Add file: ./parameter.txt
parameter,Add file: ./parameter.txt done,offset=0x1000,size=0x1e2,userspace=0x1,flash_address=0x00000000
Add file: ./MiniLoaderAll.bin
bootloader,Add file: ./MiniLoaderAll.bin done,offset=0x1800,size=0x731c0,userspace=0xe7
Add file: ./uboot.img
uboot,Add file: ./uboot.img done,offset=0x75000,size=0x400000,userspace=0x800,flash_address=0x00004000
Add file: ./misc.img
misc,Add file: ./misc.img done,offset=0x475000,size=0xc000,userspace=0x18,flash_address=0x00006000
Add file: ./dtbo.img
dtbo,Add file: ./dtbo.img done,offset=0x481000,size=0x590,userspace=0x1,flash_address=0x00008000
Add file: ./resource.img
resource,Add file: ./resource.img done,offset=0x481800,size=0x5c6a00,userspace=0xb8e,flash_address=0x0000a000
Add file: ./kernel.img
kernel,Add file: ./kernel.img done,offset=0xa48800,size=0x21c7814,userspace=0x4390,flash_address=0x00012000
Add file: ./boot.img
boot,Add file: ./boot.img done,offset=0x2c10800,size=0x7b2bcc,userspace=0xf66,flash_address=0x00026000
Add file: ./recovery.img
recovery,Add file: ./recovery.img done,offset=0x33c3800,size=0x3b4bfa8,userspace=0x7698,flash_address=0x00036000
Add file: ./rootfs.img
rootfs,Add file: ./rootfs.img done,offset=0x6f0f800,size=0x161ac04f0,userspace=0x2c3581,flash_address=0x00076000
Add file: ./userdata.img
userdata,Add file: ./userdata.img done,offset=0x1689d0000,size=0x2707c,userspace=0x4f,flash_address=0x00bd6000
Add CRC...
Make firmware OK!
------ OK ------

执行rkImageMaker命令开始打包生成update.img

root@ubuntu:/work/sambashare/rk3588/recovery/update$ sudo ./rkImageMaker -RK3588 MiniLoaderAll.bin update.raw.img update.img -os_type:androidos
********rkImageMaker ver 2.23********
Generating new image, please wait...
Writing head info...
Writing boot file...
Writing firmware...
Generating MD5 data...
MD5 data generated successfully!
New image generated successfully!

查看生成的固件:

rot@ubuntu:/work/sambashare/rk3588/recovery/update$ ll update.*
-rw-r--r-- 1 root root 6050720330  7月 20 23:09 update.img
-rw-r--r-- 1 root root 6050248708  7月 20 23:08 update.raw.img

3.2 修改根设备节点别名

与《Rockchip RK3588 - Rockchip Linux Recovery updateEngine测试》中介绍的buildroot系统升级一样,updateEngine升级方案也支持debianubuntu系统下的升级。

由于updateEngine升级需要通过设备各个分区节点来识别并写入不同设备分区节点的固件数据,buildroot系统是通过udev中的别名方式(by-name)来对设备分区节点做了通用的易识别的处理。

debianubuntu系统中因为缺少这样的方式,导致了实际中updateEngine不能正常运行的情况,所以只需要将debianubuntu系统中设备分区的节点也跟buildroot系统下可通过by-name别名方式标识出来,updateEngine即可正常工作。

3.2.1 61-partition-init.rules

具体修改方式如下:

pi@NanoPC-T6:/opt$ sudo vim /lib/udev/rules.d/61-partition-init.rules 

# rockchip internal storage links: /dev/block/by-name

ACTION=="remove", GOTO="rk_internal_storage_end"
ENV{UDEV_DISABLE_ROCKCHIP_STORAGE_RULES_FLAG}=="1", GOTO="rk_internal_storage_end"
SUBSYSTEM!="block", GOTO="rk_internal_storage_end"
KERNEL!="mmcblk*[0-9]|rkflash*|rknand*|mtdblock*", GOTO="rk_internal_storage_end"

# ignore partitions that span the entire disk
TEST=="whole_disk", GOTO="rk_internal_storage_end"

# for partitions import parent information
ENV{DEVTYPE}=="partition", IMPORT{parent}="ID_*"

# for rknand parameter partition name
ENV{DEVNAME}=="/dev/rknand*", ENV{DEVTYPE}=="disk", ENV{DEVPATH}=="/devices/virtual/block/rknand", ENV{ID_RKNAND_PART_NAME}=""

# for rkflash gpt partition name /dev/block/by-name link
ENV{DEVTYPE}=="partition", ENV{PARTNAME}=="?*", SYMLINK+="block/by-name/$env{PARTNAME}"

# for emmc gpt partition name /dev/block/by-name link
ENV{ID_PART_ENTRY_SCHEME}=="gpt", ENV{ID_PART_ENTRY_NAME}=="?*", SYMLINK+="block/by-name/$env{ID_PART_ENTRY_NAME}"

# for mtd partition name /dev/block/by-name link
ENV{DEVNAME}=="/dev/mtdblock*", ENV{DEVTYPE}=="disk", SYMLINK+="block/by-name/$attr{device/name}"

LABEL="rk_internal_storage_end"

修改的目的就是开机启动后可以将debian系统或ubuntu系统中各个分区节点形如/dev/mmcblk0p1/dev/mmcblk0p2/dev/mmcblk0p3/dev/mmcblk0p4 ... 修改为/dev/block/byname/uboot /dev/block/by-name/misc/dev/block/by-name/dtbo/dev/block/byname/ resource ...等。

3.2.2 /dev/block

重启系统,查看/dev/block

pi@NanoPC-T6:~$ ls /dev/block/
179:0   179:100  179:103  179:11  179:14  179:2   179:4  179:64  179:9   179:98  1:1  1:4  1:7  7:2  7:5  by-name
179:1   179:101  179:104  179:12  179:15  179:3   179:5  179:7   179:96  179:99  1:2  1:5  7:0  7:3  7:6
179:10  179:102  179:105  179:13  179:16  179:32  179:6  179:8   179:97  1:0     1:3  1:6  7:1  7:4  7:7
pi@NanoPC-T6:~$ ls /dev/block/by-name/ -l
lrwxrwxrwx 1 root root 15 Jul 18 15:55 uboot -> ../../mmcblk0p1
lrwxrwxrwx 1 root root 15 Jul 18 15:55 misc -> ../../mmcblk0p2
lrwxrwxrwx 1 root root 15 Jul 18 15:55 dtbo -> ../../mmcblk0p3
lrwxrwxrwx 1 root root 15 Jul 18 15:55 resource -> ../../mmcblk0p4
lrwxrwxrwx 1 root root 15 Jul 18 15:55 kernel -> ../../mmcblk0p5
lrwxrwxrwx 1 root root 15 Jul 18 15:55 boot -> ../../mmcblk0p6
lrwxrwxrwx 1 root root 15 Jul 18 15:55 recovery -> ../../mmcblk0p7
lrwxrwxrwx 1 root root 15 Jul 18 15:55 rootfs -> ../../mmcblk0p8
lrwxrwxrwx 1 root root 15 Jul 18 15:55 userdata -> ../../mmcblk0p9
lrwxrwxrwx 1 root root 15 Jul 18 15:55 security -> ../../mmcblk2p1
lrwxrwxrwx 1 root root 15 Jul 18 15:55 uboot_a -> ../../mmcblk2p2
lrwxrwxrwx 1 root root 15 Jul 18 15:55 uboot_b -> ../../mmcblk2p3
lrwxrwxrwx 1 root root 15 Jul 18 15:55 dtbo_a -> ../../mmcblk2p5
lrwxrwxrwx 1 root root 15 Jul 18 15:55 dtbo_b -> ../../mmcblk2p6
lrwxrwxrwx 1 root root 15 Jul 18 15:55 vbmeta_a -> ../../mmcblk2p7
lrwxrwxrwx 1 root root 15 Jul 18 15:55 vbmeta_b -> ../../mmcblk2p8
lrwxrwxrwx 1 root root 15 Jul 18 15:55 boot_a -> ../../mmcblk2p9
lrwxrwxrwx 1 root root 16 Jul 18 15:55 boot_b -> ../../mmcblk2p10
lrwxrwxrwx 1 root root 16 Jul 18 15:55 backup -> ../../mmcblk2p11
lrwxrwxrwx 1 root root 16 Jul 18 15:55 cache -> ../../mmcblk2p12
lrwxrwxrwx 1 root root 16 Jul 18 15:55 metadata -> ../../mmcblk2p13
lrwxrwxrwx 1 root root 16 Jul 18 15:55 baseparameter -> ../../mmcblk2p14
lrwxrwxrwx 1 root root 16 Jul 18 15:55 super -> ../../mmcblk2p15

其中:

  • mmcblk0SD卡烧录的就是我们制作的SD卡固件,系统为debian-bullseye-desktop
  • mmcblk2eMMC,烧录的是SD卡刷机固件,系统为android12

若还是出现如下类似设备节点:

root@linaro-alip:~# ls /dev/block/
179:0 179:3 179:5 179:7 179:96 7:0 7:3 7:6
179:1 179:32 179:6 179:8 1:0 7:1 7:4 7:7
179:2 179:4 179:64 179:9 254:0 7:2 7:5

可尝试将61-partition-init.rules放在debianubuntu /etc/udev/rules.d/lib/udev/rules.d/

3.3 网络升级测试

3.3.1 升级测试

我们可以在ubunut开启http服务,搭建一个web服务;

root@ubuntu:/work/sambashare/rk3588/recovery/update# python3 -m http.server 8080

然后执行updateEngine网络升级命令;

pi@NanoPC-T6:/opt$ sudo updateEngine --image_url=http://192.168.0.200:8080/update.img --misc=update --partition=0x60000 --savepath=/userdata/update.img --reboot

这里我们仅仅升级了recovery以及rootfs分区。如需更新其它分区,可以修改--partition参数:

  • 如果没有传入要升级的分区,默认升级uboot/trust/boot/recovery/rootfs/oempartition=0x3F0000;也就是升级我们制作的固件中的uboot、boot、recovery、rootfs、oem这5个分区(升级固件没有trust分区);
  • 如果指定了parametermiscparametermisc分区会自动忽略;
  • 如果升级uboot分区,指定partition=0x200000;烧录升级固件中的分区镜像uboot.img
  • 如果升级trust分区,指定partition=0x100000;升级固件中没有该分区镜像,跳过;
  • 如果升级boot分区,指定partition=0x80000;烧录升级固件中的分区镜像boot.img
  • 如果升级recovery分区,指定partition=0x40000;烧录升级固件中的分区镜像recovery.img
  • 如果升级rootfs分区,指定partition=0x20000;烧录升级固件中的分区镜像rootfs.img
  • 如果升级oem分区,指定partition=0x10000;升级固件中没有该分区镜像,跳过;
  • 如果升级userdata分区,指定partition=0x100;烧录升级固件中的分区镜像userdata.img
  • 如果需要升级多个的分区,需要将上面的值进行或运算得到新的partition值。

此外,我们制作的升级固件update.img还包含了dtboresourcekernel分区镜像,如需对这些分区进行升级需要参考《update_engine/recovery编译》支持自定义分区升级小节。

在升级指定升级分区时一定要指定recovery分区,这样才会进入recovery系统烧录除了recovery之外的分区;

  • 如果没有指定recovery分区,那么将会在normal系统进行烧录,存在诸多风险,比如:升级中断导致normal系统无法正常启动;
  • 而在recovery系统进行升级能保证升级的完整性,即升级过程被中断,如异常掉电,升级仍然能继续执行;

注意:除此之外,我们还需要注意一点,不可以升级升级固件update.img存放的分区,因为这样存在覆盖的问题。比如我们如果将升级固件放在/userdata目录,就不要升级userdata分区。

3.3.2 normal系统下升级日志

首先在normal系统下升级recovery分区,输出日志如下;

点击查看代码
LOG_INFO: *** update_engine: V1.0.1-g28f720bc5-240524-dirty ***.
LOG_INFO: Current Mode is recovery.
LOG_INFO: start RK_ota_url url [http://192.168.0.200:8080/update.img] save path [/userdata/update.img].
LOG_INFO: save image to /userdata/update.img.
LOG_INFO: url = http://192.168.0.200:8080/update.img.
LOG_INFO: [MiscUpdate:90] save path: /userdata/update.img
LOG_INFO: update recovery in normal system.
LOG_INFO: [RK_ota_set_partition:106] num [16]
LOG_INFO: need update parameter.
LOG_INFO: need update recovery.
LOG_INFO: Now is SD.
LOG_INFO: no found mtd.
LOG_INFO: start RK_ota_start.
LOG_INFO: rk m_status = 1.
LOG_INFO: down_processvalue is 0%
LOG_INFO: down_processvalue is 1%
LOG_INFO: down_processvalue is 2%
......
LOG_INFO: down_processvalue is 100%
LOG_INFO: [RK_ota_set_partition:106] num [16]
LOG_DEBUG: uiTag = 57464b52.
LOG_DEBUG: usSize = 66.
LOG_DEBUG: dwVersion = c000000.
LOG_DEBUG: btMajor = c, btMinor = 0, usSmall = 00.
LOG_DEBUG: dwBootOffset = 66.
LOG_DEBUG: dwBootSize = 731c0.
LOG_DEBUG: dwFWOffset = 73226.
LOG_DEBUG: dwFWSize = 689f7804.
LOG_DEBUG: tag = 1178684242
LOG_DEBUG: size = 1755281408
LOG_DEBUG: machine_model =  RK3588
LOG_DEBUG: manufacturer =  RK3588
LOG_DEBUG: version = 201326592
LOG_DEBUG: item = 12.
LOG_INFO: ================================================
LOG_DEBUG: name = package-file
LOG_DEBUG: file = package-file
LOG_DEBUG: offset = 473638
LOG_DEBUG: flash_offset = -1
LOG_DEBUG: usespace = 1
LOG_DEBUG: size = 348
LOG_INFO: ================================================
LOG_DEBUG: name = parameter
LOG_DEBUG: file = parameter.txt
LOG_DEBUG: offset = 475686
LOG_DEBUG: flash_offset = 0
LOG_DEBUG: usespace = 1
LOG_DEBUG: size = 482
LOG_INFO: ================================================
LOG_DEBUG: name = bootloader
LOG_DEBUG: file = MiniLoaderAll.bin
LOG_DEBUG: offset = 102
LOG_DEBUG: flash_offset = -1
LOG_DEBUG: usespace = 231
LOG_DEBUG: size = 471488
LOG_INFO: ================================================
LOG_DEBUG: name = uboot
LOG_DEBUG: file = uboot.img
LOG_DEBUG: offset = 950822
LOG_DEBUG: flash_offset = 16384
LOG_DEBUG: usespace = 2048
LOG_DEBUG: size = 4194304
LOG_INFO: ================================================
LOG_DEBUG: name = misc
LOG_DEBUG: file = misc.img
LOG_DEBUG: offset = 5145126
LOG_DEBUG: flash_offset = 24576
LOG_DEBUG: usespace = 24
LOG_DEBUG: size = 49152
LOG_INFO: ================================================
LOG_DEBUG: name = dtbo
LOG_DEBUG: file = dtbo.img
LOG_DEBUG: offset = 5194278
LOG_DEBUG: flash_offset = 32768
LOG_DEBUG: usespace = 1
LOG_DEBUG: size = 1424
LOG_INFO: ================================================
LOG_DEBUG: name = resource
LOG_DEBUG: file = resource.img
LOG_DEBUG: offset = 5196326
LOG_DEBUG: flash_offset = 40960
LOG_DEBUG: usespace = 2958
LOG_DEBUG: size = 6056448
LOG_INFO: ================================================
LOG_DEBUG: name = kernel
LOG_DEBUG: file = kernel.img
LOG_DEBUG: offset = 11254310
LOG_DEBUG: flash_offset = 73728
LOG_DEBUG: usespace = 17296
LOG_DEBUG: size = 35420180
LOG_INFO: ================================================
LOG_DEBUG: name = boot
LOG_DEBUG: file = boot.img
LOG_DEBUG: offset = 46676518
LOG_DEBUG: flash_offset = 155648
LOG_DEBUG: usespace = 3942
LOG_DEBUG: size = 8072140
LOG_INFO: ================================================
LOG_DEBUG: name = recovery
LOG_DEBUG: file = recovery.img
LOG_DEBUG: offset = 54749734
LOG_DEBUG: flash_offset = 221184
LOG_DEBUG: usespace = 30360
LOG_DEBUG: size = 62177192
LOG_INFO: ================================================
LOG_DEBUG: name = rootfs
LOG_DEBUG: file = rootfs.img
LOG_DEBUG: offset = 116927014
LOG_DEBUG: flash_offset = 483328
LOG_DEBUG: usespace = 2897281
LOG_DEBUG: size = 1638663408
LOG_INFO: ================================================
LOG_DEBUG: name = userdata
LOG_DEBUG: file = userdata.img
LOG_DEBUG: offset = 1755591206
LOG_DEBUG: flash_offset = 12410880
LOG_DEBUG: usespace = 79
LOG_DEBUG: size = 159868
LOG_INFO: new md5:fb182c591afe986abd10f8069cac30c0
LOG_INFO: MD5Check is ok of /userdata/update.img
LOG_INFO: analyticImage ok.
LOG_INFO: found rkimage_hdr.item[1].name = parameter.
LOG_INFO: found rkimage_hdr.item[9].name = recovery.
LOG_INFO: Now is SD.
LOG_INFO: no found mtd.
LOG_INFO: now write parameter to /dev/block/by-name/gpt.
LOG_INFO: ingore misc.
LOG_INFO: now write recovery to /dev/block/by-name/recovery.
LOG_INFO: update_cmd.flash_offset = 0.
LOG_INFO: flash_normal:205 start.
LOG_INFO: Now is SD.
LOG_INFO: no found mtd.
LOG_INFO: flash_normal:227, diff check for recovery
LOG_WARN: Not a diff image, ret = 80
LOG_INFO: block_write src /userdata/update.img dest /dev/block/by-name/recovery.
LOG_INFO: Now is SD.
LOG_INFO: no found mtd.
LOG_INFO: new md5:df6b7b60a38d22f1f4c0779d2600df10
LOG_INFO: MD5Check is ok of /dev/block/by-name/recovery
LOG_INFO: new md5:df6b7b60a38d22f1f4c0779d2600df10
LOG_INFO: MD5Check is ok of /userdata/update.img
LOG_INFO: check /dev/block/by-name/recovery ok.
LOG_INFO: RK_ota_start is ok!LOG_INFO: rk ota success.
LOG_INFO: Current Mode is recovery.
LOG_INFO: rk m_status = 0.
LOG_INFO: Now is SD.
LOG_INFO: no found mtd.

通过查看日志信息,我们就可以了解到在normal系统下的升级流程,即仅仅升级recovery分区,这和和文章《Rockchip RK3588 - Rockchip Linux Recovery updateEngine源码分析》分析的一致。

3.3.3 recovery系统下升级日志

接着重启进入recovery系统,系统启动关键日志:

点击查看代码
U-Boot 2017.09-g8241716-dirty #root (Jul 20 2024 - 11:31:35 +0800)

Board: NanoPi R6S
PreSerial: 2, raw, 0xfeb50000
DRAM:  16 GiB
Sysmem: init
Relocation Offset: eda2b000
Relocation fdt: eb9f9e20 - eb9fece0
CR: M/C/I
Using default environment

DM: v2
no mmc device at slot 1
mmc@fe2c0000: 1 (SD), mmc@fe2e0000: 0
Bootdev(atags): mmc 1
MMC1: Legacy, 52Mhz
PartType: EFI
boot mode: recovery (misc)
Failed to get rk3588-nanopi6-rev01.dtb
Failed to load DTB, ret=-19
No valid DTB, ret=-22
Failed to get kernel dtb, ret=-22
rockchip_set_serialno: could not find efuse/otp device
CLK: (sync kernel. arm: enter 1008000 KHz, init 1008000 KHz, kernel 0N/A)
  b0pll 24000 KHz
  b1pll 24000 KHz
  lpll 24000 KHz
  v0pll 24000 KHz
  aupll 24000 KHz
  cpll 1500000 KHz
  gpll 1188000 KHz
  npll 24000 KHz
  ppll 100000 KHz
  aclk_center_root 702000 KHz
  pclk_center_root 100000 KHz
  hclk_center_root 396000 KHz
  aclk_center_low_root 500000 KHz
  aclk_top_root 750000 KHz
  pclk_top_root 100000 KHz
  aclk_low_top_root 396000 KHz
vdd_usbc 12510 mV

Net:   No ethernet found.
Hit key to stop autoboot('CTRL+C'):  0
## Booting FIT Image at 0xe9f0b200 with size 0x018eda94
Fdt Ramdisk skip relocation
## Loading kernel from FIT Image at e9f0b200 ...
   Using 'conf' configuration
optee api revision: 2.0
TEEC: Waring: Could not find security partition
## Verified-boot: 0
   Trying 'kernel' kernel subimage
     Description:  Vanilla Linux kernel
     Type:         Kernel Image
     Compression:  gzip compressed
     Data Start:   0xe9f4d924
     Data Size:    12742364 Bytes = 12.2 MiB
     Architecture: AArch64
     OS:           Linux
     Load Address: 0x00400000
     Entry Point:  0x00400000
     Hash algo:    sha256
     Hash value:   7b70ba6cae5487c135c24b66122cb63002ea1d12166bd99e456d3ff43061c8a2
   Verifying Hash Integrity ... sha256+ OK
## Loading ramdisk from FIT Image at e9f0b200 ...
   Using 'conf' configuration
   Trying 'ramdisk' ramdisk subimage
     Description:  Ramdisk for project-x
     Type:         RAMDisk Image
     Compression:  gzip compressed
     Data Start:   0xeab74800
     Data Size:    13124403 Bytes = 12.5 MiB
     Architecture: AArch64
     OS:           Linux
     Load Address: 0x0a200000
     Entry Point:  unavailable
     Hash algo:    sha256
     Hash value:   06fce1042757d3414fbb496f362e7b537c476674eae2f9fce0f0fc77518303c6
   Verifying Hash Integrity ... sha256+ OK
+    Loading ramdisk from 0x0a200000 to 0x0a200000
## Loading fdt from FIT Image at e9f0b200 ...
   Using 'conf' configuration
   Trying 'fdt' fdt subimage
     Description:  Flattened Device Tree blob
     Type:         Flat Device Tree
     Compression:  uncompressed
     Data Start:   0xe9f0ba00
     Data Size:    270114 Bytes = 263.8 KiB
     Architecture: AArch64
     Load Address: 0x08300000
     Hash algo:    sha256
     Hash value:   cd297b6895d00b4626dcd74ad9a66b4c90173077585dc7a0c63034921e5ecb8a
   Verifying Hash Integrity ... sha256+ OK
   Using fdt from load-in fdt
   Loading fdt from 0xe9f0ba00 to 0x08300000
   Booting using the fdt blob at 0x08300000
   Uncompressing GZIP Kernel Image from 0xe9f4d924 to 0x00400000 ... with 021c7808 bytes OK
   kernel loaded at 0x00400000, end = 0x025c7808
   Using Device Tree in place at 0000000008300000, end 0000000008344f21
can't get otp device, ret=-19
## reserved-memory:
  cma: addr=10000000 size=8000000
  vendor-storage-rm@00000000: addr=ebc37000 size=10000
  ramoops@110000: addr=110000 size=e0000
Adding bank: 0x00200000 - 0x08400000 (size: 0x08200000)
Adding bank: 0x09400000 - 0xf0000000 (size: 0xe6c00000)
Adding bank: 0x100000000 - 0x3fc000000 (size: 0x2fc000000)
Adding bank: 0x3fc500000 - 0x3fff00000 (size: 0x03a00000)
Adding bank: 0x4f0000000 - 0x500000000 (size: 0x10000000)
Total: 3649.665/4541.166 ms

Starting kernel ...

[    4.551860] Booting Linux on physical CPU 0x0000000000 [0x412fd050]
[    4.551883] Linux version 6.1.25 (root@ubuntu) (aarch64-linux-gnu-gcc (Ubuntu 10.5.0-1ubuntu1~22.04) 10.5.0, GNU ld (GNU Binutils for Ubuntu) 2.38) #3 SMP Sat Jul 20 17:51:46 CST 2024
......
[    4.916562] Kernel command line: storagemedia=sd androidboot.storagemedia=sd androidboot.mode=normal earlycon=uart8250,mmio32,0xfeb50000 console=ttyFIQ0 coherent_pool=1m irqchip.gicv3_pseudo_nmi=0
......
[    6.619259] RAMDISK: ext2 filesystem found at block 0
[    6.619277] RAMDISK: Loading 74136KiB [1 disk] into ram disk... /
......

执行/usr/bin/recovery升级其它分区。

3.4 其它

3.4.1 开机进入normal系统

uboot会根据misc分区存放的字段来判断将要引导的系统是normal系统还是recovery系统。

如果uboot引导一直进入recovery系统,我们可以考虑在uboot命令行中清理misc分区内容。

misc分区位于0x6000扇区,大小为0x2000个扇区,实际上我们只用清除偏移16k1088个字节即可.

切换到SD卡所属设备:

==> mmc list
mmc@fe2c0000: 1 (SD)         # 代表SD卡
mmc@fe2e0000: 0              # 代表eMMC

==> mmc dev 1
switch to partitions #0, OK
mmc1 is current device

==> mmc info
Device: mmc@fe2c0000
Manufacturer ID: 3
OEM: 5344
Name: SD32G
Timing Interface: Legacy
Tran Speed: 52000000
Rd Block Len: 512
SD version 3.0
High Capacity: Yes
Capacity: 29.7 GiB
Bus Width: 4-bit
Erase Group Size: 512 Bytes

misc分区偏移16k读取3个扇区数据:

==> mmc read 0x10000000  0x6020 3

查看这三个扇区数据:

# 查看前两个扇区数据
==> md.b 0x10000000 0x600
10000000: 62 6f 6f 74 2d 72 65 63 6f 76 65 72 79 00 00 00    boot-recovery...
10000010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
10000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
10000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
10000040: 72 65 63 6f 76 65 72 79 0a 2d 2d 75 70 64 61 74    recovery.--updat
10000050: 65 5f 70 61 63 6b 61 67 65 3d 2f 75 73 65 72 64    e_package=/userd
10000060: 61 74 61 2f 75 70 64 61 74 65 2e 69 6d 67 0a 00    ata/update.img..
10000070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
10000080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
10000090: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
......
10000340: 00 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
......

这里的数据会解析成struct bootloader_message数据结构:

struct bootloader_message {
    char command[32];            // boot-recovery
    char status[32];
    char recovery[768];          //  recovery\n--update_package=/userdata/update.img
    char needupdate[4];          // 偏移0x340  00 00 02 00 =>0x20000,即升级rootfs分区
    char systemFlag[252];
};

清除这三个扇区数据:

==> mmc erase 0x6020 3
MMC erase: dev # 1, block # 24608, count 3 ... 3 blocks erased: OK

然后重启开发板看看是否可以正常进入normal系统。

3.4.2 开机进入recovery系统

如需开机就进入recovery系统,我们可以向misc分区写入启动命令;

# 切换当前设备为SD卡
==> mmc dev 1
# 清除内存0x10000000开始的16个字节
==> mw.b 0x10000000 00 0x10           
# 向内存0x10000000写入8个字节
==> mw.q 0x10000000 6365722d746f6f62
# 向内存0x10000008写入8个字节
==> mw.q 0x10000008 000000797265766f
# 将内存0x10000000开始的512个字节写入删除0x6020
==> mmc write 0x10000000 0x6020 1

# 读取数据,确认写入成功
==> mmc read 0x10000000  0x6020 1
==> md.b 0x10000000 0x10

这样重启开发板就可以进入recovery系统;

四、uboot改造

4.1 rockchip_get_boot_mode

函数定义在arch/arm/mach-rockchip/spl_boot_mode.c文件,rockchip_get_boot_mode用于获取系统启动是进入normal系统还是recovery系统,如果是normal系统,则从boot分区加载内核,否则从recovery分区加载内核。

int rockchip_get_boot_mode(struct blk_desc *dev_desc, u32 bcb_sector_offset)
{
        uint32_t reg_boot_mode;
        int boot_mode;

        /*
         * Boot mode priority
         *
         * Anyway, we should set download boot mode as the highest priority, so:
         * reboot loader/bootloader/fastboot > misc partition "recovery" > reboot xxx.
         */
    	// 0xfd588080
        reg_boot_mode = readl((void *)CONFIG_ROCKCHIP_BOOT_MODE_REG);
        if (reg_boot_mode == BOOT_LOADER) {      // 进入normal系统
                printf("boot mode: loader\n");
                boot_mode = BOOT_MODE_LOADER;
        } else if (reg_boot_mode == BOOT_DFU) {
                printf("boot mode: dfu\n");
                boot_mode = BOOT_MODE_DFU;
        } else if (reg_boot_mode == BOOT_FASTBOOT) {
                printf("boot mode: bootloader\n");
                boot_mode = BOOT_MODE_BOOTLOADER;
        } else if (misc_require_recovery(dev_desc, bcb_sector_offset)) {    // 进入recovery系统
                printf("boot mode: recovery (misc)\n");
                boot_mode = BOOT_MODE_RECOVERY;
        } else {
                switch (reg_boot_mode) {
                case BOOT_NORMAL:
                        printf("boot mode: normal\n");
                        boot_mode = BOOT_MODE_NORMAL;
                        break;
                case BOOT_RECOVERY:
                        printf("boot mode: recovery (cmd)\n");
                        boot_mode = BOOT_MODE_RECOVERY;
                        break;
                case BOOT_UMS:
                        printf("boot mode: ums\n");
                        boot_mode = BOOT_MODE_UMS;
                        break;
                case BOOT_CHARGING:
                        printf("boot mode: charging\n");
                        boot_mode = BOOT_MODE_CHARGING;
                        break;
                case BOOT_PANIC:
                        printf("boot mode: panic\n");
                        boot_mode = BOOT_MODE_PANIC;
                        break;
                case BOOT_WATCHDOG:
                        printf("boot mode: watchdog\n");
                        boot_mode = BOOT_MODE_WATCHDOG;
                        break;
                default:
                        printf("boot mode: None\n");
                        boot_mode = BOOT_MODE_UNDEFINE;
                }
        }

        return boot_mode;
}

参考文章

[1] Mini2440uboot移植之源码分析start.S(一)]

[2] Mini2440之uboot移植之裁剪、分区与环境变量设置(五)

[3] Rockchip RK3399 - 移植linux 5.2.8

[4] Mini2440uboot移植之源码分析命令解析(五)

[5] 基于rk3566uboot分析 - dts加载和dm模型的本质

[6] Rockchip RK3588 - 移植uboot 2017.09 & linux 6.1(友善之家脚本方式)

[7] 树莓派4B(rpi4b)引导ubuntu分析.md

[8] Rockchip RK3399 - busybox 1.36.0制作根文件系统

posted @ 2024-07-14 00:14  大奥特曼打小怪兽  阅读(13)  评论(0编辑  收藏  举报
如果有任何技术小问题,欢迎大家交流沟通,共同进步