rk3568 A/B系统 OAT升级 实践

        在阅读本文档之前,读者可以参考瑞芯微官方文档参考文档:《Linux A/B System》。所谓的A/B System即把系统固件分为两份,系统可以从其中的一个slot上启动。当一份启动失败后可以从另份启动,同时升级时可以直接将固件拷贝到另一个slot上而无需进入系统升级模式。AB system 不考虑支持 recovery 升级。

1、修改分区文件parameter-buildroot-fit-ab.txt

      首先需要根据rk3568外挂的存储设备的大小以及需求来划分分区,因为只有将分区划分清楚后,才能开展后面的工作。A/B系统,意味着至少kernel,rootfs至少存在kernel_a,kernle_b,rootfs_a和rootfs_b分区。以笔者的设备为例,因为这是个存储设备,外挂的emmc容量很大,emmc划分的分区信息如下,如果没有对uboot升级的需求,其实uboot_b分区没有存在的意义。

    

       sdk默认使用的分区表是parameter-buildroot-fit.txt,为了区分,A/B分区使用的分区表为parameter-buildroot-fit-ab.txt。

        为了使的配置一开始就有效,将下面的配置放到对应的配置文件中即可,笔者的配置文件路径是:rk3568_bsp/device/rockchip/.chips/rk3566_rk3568/rockchip_rk3568_evb1_ddr4_v10_defconfig文件。读者根据自己的项目配置修改对应文件即可。

RK_PARAMETER="parameter-buildroot-fit-ab.txt"

2、uboot添加配置

增加uboot配置项如下:

        CONFIG_AVB_LIBAVB=y

        CONFIG_AVB_LIBAVB_AB=y

        CONFIG_AVB_LIBAVB_ATX=y

        CONFIG_AVB_LIBAVB_USER=y

        CONFIG_RK_AVB_LIBAVB_USER=y

        CONFIG_ANDROID_AB=y(默认未配置)

上面配置主要是使得spl程序能够根据标识去加载对用的uboot和kernel分区。

3、A/B系统分区挂载问题

        rk3568SDK中默认帮助我们划分了两个分区:userdata和oem。userdata用于存放可读写的内容如程序所需的配置文件,以及其他资源。oem分区是只读分区,用于存放用户只读的内容如可执行程序,库文件等。

        由于瑞芯微配置了oem和userdata分区,而在A/B分区方案中,修改为oem_a/_b和userdata_a/_b导致配置的/etc/fstab文件错误,错误挂载了分区信息。

        /etc/fstab内容如下,会自动挂载oem和userdata分区到/oem和/userdata。

          由于瑞芯微SDK中会将默认创建的userdata和oem分区自动添加到/etc/fstab文件中,该文件用于存放分区挂载文件信息,设备上电后会就会自动进行分区的挂载(/etc/init.d/S00mountall.sh文件来挂载分区,感兴趣的读者可以阅读该脚本)。

etc/init.d/S00mountall.sh 内容如下:
#!/bin/sh
### BEGIN INIT INFO
# Provides:       mount-all
# Required-Start:
# Required-Stop:
# Default-Start:  S
# Default-Stop:
# Description:    Mount all internal partitions in /etc/fstab
### END INIT INFO

case "$1" in
	start|"")
		# 执行mount-helper函数处理挂载问题
		mount-helper
		;;
	restart|reload|force-reload)
		echo "Error: argument '$1' not supported" >&2
		exit 3
		;;
	stop|status)
		# No-op
		;;
	*)
		echo "Usage: start" >&2
		exit 3
		;;
esac

:
mount-helper脚本内容如下:
#!/bin/sh
### BEGIN INIT INFO
# Provides:       mount-all
# Default-Start:  S
# Default-Stop:
# Description:    Mount all internal partitions in /etc/fstab
### END INIT INFO

# Don't exit on error status
set +e

# Uncomment below to see more logs
# set -x

. $(dirname $0)/disk-helper

LOGFILE=/var/log/mount-all.log

BASIC_FSTYPE="proc devtmpfs devpts tmpfs sysfs configfs debugfs pstore"

do_part()
{
	# Not enough args
	[ $# -lt 6 ] && return
	# <file system> <mount pt>      <type>  <options>       <dump>  <pass>

	DEV=${1##*=}  #删除最长匹配*=的头部模式,将$1从头部开始最长匹配=之前包括=一起删除
	MOUNT_POINT=$2
	FSTYPE=$3
	OPTS=$4
	#跳过fsck
	PASS=$6 # Skip fsck when pass is 0

	# Setup check/mount tools and do some prepare
	# 检查分区是否存在等准备工作
	prepare_part || return

	# Check and repair partition
	# 是否进行fsck操作
	check_part

	# Mount partition
	# 挂载分区
	mount_part || return

	# Resize partition if needed
	resize_part

	# Restore ro/rw
	# 恢复原来的状态
	remount_part $MOUNT_RO_RW
}

mount_all()
{
	# 根目录下的.auto_mkfs文件,用于自动格式化分区
	AUTO_MKFS="/.auto_mkfs"
	if [ -f $AUTO_MKFS ];then
		echo "Note: Will auto format partitons, remove $AUTO_MKFS to disable"
	else
		unset AUTO_MKFS
	fi

	# 根目录下的.skip_fsck文件,用于跳过fsck
	SKIP_FSCK="/.skip_fsck"
	if [ -f $SKIP_FSCK ];then
		echo "Note: Will skip fsck, remove $SKIP_FSCK to enable"
	else
		echo "Note: Create $SKIP_FSCK to skip fsck"
		echo " - The check might take a while if didn't shutdown properly!"
		unset SKIP_FSCK
	fi

	# Ignore basic file systems
	ID=0
	# -w      Match whole words only,完全匹配
	# -v      Select non-matching lines,选中非匹配项
	# -E      PATTERN is an extended regexp,使用扩展正则表达式,因为使用了|符号
	# grep查找 /etc/fstab中#或$BASIC_FSTYPE中内容开头以外的项
	# read 读取一行到LINE中
	grep -vwE "^#|${BASIC_FSTYPE// /|}" /etc/fstab | while read LINE;do
		#进行挂载处理
	do_part $LINE&
	ID=$(( $ID + 1 ))
done
wait
}

case "$1" in
	start|"")
		echo "Start mounting all internal partitions in /etc/fstab"
		echo "Log saved to $LOGFILE"

		# Mount basic file systems firstly
		# ${var//pattern/repl}替换所有匹配的pattern为repl
		# 将BASIC_FSTYPE中的空格替换为逗号(,)
		#  -a, --all  mount all filesystems mentioned in fstab
		#  -t, --types <list>      limit the set of filesystem types
		# mount -a -t list, 挂载fstab中类型在BASIC_FSTYPE中的项
		mount -a -t "$(echo "${BASIC_FSTYPE// /,}")"

		# 处理剩下分区的挂载,将过程中的输出既输出到终端又保存到文件LOGFILE中
		mount_all 2>&1 | tee $LOGFILE
				
		;;
	*)
		echo "Usage: mount-helper start" >&2
		exit 3
		;;
esac

:

         由于我们修改了userdata和oem的分区名为 userdata_a/b,oem_a/b。但是SDK并不知道,所以需要修改SDK对应脚本中的内容,能够动态修改/etc/fstab中的内容,当从A分区系统启动时挂载userdata_a,oem_a分区,当从B分区系统启动时挂载userdata_b,oem_b分区。当然整体而言也包括kernel和rootfs的分区信息也需要修改。

        笔者的处理方法是sdk编译时默认不让添加userdata和oem的挂载分区信息到/etc/fstab文件中。设备上电后在mount-helper脚本中根据uboot传递到/proc/cmdline中表示使用A/B系统中的哪个系统的标志(android_slotsufix=_a或android_slotsufix=_b)动态的添加usetdat和oem分区的内容到/etc/fstab文件中。

动态设置挂载userdata_a/b,oem_a/b分区到/etc/fstab文件如下:
add_part()
{
	userdata_a="/dev/mmcblk0p9\t/userdata\text4\tdefaults\t0\t0" 
	oem_a="/dev/mmcblk0p11\t/oem\text4\trelatime,ro\t0\t0"
	
	userdata_b="/dev/mmcblk0p10\t/userdata\text4\tdefaults\t0\t0"
	oem_b="/dev/mmcblk0p12\t/oem\text4\trelatime,ro\t0\t0" 

	slotsufix=$(cat "/proc/cmdline" | grep -o 'android_slotsufix=_\w')	
	if [ -n $slotsufix ] ; then
		slot=$(echo $slotsufix | cut -d'=' -f2)
	fi	
	
	if [ "$slot" == "_a" ] ; then
		oem=${oem_a}
		userdata=${userdata_a}
	elif [ "$slot" == "_b" ] ; then
		oem=${oem_b}
		userdata=${userdata_b}
	else
		echo "slotsufix error,using default _a"
		oem=${oem_a}
		userdata=${userdata_a}
	fi   

	# 由于字符串中存在\t转义字符,grep不会处理其中的转义字符,使用printf来处理转义字符
	value_oem=$(grep "$(printf $oem)" /etc/fstab || echo "Not found")
	value_userdata=$(grep $"$(printf $userdata)" /etc/fstab || echo "Not found")

	if [ "$value_oem" == "Not found" -o "$value_userdata" == "Not found" ] ; then
		echo "Not find oem or userdata partitons,will create it"
		echo -e "$oem" 
		echo -e "$userdata" 
		sed -i '/oem/d' 		/etc/fstab
		sed -i '/userdata/d' 	/etc/fstab
		echo -e "$oem" >> /etc/fstab
		echo -e "$userdata" >> /etc/fstab
	fi
}

        为什么我们非要将动态分区的挂载放在/etc/fstab中来处理?我们可以自己在程序或者脚本中使用mount函数来挂载分区不行吗? 其实都是可以的,主要是我们想偷个懒。因为使用瑞芯微的挂载脚本其可以帮我们处理resize的问题。

        以笔者的设备为例,SDK编译出来的userdata.img只有几百K,但是userdata_a/b分区的大小是100M。如果不进行resize处理的话,通过df查看的userdata分区就是镜像文件的大小几百K。为了让这个分区的大小为期望的100M,需要进行resize操作。如果对resize感兴趣的同学可以阅读resize相关的脚本,在脚本中会探测分区有没有被resize过,如果没有就进行resize操作,resize完成后在分区中存放一个隐藏文件,表明该分区被resize过。后面设备再上电只要检测到该隐藏文件就跳过resize操作,这里不再继续进行展开,大家知道有这么个事情就行,点到即止。

        resize后分区情况如下:

         

4、其他分区配置

        笔者的设备上将剩余的分区都配置到data分区中。

        在sdk目录下执行make menuconfig配置来设置分区自定义的分区:

最终生成的配置信息如下:

RK_EXTRA_PARTITION_NUM=3
#
# Extra partition 3
#
RK_EXTRA_PARTITION_3_NAME="data"
RK_EXTRA_PARTITION_3_DEV="auto"
RK_EXTRA_PARTITION_3_MOUNTPOINT="/home/userspace"
RK_EXTRA_PARTITION_3_FSTYPE="ext4"
RK_EXTRA_PARTITION_3_OPTIONS="defaults"
RK_EXTRA_PARTITION_3_SRC="normal"
RK_EXTRA_PARTITION_3_SIZE="auto"
# RK_EXTRA_PARTITION_3_BUILTIN is not set
RK_EXTRA_PARTITION_3_FEATURES="${RK_EXTRA_PARTITION_3_BUILTIN:+builtin}"
RK_EXTRA_PARTITION_3_STR="${RK_EXTRA_PARTITION_3_DEV:-auto}:$RK_EXTRA_PARTITION_3_NAME:$RK_EXTRA_PARTITION_3_MOUNTPOINT:$RK_EXTRA_PARTITION_3_FSTYPE:$RK_EXTRA_PARTITION_3_OPTIONS:${RK_EXTRA_PARTITION_3_SRC// /,}:$RK_EXTRA_PARTITION_3_SIZE:$RK_EXTRA_PARTITION_3_FEATURES"

将上面的配置存放到sdk的配置文件中就能一开始就生效。

5、ota升级

        将需要升级的分区的镜像文件下载到本地后,如果当前系统运行在A分区,则需要刷写B系统对应的分区。刷写分区时使用open,read,write,fsync等系统调用操作/dev/mmcblk0px分区即可。所有待升级的分区刷写成功后,需要修改misc分区中的标志。这部分在瑞芯微的官方文档中有详细描述这里不再赘述。

        正常情况下只有ota升级成功后才会去设置切面标志,使设备ota后从另一面启动。比如当前设备在A面,ota时烧录的是B面,烧录成功后将设置启动面B面,下次设备上电后从B面启动。OTA升级完成了,从B面启动,但是之前升级包里面的内容存在bug,导致设备卡死在kernel某个位置,这时就无法回退到A面。我们需要设计一种回退机制,使得设备在某个面启动过程中出现问题时,被外部看门狗重启后,设备能够切换到另外一面。瑞芯微在uboot中提供的reset retry模式能够解决上述问题。

        为了在进行烧录时同时将A/B系统都进行烧录,需要设置如下配置:

        RK_AB_UPDATE=y

您好!感谢您的提问。根据您的描述,这是一个关于PackageManager的错误日志,表明在解析 /system/framework/oat 目录时出现了问题,缺少了基本 APK 文件。 在Android系统中,oat目录存储了经过优化的dex文件,以提高应用程序的运行效率。而基本APK文件则是应用程序的核心安装包。根据错误提示,系统无法在 /system/framework/oat 目录中找到基本APK文件,导致解析失败。 这个问题可能是由于系统文件的损坏或缺失引起的。为了解决这个问题,您可以尝试以下步骤: 1. 清除应用程序缓存:在设备的设置中找到应用程序管理器,然后找到PackageManager相关的应用程序(例如Google Play Store),清除其缓存。 2. 检查系统文件完整性:如果清除缓存没有解决问题,您可以尝试使用系统自带的文件检查工具(如Android Recovery Mode)来检查和修复系统文件的完整性。 3. 刷机或恢复出厂设置:如果以上方法都无法解决问题,您可以考虑刷机或恢复出厂设置来重置系统,并重新安装应用程序。 请注意,在进行任何系统操作之前,请务必备份重要数据,并确保您对这些操作有足够的了解和经验,以避免意外数据丢失或设备损坏。 如果问题仍然存在,建议您咨询设备制造商或Android系统的支持团队,以获得更专业的帮助和指导。 希望以上信息对您有所帮助!如果您有任何其他问题,请随时提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值