Rockchip RK3588 - Rockchip Linux Recovery rkupdate升级

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

开发板 :ArmSoM-Sige7开发板eMMC64GBLPDDR48GB
显示屏 :15.6英寸HDMI接口显示屏u-boot2017.09linux5.10
----------------------------------------------------------------------------------------------------------------------------

如果对Rockchip Linux SDK不了解的前提下,请先阅读以下两篇文章:

一、rkupdate in & mk分析

接下来我们来研究《配置recovery》小节中我们添加了如下配置项究竟实现了什么功能;

#rkupdate升级方式配置项
BR2_PACKAGE_RKUPDATE=y

注意:本节仅仅分析rkupdate升级方式。

在为Buildroot自定义软件包时,在软件包目录下通常为引入.in.mk文件;

root@ubuntu:/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot$ ll package/rockchip/rkupdate/
-rw-r--r--  1 root root  704  6月  9 12:58 Config.in
-rw-r--r--  1 root root   82  6月  9 12:58 rkupdate.hash
-rw-r--r--  1 root root 1017  6月  9 12:58 rkupdate.mk
1.1 Config.in

我们首先从Config.in文件入手,该文件实际上就是定义make menuconfig支持的配置选项;

config BR2_PACKAGE_RKUPDATE
        bool "Rockchip rkupdate for linux"
        depends on BR2_TOOLCHAIN_HAS_THREADS # libpthread-stubs  此配置项依赖于BR2_TOOLCHAIN_HAS_THREADS(其开启的情况下才可用)
        select BR2_PACKAGE_LIBPTHREAD_STUBS   # 如果启动,自动选择启用以下列出的其他软件包和库
        select BR2_PACKAGE_UTIL_LINUX
        select BR2_PACKAGE_UTIL_LINUX_LIBUUID
        help
          Rockchip rkupdate for linux.

if BR2_PACKAGE_RKUPDATE

config BR2_PACKAGE_RKUPDATE_SINGNATURE_FW
        bool "update signature firmware"
        help
                update signature firmware.

config BR2_PACKAGE_RKUPDATE_SIMULATE_ABNORMAL_POWER_OFF
        bool "simulate abnormal power off during updating fw"
        help
                simulate abnormal power off during updating fw.

config BR2_PACKAGE_RKUPDATE_STATIC
        bool "Enable static"
        default y if BR2_STATIC_LIBS
        select BR2_PACKAGE_UTIL_LINUX_STATIC

endif

由于我们勾选了Rockchip rkupdate for linux,因此在生成的output/rockchip_rk3588_recovery/.config配置文件会配置:

BR2_PACKAGE_RKUPDATE=y
1.2 rkupdate.mk

buildroot编译rkupdate所需要的设置rkupdate.mk,包括源码位置、安装目录、权限设置等。

################################################################################
#
# Rockchip rkupdate For Linux
#
################################################################################

RKUPDATE_VERSION = develop
RKUPDATE_SITE = $(TOPDIR)/../external/rkupdate
RKUPDATE_SITE_METHOD = local

RKUPDATE_LICENSE = Apache V2.0
RKUPDATE_LICENSE_FILES = NOTICE

RKUPDATE_DEPENDENCIES = \
        libpthread-stubs util-linux

RKUPDATE_CFLAGS = $(TARGET_CFLAGS) -fPIC -lpthread -luuid

ifeq ($(BR2_PACKAGE_RKUPDATE_SINGNATURE_FW),y)
RKUPDATE_CFLAGS += -DUSE_SIGNATURE_FW=ON
endif

ifeq ($(BR2_PACKAGE_RKUPDATE_SIMULATE_ABNORMAL_POWER_OFF),y)
RKUPDATE_CFLAGS += -DUSE_SIMULATE_POWER_OFF=ON
endif

ifeq ($(BR2_PACKAGE_RKUPDATE_STATIC),y)
RKUPDATE_CFLAGS += -static
endif

define RKUPDATE_BUILD_CMDS
        $(TARGET_MAKE_ENV) $(MAKE) -C $(@D) \
                CXX="$(TARGET_CXX)" CFLAGS="$(RKUPDATE_CFLAGS)"
endef

define RKUPDATE_INSTALL_TARGET_CMDS
        $(INSTALL) -D -m 755 $(@D)/rkupdate $(TARGET_DIR)/usr/bin/
endef

$(eval $(generic-package))
1.2.1 约定配置

首先映入眼帘的是一些约定的配置:

  • RKUPDATE_VERSION:定义了源码的版本号为develop
  • RKUPDATE_SITE:定义了源码下载地址,这里指定为<SDK>/external/rkupdate
  • RKUPDATE_SITE_METHOD:定义了源码下载的方式,该处指定为本地获取(local);
  • RKUPDATE_LICENSE:设置许可证类型为Apache V2.0
  • RKUPDATE_LICENSE_FILES:设置许可证文件NOTICE
1.2.2 编译选项

接下来的是编译选项和依赖关系配置:

(1) RKUPDATE_CFLAGS:编译选项配置,包括开启线程支持、uuid等;

RKUPDATE_CFLAGS = $(TARGET_CFLAGS) -fPIC -lpthread -luuid

其中:

  • -fPICGCC和其他兼容编译器的选项,表示生成位置无关的代码,用于动态链接库。这是因为动态链接库在编译时无法确定加载地址,因此需要生成位置无关的代码;
  • -lpthread:表示链接pthread库,即POSIX线程库,用于多线程编程;
  • -luuid:表示链接uuid库,该库提供了生成和操作通用唯一标识符的函数。

这些选项通常用于构建需要多线程、uuid支持的应用程序或库。在编译时,它们告诉编译器在链接阶段需要使用这些特定的外部库。

(2) RKUPDATE_DEPENDENCIES:定义依赖项,包括libpthread-stubsutil-linux,这样在编译rkupdate软件包的时候才会构建和安装依赖包。

1.2.3 条件编译配置

紧跟着的就是一些条件编译配置项;

ifeq ($(BR2_PACKAGE_RKUPDATE_SINGNATURE_FW),y)
RKUPDATE_CFLAGS += -DUSE_SIGNATURE_FW=ON   # 不走这里
endif

ifeq ($(BR2_PACKAGE_RKUPDATE_SIMULATE_ABNORMAL_POWER_OFF),y)
RKUPDATE_CFLAGS += -DUSE_SIMULATE_POWER_OFF=ON  # 不走这里
endif

ifeq ($(BR2_PACKAGE_RKUPDATE_STATIC),y)
RKUPDATE_CFLAGS += -static # 不走这里
endif

由于这些配置项我们都未开启,因此直接跳过。

1.2.4 CMD命令之BUILD

接着是定义一系列的编译命令,这些_CMD结尾的变量会在buildroot框架编译的时候执行,用于给源码的Makefile传递编译选项和链接选项,调用源码的Makefile

define RKUPDATE_BUILD_CMDS
        $(TARGET_MAKE_ENV) $(MAKE) -C $(@D) \
                CXX="$(TARGET_CXX)" CFLAGS="$(RKUPDATE_CFLAGS)"
endef

RKUPDATE_BUILD_CMDSmake <pkg>build阶段被执行;

(TARGET_MAKE_ENV) $(MAKE) -C $(@D) CC="$(TARGET_CC)" CFLAGS="$(RKUPDATE_CFLAGS)"

这里同updateEngine源码分析,不再重复介绍。其中:

  • MAKE变量值被设置为make
  • CC变量值被设置为$(TARGET_CC)
  • CFLAGS变量值被设置为$(RKUPDATE_CFLAGS)

其中:

TARGET_CC = <SDK>/buildroot/output/rockchip_rk3588_recovery/host/bin/aarch64-buildroot-linux-gnu-gcc

RKUPDATE_CFLAGS = $(TARGET_CFLAGS) -fPIC -lpthread -luuid

我们知道buildroot软件包在编译时会将源码拷贝到output/rockchip_rk3588_recovery/build目录下,rkupdate软件包对应的目录为rkupdate-develop

root@ubuntu:/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot$ ll output/rockchip_rk3588_recovery/build/rkupdate-develop/
-rw-r--r--  1 root root    860  6月  9 12:59 Android.mk
-rwxr-xr-x  1 root root    832  6月 18 01:48 .build.sh*
-rwxr-xr-x  1 root root    158  6月 18 01:48 .configure.sh*
-rw-r--r--  1 root root  13748  6月  9 12:59 CRC.cpp
-rw-r--r--  1 root root   7648  6月 18 01:48 CRC.o
-rw-r--r--  1 root root   9516  6月  9 12:59 DefineHeader.h
-rwxr-xr-x  1 root root    795  6月 18 01:48 .deploy.sh*
-rw-r--r--  1 root root   1170  6月  9 12:59 Endian.h
-rw-r--r--  1 root root      0  6月 18 01:48 .files-list-host.txt
-rw-r--r--  1 root root      0  6月 18 01:48 .files-list-images.txt
-rw-r--r--  1 root root      0  6月 18 01:48 .files-list-staging.txt
-rw-r--r--  1 root root     19  6月 18 01:48 .files-list-target.txt
-rw-r--r--  1 root root     28  6月 18 01:48 .files-list.txt
-rw-r--r--  1 root root   5145  6月  9 12:59 gpt.h
-rw-r--r--  1 root root   1574  6月  9 12:59 LICENSE
-rw-r--r--  1 root root   1435  6月  9 12:59 main.cpp
-rw-r--r--  1 root root   3696  6月 18 01:48 main.o
-rw-r--r--  1 root root    360  6月  9 12:59 Makefile
-rw-r--r--  1 root root  25035  6月  9 12:59 MD5Checksum.cpp
-rw-r--r--  1 root root   4792  6月  9 12:59 MD5ChecksumDefines.h
-rw-r--r--  1 root root  16694  6月  9 12:59 MD5Checksum.h
-rw-r--r--  1 root root  17984  6月 18 01:48 MD5Checksum.o
-rw-r--r--  1 root root   2633  6月  9 12:59 Property.hpp
-rw-r--r--  1 root root 116700  6月  9 12:59 RKAndroidDevice.cpp
-rw-r--r--  1 root root   9743  6月  9 12:59 RKAndroidDevice.h
-rw-r--r--  1 root root  87464  6月 18 01:48 RKAndroidDevice.o
-rw-r--r--  1 root root   8826  6月  9 12:59 RKBoot.cpp
-rw-r--r--  1 root root   2879  6月  9 12:59 RKBoot.h
-rw-r--r--  1 root root   6840  6月 18 01:48 RKBoot.o
-rw-r--r--  1 root root  17886  6月  9 12:59 RKComm.cpp
-rw-r--r--  1 root root   7447  6月  9 12:59 RKComm.h
-rw-r--r--  1 root root  22200  6月 18 01:48 RKComm.o
-rw-r--r--  1 root root  18278  6月  9 12:59 RKDevice.cpp
-rw-r--r--  1 root root   6709  6月  9 12:59 RKDevice.h
-rw-r--r--  1 root root  15728  6月 18 01:48 RKDevice.o
-rw-r--r--  1 root root  10811  6月  9 12:59 RKImage.cpp
-rw-r--r--  1 root root   2494  6月  9 12:59 RKImage.h
-rw-r--r--  1 root root  14488  6月 18 01:48 RKImage.o
-rw-r--r--  1 root root   1365  6月  9 12:59 RKLog.cpp
-rw-r--r--  1 root root    586  6月  9 12:59 RKLog.h
-rw-r--r--  1 root root   6048  6月 18 01:48 RKLog.o
-rwxr-xr-x  1 root root 132992  6月 18 01:48 rkupdate*
-rw-r--r--  1 root root 143360  6月 18 01:48 rkupdate-develop.tar
-rw-r--r--  1 root root      0  6月 18 01:48 .stamp_built
-rw-r--r--  1 root root      0  6月 18 01:48 .stamp_configured
-rw-r--r--  1 root root      0  6月 18 01:48 .stamp_installed
-rw-r--r--  1 root root      0  6月 18 01:48 .stamp_rsynced
-rw-r--r--  1 root root      0  6月 18 01:48 .stamp_target_installed
-rwxr-xr-x  1 root root   2700  6月 18 01:48 .target_install.sh*
-rwxr-xr-x  1 root root    984  6月 18 01:48 .update.sh*
-rw-r--r--  1 root root  37184  6月  9 12:59 Upgrade.cpp
-rw-r--r--  1 root root    440  6月  9 12:59 Upgrade.h
-rw-r--r--  1 root root  41432  6月 18 01:48 Upgrade.o
1.2.5 CMD命令之INSTALL
define RKUPDATE_INSTALL_TARGET_CMDS
        $(INSTALL) -D -m 755 $(@D)/rkupdate $(TARGET_DIR)/usr/bin/
endef

这行命令使用了makefile中定义的INSTALL变量,以及自动变量$(@D)和预定义变量TARGET_DIR。它的作用是将源码rkupdate编译出来的rkupdate可执行文件安装到目标系统的/usr/bin/目录下,并设置相应的权限;

因此如果在recovery软件包编译安装完成后,我们可以在<SDK>/buildroot/output/rockchip_rk3588_recovery/target/usr/bin中看到rkupdate可执行文件;

root@ubuntu:/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot$  ll output/rockchip_rk3588_recovery/target/usr/bin/rkupdate
-rwxr-xr-x 1 root root 106568  6月 18 01:48 output/rockchip_rk3588_recovery/target/usr/bin/rkupdate*
1.2.6 $(eval $(generic-package))

($eval$(generic-package)) 最核心的就是这个东西了,一定不能够漏了,不然源码不会被编译,这个函数就是把整个.mk构建脚本,通过Buildroot框架的方式,展开到Buildroot/目录下的Makfile中,生成的构建目标。

1.3 Makefile

经过前面对rkupdate.mk的分析,我们已经知道rkupdate的源码位于本地<SDK>/external/rkupdate目录;

root@ubuntu:/work/sambashare/rk3588/armsom/armsom-rk3588-bsp$ ll external/rkupdate/
-rw-r--r--  1 root root    860  6月  9 12:59 Android.mk
-rw-r--r--  1 root root  13748  6月  9 12:59 CRC.cpp
-rw-r--r--  1 root root   9516  6月  9 12:59 DefineHeader.h
-rw-r--r--  1 root root   1170  6月  9 12:59 Endian.h
-rw-r--r--  1 root root   5145  6月  9 12:59 gpt.h
-rw-r--r--  1 root root   1574  6月  9 12:59 LICENSE
-rw-r--r--  1 root root   1435  6月  9 12:59 main.cpp
-rw-r--r--  1 root root    360  6月  9 12:59 Makefile
-rw-r--r--  1 root root  25035  6月  9 12:59 MD5Checksum.cpp
-rw-r--r--  1 root root   4792  6月  9 12:59 MD5ChecksumDefines.h
-rw-r--r--  1 root root  16694  6月  9 12:59 MD5Checksum.h
-rw-r--r--  1 root root   2633  6月  9 12:59 Property.hpp
-rw-r--r--  1 root root 116700  6月  9 12:59 RKAndroidDevice.cpp
-rw-r--r--  1 root root   9743  6月  9 12:59 RKAndroidDevice.h
-rw-r--r--  1 root root   8826  6月  9 12:59 RKBoot.cpp
-rw-r--r--  1 root root   2879  6月  9 12:59 RKBoot.h
-rw-r--r--  1 root root  17886  6月  9 12:59 RKComm.cpp
-rw-r--r--  1 root root   7447  6月  9 12:59 RKComm.h
-rw-r--r--  1 root root  18278  6月  9 12:59 RKDevice.cpp
-rw-r--r--  1 root root   6709  6月  9 12:59 RKDevice.h
-rw-r--r--  1 root root  10811  6月  9 12:59 RKImage.cpp
-rw-r--r--  1 root root   2494  6月  9 12:59 RKImage.h
-rw-r--r--  1 root root   1365  6月  9 12:59 RKLog.cpp
-rw-r--r--  1 root root    586  6月  9 12:59 RKLog.h
-rw-r--r--  1 root root  37184  6月  9 12:59 Upgrade.cpp
-rw-r--r--  1 root root    440  6月  9 12:59 Upgrade.h

首先我们需要了解源码的Makefile是怎么样的,其内容如下;

点击查看代码
PROJECT_DIR := $(shell pwd)
PROM = rkupdate
OBJ =CRC.o \
        MD5Checksum.o \
        RKAndroidDevice.o \
        RKBoot.o \
        RKComm.o \
        RKDevice.o \
        RKImage.o \
        RKLog.o \
        Upgrade.o \
        main.o

$(PROM): $(OBJ)
        $(CXX) -o $(PROM) $(OBJ) $(CFLAGS)

%.o: %.cpp
        $(CXX) -c $< -o $@ $(CFLAGS)

clean:
        rm -rf $(OBJ) $(PROM)

install:
        sudo install -D -m 755 $(PROJECT_DIR)/rkupdate -t $(DESTDIR)/usr/bin/

这段脚本写的比较简单,在编译rkupdate软件包的build阶段,会执行如下命令;

(TARGET_MAKE_ENV) $(MAKE) -C $(@D) CC="$(TARGET_CC)" CFLAGS="$(RKUPDATE_CFLAGS)"

其中:

  • CC<SDK>/buildroot/output/rockchip_rk3588_recovery/host/bin/aarch64-buildroot-linux-gnu-gcc
  • CFLAGS$(TARGET_CFLAGS) -fPIC -lpthread -luuid
1.3.1 变量定义

脚本开始定义了一些变量:

  • PROJECT_DIR:既时变量,值在定义时就确定,为当前工作路径;
  • PROM:延时变量,值在使用的时候才确定,这里设置为rkupdate
1.3.2 目标rkupdate

第一个目标rkupdate,是由若干个.o文件通过aarch64-buildroot-linux-gnu-gcc编译器链接生成的可执行文件。

OBJ =CRC.o \
        MD5Checksum.o \
        RKAndroidDevice.o \
        RKBoot.o \
        RKComm.o \
        RKDevice.o \
        RKImage.o \
        RKLog.o \
        Upgrade.o \
        main.o

$(PROM): $(OBJ)
        $(CXX) -o $(PROM) $(OBJ) $(CFLAGS)

.o文件实际上是由.c文件通过aarch64-buildroot-linux-gnu-gcc编译器编译生成的。

%.o: %.cpp
        $(CXX) -c $< -o $@ $(CFLAGS)
1.3.3 目标clean

伪目标clean用于执行清理工作;

clean:
        rm -rf $(OBJ) $(PROM)
1.3.4 目标install

伪目标install用于执行安装工作;

install:
        sudo install -D -m 755 $(PROJECT_DIR)/rkupdate -t $(DESTDIR)/usr/bin/
1.4 编译

在《Rockchip RK3588 - Rockchip Linux SDK Buildroot文件系统构建》中介绍过buildroot编译命令;

root@ubuntu:/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot$ sudo make -j8

或者使用如下命令单独编译rkupdate软件包:

root@ubuntu:/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot$ sudo make rkupdate-dirclean
root@ubuntu:/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot$ sudo make rkupdate

其中rkupdate编译日志如下:

点击查看代码
>>> rkupdate develop Syncing from source dir /work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/../external/rkupdate
rsync -au --chmod=u=rwX,go=rX  --exclude .svn --exclude .git --exclude .hg --exclude .bzr --exclude CVS /work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/../external/rkupdate/ /work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/build/rkupdate-develop
>>> rkupdate develop Configuring
>>> rkupdate develop Building
PATH="/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/host/bin:/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/host/sbin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin" /usr/bin/make  -C /work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/build/rkupdate-develop CXX="/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/host/bin/aarch64-buildroot-linux-gnu-g++" CFLAGS="-D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64  -Os -g0 -D_FORTIFY_SOURCE=1 -fPIC -lpthread -luuid"
make[1]: 进入目录“/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/build/rkupdate-develop”
/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/host/bin/aarch64-buildroot-linux-gnu-g++ -c CRC.cpp -o CRC.o -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64  -Os -g0 -D_FORTIFY_SOURCE=1 -fPIC -lpthread -luuid
/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/host/bin/aarch64-buildroot-linux-gnu-g++ -c MD5Checksum.cpp -o MD5Checksum.o -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64  -Os -g0 -D_FORTIFY_SOURCE=1 -fPIC -lpthread -luuid
/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/host/bin/aarch64-buildroot-linux-gnu-g++ -c RKAndroidDevice.cpp -o RKAndroidDevice.o -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64  -Os -g0 -D_FORTIFY_SOURCE=1 -fPIC -lpthread -luuid
/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/host/bin/aarch64-buildroot-linux-gnu-g++ -c RKBoot.cpp -o RKBoot.o -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64  -Os -g0 -D_FORTIFY_SOURCE=1 -fPIC -lpthread -luuid
/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/host/bin/aarch64-buildroot-linux-gnu-g++ -c RKComm.cpp -o RKComm.o -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64  -Os -g0 -D_FORTIFY_SOURCE=1 -fPIC -lpthread -luuid
/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/host/bin/aarch64-buildroot-linux-gnu-g++ -c RKDevice.cpp -o RKDevice.o -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64  -Os -g0 -D_FORTIFY_SOURCE=1 -fPIC -lpthread -luuid
/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/host/bin/aarch64-buildroot-linux-gnu-g++ -c RKImage.cpp -o RKImage.o -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64  -Os -g0 -D_FORTIFY_SOURCE=1 -fPIC -lpthread -luuid
/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/host/bin/aarch64-buildroot-linux-gnu-g++ -c RKLog.cpp -o RKLog.o -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64  -Os -g0 -D_FORTIFY_SOURCE=1 -fPIC -lpthread -luuid
/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/host/bin/aarch64-buildroot-linux-gnu-g++ -c Upgrade.cpp -o Upgrade.o -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64  -Os -g0 -D_FORTIFY_SOURCE=1 -fPIC -lpthread -luuid
/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/host/bin/aarch64-buildroot-linux-gnu-g++ -c main.cpp -o main.o -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64  -Os -g0 -D_FORTIFY_SOURCE=1 -fPIC -lpthread -luuid
/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/host/bin/aarch64-buildroot-linux-gnu-g++ -o rkupdate CRC.o MD5Checksum.o RKAndroidDevice.o RKBoot.o RKComm.o RKDevice.o RKImage.o RKLog.o Upgrade.o main.o -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64  -Os -g0 -D_FORTIFY_SOURCE=1 -fPIC -lpthread -luuid
make[1]: 离开目录“/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/build/rkupdate-develop”
>>> rkupdate develop Installing to target
/usr/bin/install -D -m 755 /work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/build/rkupdate-develop/rkupdate /work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/target/usr/bin/

在日志中输出了rkupdate软件包构建的各个阶段的信息,可以验证我们之前的分析是否正确。比如编译命令:

PATH="/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/host/bin:/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/host/sbin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin" /usr/bin/make  -C /work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/build/rkupdate-develop CXX="/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/host/bin/aarch64-buildroot-linux-gnu-g++" CFLAGS="-D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64  -Os -g0 -D_FORTIFY_SOURCE=1 -fPIC -lpthread -luuid"

二、rkupdate源码分析

由于rkupdate可执行文件是由 MD5Checksum.cRKAndroidDevice.cRKBoot.cRKComm.cRKDevice.cRKImage.cRKLog.cUpgrade.cmain.c文件编译而成,程序的入口为main.cpp文件。

2.1 main.cpp

main函数实现如下:

FILE *cmd_pipe = NULL;
int sdBootUpdate = 0;

int main(int argc, char *argv[])
{
    int status;

    setbuf(stdout, NULL);
    setbuf(stderr, NULL);
    if (argc != 5)
    {
        printf("unexpected number of arguments (%d)\n", argc);
        fprintf(stderr, "unexpected number of arguments (%d)\n", argc);
        return 1;
    }
    int fd = atoi(argv[2]);
    cmd_pipe = fdopen(fd, "wb");
    setlinebuf(cmd_pipe);

    char *filepath = argv[3];
    sdBootUpdate = atoi(argv[4]);

    //call update
    bool bRet = do_rk_firmware_upgrade(filepath, (void *)handle_upgrade_callback,
                                       (void *)handle_upgrade_progress_callback);

    if (!bRet)
    {
        status = INSTALL_ERROR;
    }
    else
    {
        status = INSTALL_SUCCESS;
    }

    sleep(5);
    sync();
    return status;
}

函数接收5个参数:

  • 第一个参数为当前可执行文件,即rkupdate
  • 第二个参数:
  • 第三个参数是文件描述符fd
  • 第四个参数为升级的固件文件filepath,比如/userdata/update.img
  • 第五个参数为sdBootUpdate:指明当前设备启动方式是否为SD启动;

main函数内部主要调用do_rk_firmware_upgrade来实现固件的升级,函数接收4个参数;

  • szFw:需要升级的固件文件路径;
  • pCallback:固件升级回调函数;
  • pProgressCallback:固件升级进度回调函数;
  • szBootDev:设备是否是SD卡启动;
2.2 Upgrade.cpp

do_rk_firmware_upgrade函数位于Upgrade.cpp文件;

UpgradeCallbackFunc g_callback = NULL;
UpgradeProgressCallbackFunc g_progress_callback = NULL;

extern int sdBootUpdate;
bool do_rk_firmware_upgrade(char *szFw, void *pCallback, void *pProgressCallback, char *szBootDev)
{
    bool bSuccess = false, bRet = false, bLock;
    int iRet;
    CRKImage *pImage = NULL;
    CRKLog *pLog = NULL;
    CRKAndroidDevice *pDevice = NULL;
    CRKComm *pComm = NULL;
    STRUCT_RKDEVICE_DESC device;
    BYTE key[514];
    UINT nKeySize = 514;
    BYTE uid[RKDEVICE_UID_LEN];
    tstring strFw = szFw;
    tstring strUid;
    bool bUpdateLoader = true;

    // 1. 设置了全局的回调函数,并立即调用进度回调函数报告0.1的进度和10秒的耗时。
    g_callback = (UpgradeCallbackFunc)pCallback;
    g_progress_callback = (UpgradeProgressCallbackFunc)pProgressCallback;
    if (g_progress_callback)
    {
        g_progress_callback(0.1, 10);
    }

    // 2. 初始化日志对象pLog用于记录升级过程中的信息
    pLog = new CRKLog();
    if (!pLog)
    {
        goto EXIT_UPGRADE;
    }
    pLog->Record("Start to upgrade firmware...");
    
    // 3. 创建固件镜像对象pImage
    pImage = new CRKImage(strFw, bRet);
    if (!bRet)
    {
        pLog->Record("ERROR:do_rk_firmware_upgrade-->new CRKImage failed!");
        goto EXIT_UPGRADE;
    }
    
    // 4. 创建通信对象pComm用于后续的设备交互
    pComm = new CRKUsbComm(pLog);
    if (!pComm)
    {
        pLog->Record("ERROR:do_rk_firmware_upgrade-->new CRKComm failed!");
        goto EXIT_UPGRADE;
    }

    // 5. 创建设备对象pDevice	
    pDevice = new CRKAndroidDevice(device);
    if (!pDevice)
    {
        pLog->Record("ERROR:do_rk_firmware_upgrade-->new CRKAndroidDevice failed!");
        goto EXIT_UPGRADE;
    }
    pDevice->SetObject(pImage, pComm, pLog);

    // 6. 如果设备不是eMMC闪存,则生成并记录UUID。
    if (!pComm->RKU_IsEmmcFlash())    //chad.ma if is Emmc flash don't create UUID.
    {
        if (CreateUid(uid))
        {
            pDevice->Uid = uid;
            pLog->PrintBuffer(strUid, uid, RKDEVICE_UID_LEN);
            pLog->Record("uid:%s", strUid.c_str());
        }
    }

    // 7. 获取闪存信息
    pDevice->m_pCallback = (UpgradeCallbackFunc)pCallback;
    pDevice->m_pProcessCallback = (UpgradeProgressCallbackFunc)pProgressCallback;
    pLog->Record("Get FlashInfo...");
    bRet = pDevice->GetFlashInfo();
    if (!bRet)
    {
        pLog->Record("ERROR:do_rk_firmware_upgrade-->GetFlashInfo failed!");
        goto EXIT_UPGRADE;
    }

    // 8. 判断升级的固件是否存在bootloader
    bUpdateLoader = pDevice->IsExistBootloaderInFw();

    if (IsRK3308_Platform() && Compatible_rk3308bs_loader())
    {
   		// rk3308 才会进入
        .......
    }

    #ifndef USE_SIGNATURE_FW           // 未定义进入
    if (bUpdateLoader)        // 更新bootloader
    {
        printf("############### update bootloader start ############\n");

        pLog->Record("IDBlock Preparing...");
        printf("\t\t############### IDBlock Preparing...\n");
        iRet = pDevice->PrepareIDB();
        if (iRet != ERR_SUCCESS)
        {
            pLog->Record("ERROR:do_rk_firmware_upgrade-->PrepareIDB failed!");
            goto EXIT_UPGRADE;
        }
        pLog->Record("IDBlock Writing...");
        printf("\t\t############### IDBlock Writing...\n");
        iRet = pDevice->DownloadIDBlock();
        if (iRet != ERR_SUCCESS)
        {
            pLog->Record("ERROR:do_rk_firmware_upgrade-->DownloadIDBlock failed!");
            goto EXIT_UPGRADE;
        }
        printf("############### update bootloader Success############\n");

        if (strFw.find(_T(".bin")) != tstring::npos)
        {
            pLog->Record("INFO:do_rk_firmware_upgrade-->Download loader only success!");
            bSuccess = true;
            return bSuccess;
        }
    }

    // 9. 下载固件
    iRet = pDevice->DownloadImage();
    if (iRet != ERR_SUCCESS)
    {
        pLog->Record("ERROR:do_rk_firmware_upgrade-->DownloadImage failed!");
        goto EXIT_UPGRADE;
    }

    #else                 // 不会进入
    printf("use signature firmware to update.\n");
	......
    #endif

    bSuccess = true;

EXIT_UPGRADE:
    if (bSuccess)
    {
        pLog->Record("Finish to upgrade firmware.");
    }
    else
    {
        pLog->Record("Fail to upgrade firmware!");
    }
    if (pLog)
    {
        delete pLog;
        pLog = NULL;
    }
    if (pImage)
    {
        delete pImage;
        pImage = NULL;
    }
    if (pDevice)
    {
        delete pDevice;
        pDevice = NULL;
    }
    else
    {
        if (pComm)
        {
            delete pComm;
            pComm = NULL;
        }
    }

    return bSuccess;
}

三、系统升级测试

3.1 buildroot系统升级

首先我们需要将我们制作的统一固件update.img(这里采用的buildroot系统)烧录到开发板eMMC中,具体烧录步骤可以参考《Rockchip RK3588 - Rockchip Linux SDK编译》。

需要注意的是:我们烧录的buildroot文件系统要按照本篇博客第一节的配置进行编译,即编译出来的rootfs.imgrecovery.img包含用于升级的updateEnginerkupdate可执行文件;

root@rk3588-buildroot:~# ls /usr/bin/updateEngine -l
-rwxr-xr-x    1 root     root         80544 Jun 17 17:48 /usr/bin/updateEngine
root@rk3588-buildroot:~# ls /usr/bin/rkupdate -l
-rwxr-xr-x    1 root     root        106568 Jun 17 17:48 /usr/bin/rkupdate
3.1.1 制作升级固件

按照正常的固件编译流程,制作用于升级的update.img固件,可以参考《Rockchip RK3588 - Rockchip Linux SDK编译》。

升级固件不一定要全分区升级,可修改 package-file 文件,将不要升级的分区去掉,这样可以减少升级包的大小。

例如,执行如下命令(可参考文件tools/linux/Linux_Pack_Firmware/rockdev/rk3588-package-file):

root@ubuntu:/work/sambashare/rk3588/armsom/armsom-rk3588-bsp$ sudo ./build.sh edit-package-file

rootfs的相对路径改为 RESERVED,这样就不会打包根文件系统,即不升级根文件系统分区。

# NAME  PATH
package-file    package-file
parameter       parameter.txt
bootloader      MiniLoaderAll.bin
uboot   uboot.img
misc    misc.img
boot    boot.img
recovery        recovery.img
backup  RESERVED
rootfs  rootfs.img
oem     oem.img
userdata  RESERVED

注意: 若将升级固件放至设备的/userdata/目录,则不要打包userdata.img,需要将image/userdata.img改为RESERVED

执行如下命令生成统一固件;

root@ubuntu:/work/sambashare/rk3588/armsom/armsom-rk3588-bsp$ sudo ./build.sh updateimg

将制作好的升级固件拷贝到U盘、TF卡或者开发版的/userdata/目录下。

我们可以将统一镜像复制到/work/tftpwork目录下:

root@ubuntu:/work/sambashare/rk3588/armsom/armsom-rk3588-bsp$ cp ./rockdev/update.img /work/tftpboot/
3.1.2 rkupdate升级测试

rkupdate升级流程如下:

  • 将升级固件update.img放在SD卡或U盘根目录或者设备的/userdata目录下;
  • normal系统下执行升级程序update ota /xxx/update.img
  • 升级recovery分区;
  • 重启;
  • 设备将会进入recovery模式,并进行升级;
  • 升级成功后会reboot到正常的normal系统;

可使用的路径如下:

  • U盘的挂载路径:/udisk
  • sdcard的挂载路径:/mnt/sdcard//sdcard
  • eMMC的挂载路径:/userdata/

升级流程图如下:

接下来我们进行测试,开发板从ubuntu tftp服务器下载统一镜像:

root@rk3588-buildroot:/userdata# tftp -g -l update.img 192.168.0.200

然后执行rkupdate升级命令:

root@rk3588-buildroot:/userdata# update ota /userdata/update.img

等待升级完成,升级成功后开发版会重新启动进入系统。

3.2 debian/ubuntu系统升级

Buildroot recovery升级一样,该Recovery OTA升级方案也支持DebianUbuntu系统下的升级。

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

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

具体修改方式如下:

buildroot/output/rockchip_rkxxxx/target/lib/udev/rules.d/61-partition-init.rules 
或者
buildroot/output/rockchip_rkxxxx_recovery/target/lib/udev/rules.d/61-partitioninit.rules

拷贝到DebianUbuntu系统下相关的位置,如rootfs/overlay-debug/lib/udev/rules.d/下。此处rkxxxx为具体某一rk芯片平台(RK3308RK3328RK3399RK3326等)。修改的目的就是开机启动后可以将Debian系统或Ubuntu系统中各个分区节点形如/dev/mmcblk0p0/dev/mmcblk0p1/dev/mmcblk0p2/dev/mmcblk0p3 ... 修改为/dev/block/byname/uboot /dev/block/by-name/misc/dev/block/by-name/boot/dev/block/byname/ rootfs ...等。

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

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/

参考文章

[1] Rockchip Linux updateEngine升级方案介绍

[2] Rockchip Linux Recovery升级开发指南

[3] 嵌入式Linux开发之Makefile

Rockchip RK3566是一款由Rockchip推出的高性能应用处理器,其集成了四核ARM Cortex-A55 CPU和ARM Mali-G52 GPU。在移植U-Boot 2023.04时,我们需要考虑以下几个方面的工作: 1. 了解RK3566芯片的硬件架构和技术规格,包括处理器核心、内存控制器、外设接口等。这将有助于理解U-Boot如何与硬件交互,并进行相应的配置。 2. 下载并准备U-Boot 2023.04的源代码。在Rockchip官方网站或开源社区中可以找到最新的U-Boot源代码。将其下载并解压到开发机上。 3. 设置交叉编译环境。因为U-Boot是一个跨平台的项目,所以需要配置适合RK3566的交叉编译器,确保能够正确编译U-Boot源代码。 4. 配置U-Boot。根据RK3566的硬件架构和技术规格,需要进行相应的配置,包括处理器、内存、外设等设置。这些设置在U-Boot的配置文件中进行,可以根据需求进行修改。 5. 编译U-Boot。在配置好U-Boot后,使用交叉编译器编译U-Boot源代码。编译完成后,将生成的U-Boot二进制文件烧录到RK3566的启动设备上,如eMMC或SD卡。 6. 测试U-Boot。将准备好的启动设备插入RK3566开发板中,根据开发板的启动方式,进入U-Boot命令行界面。在命令行界面中可以进行各种操作和调试,如加载内核、启动操作系统等。 7. 调试和优化。在移植和测试U-Boot过程中,可能会出现一些问题和不稳定的情况。需要通过调试和优化来解决这些问题,确保U-Boot的正常运行和稳定性。 总之,移植U-Boot 2023.04到Rockchip RK3566需要了解芯片的硬件架构和技术规格,配置和编译U-Boot源代码,进行测试和调试。这样可以确保U-Boot能够与RK3566正常交互,并为后续的操作系统加载和启动提供基础支持。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Graceful_scenery

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值