----------------------------------------------------------------------------------------------------------------------------
开发板 :ArmSoM-Sige7
开发板eMMC
:64GB
LPDDR4
:8GB
显示屏 :15.6
英寸HDMI
接口显示屏u-boot
:2017.09
linux
:5.10
----------------------------------------------------------------------------------------------------------------------------
如果对Rockchip Linux SDK
不了解的前提下,请先阅读以下两篇文章:
- 《
Rockchip RK3588 - Rockchip Linux SDK
编译》; - 《
Rockchip RK3588 - Rockchip Linux SDK Buildroot
文件系统构建》。
一、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
其中:
-fPIC
是GCC
和其他兼容编译器的选项,表示生成位置无关的代码,用于动态链接库。这是因为动态链接库在编译时无法确定加载地址,因此需要生成位置无关的代码;-lpthread
:表示链接pthread
库,即POSIX
线程库,用于多线程编程;-luuid
:表示链接uuid
库,该库提供了生成和操作通用唯一标识符的函数。
这些选项通常用于构建需要多线程、uuid
支持的应用程序或库。在编译时,它们告诉编译器在链接阶段需要使用这些特定的外部库。
(2) RKUPDATE_DEPENDENCIES
:定义依赖项,包括libpthread-stubs
、util-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_CMDS
在make <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.c
、RKAndroidDevice.c
、RKBoot.c
、RKComm.c
、RKDevice.c
、RKImage.c
、RKLog.c
、Upgrade.c
、main.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.img
、recovery.img
包含用于升级的updateEngine
、rkupdate
可执行文件;
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
升级方案也支持Debian
或Ubuntu
系统下的升级。
由于Recovery
模式下升级需要通过设备各个分区节点来识别并写入不同设备分区节点的固件数据,Buildroot
系统是通过udev
中的别名方式(by-name
)来对设备分区节点做了通用的易识别的处理。
Debian
或Ubuntu
系统中因为缺少这样的方式,导致了实际中Recovery
不能正常运行的情况,所以只需要将Debian
或Ubuntu
系统中设备分区的节点也跟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
拷贝到Debian
或Ubuntu
系统下相关的位置,如rootfs/overlay-debug/lib/udev/rules.d/
下。此处rkxxxx
为具体某一rk
芯片平台(RK3308
、RK3328
、RK3399
、RK3326
等)。修改的目的就是开机启动后可以将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
放在Debian
或Ubuntu /etc/udev/rules.d
或/lib/udev/rules.d/
。
参考文章
[1] Rockchip Linux updateEngine
升级方案介绍
[2] Rockchip Linux Recovery
升级开发指南
[3] 嵌入式Linux
开发之Makefile