sheldon_blogs

RK3588 Android12 一个固件兼容多个板型方案

一、专业术语:DTB, DTBO, DTC, DTO,DTS, FDT

它们之间的关系可以描述为:
DTS 是用于描述 FDT 的文件;
DTS 经过 DTC 编译后可生成 DTB/DTBO
DTB 和 DTBO 通过 DTO 操作可合并成一个新的 DTB
通常情况下很多用户习惯把“DTO“这个词的动作含义用“DTBO“来替代,下文中我们避开这个概念混用,
明确:DTO 是一个动词概念,代表的是操作;DTBO 是一个名词概念,指的是用于叠加的次 dtb
本章节更多知识可参考:https://source.android.google.cn/devices/architecture/dto

二、Multi DTB

  RK3588平台默认支持multi dtb,即多dtb兼容方式,功能启用需配置 CONFIG_ROCKCHIP_HWID_DTB=y ,代码实现在uboot(u-boot/arch/arm/mach-rockchip/resource_hwid.c),对应的function:static int hwid_adc_find_dtb(const char *file_name),实现的逻辑是通过获取kernel-5.10/scripts/mkmultidtb.py 脚本中配置的DTBS列表,和实际从主板对应saradc通道读取的电压值(或gpio方式)进行匹配,加载对应的dtb。

DTB命名用户需要将ADC/GPIO 的硬件唯一值信息体现在 dtb 文件名里。

命名规则如下:

ADC 作为 HW_ID DTB文件名以“.dtb”结尾;

HW_ID 格式: #[controller]_ch[channel]=[adcval],称为一个完整单元
[controller]: dts 里面 ADC 控制器的节点名字。
[channel]: ADC 通道。
[adcval]: ADC 的中心值,实际有效范围是:adcval±30
每个完整单元必须使用小写字母,内部不能有空格;
多个单元之间通过#进行分隔,最多支持 10 个单元;

例如mkmultidtb.py中增加 RK3588-xxx-all (xxx代表对应产品型号名)把当前所有的板型对应的dts汇总:

DTBS['RK3588-xxx-all'] = OrderedDict([('rk3588-xxx-hvt', #_saradc_ch5=1365#_saradc_ch3=10'),
                               ('rk3588-xxx-evt', '#_saradc_ch5=1365#_saradc_ch3=682')])

然后通过build.sh脚本调用./scripts/mkmultidtb.py 传入对应的DTBS对应board名称作为参数,例如上面定义的“RK3588-xxx-all”,mkmultidtb.py脚本会将上面2份dtb打包进resource.img,而resource.img又进一步打包到boot.img

dtb是由dts编译而来,编译变量在device/rockchip/rk3588/xxx/BoardConfig.mk中指定,这里我加了一个变量 BOARD_BUILD_DTS_ALL_IN_ONE 来决定是否启用多multi dtb方式,然后通过PRODUCT_KERNEL_BOARD 指定mkmultidtb.py 中定义的DTBS列表对应board名称,即如上的 'RK3588-xxx-all'。

# Build all dts in one img and loading by HWID
BOARD_BUILD_DTS_ALL_IN_ONE := true
# $(warning "BOARD_BUILD_DTS_ALL_IN_ONE : $(BOARD_BUILD_DTS_ALL_IN_ONE)")
ifeq ($(strip $(BOARD_BUILD_DTS_ALL_IN_ONE)), true)
    PRODUCT_KERNEL_DTS := rk3588-xxx-hvt rk3588-xxx-evt
    PRODUCT_KERNEL_BOARD := RK3588-xxx-all
else
    PRODUCT_KERNEL_DTS := rk3588-xxx-hvt
endif

执行编译、打包的脚本是build.sh,通过读取BoardConfig.mk设定的变量决定编译哪些dts,打包哪些dtb:

if [ "$BUILD_DTS_ALL_IN_ONE" = "" ] ; then
BUILD_DTS_ALL_IN_ONE=`get_build_var BOARD_BUILD_DTS_ALL_IN_ONE`
echo "build_var BOARD_BUILD_DTS_ALL_IN_ONE : $BUILD_DTS_ALL_IN_ONE"
fi
if [ "$KERNEL_DTS" = "" ] ; then
KERNEL_DTS=`get_build_var PRODUCT_KERNEL_DTS`
echo "build_var PRODUCT_KERNEL_DTS : $KERNEL_DTS"
fi
...... # build kernel getbuildtimes
"build kernel begin" a if [ "$BUILD_KERNEL" = true ] ; then echo "Start build kernel" if [ "$BUILD_DTS_ALL_IN_ONE" = true ] ; then # Build all dts in one img and loading by HWID DTBS_BOARD=`get_build_var PRODUCT_KERNEL_BOARD` echo "build_var PRODUCT_KERNEL_BOARD : $DTBS_BOARD" cd $LOCAL_KERNEL_PATH && make clean && make $ADDON_ARGS ARCH=$KERNEL_ARCH $KERNEL_DEFCONFIG && build_multidtb && ./scripts/mkmultidtb.py $DTBS_BOARD && cd - else cd $LOCAL_KERNEL_PATH && make clean && make $ADDON_ARGS ARCH=$KERNEL_ARCH $KERNEL_DEFCONFIG && build_multidtb && ./scripts/mkmultidtb.py RK3588-$TARGET_PRODUCT && cd - fi

 

另外GPIO作为 HW_ID DTB文件名以“.dtb”结尾;

HW_ID 格式:#gpio[pin]=[level],称为一个完整单元
[pin]: GPIO 脚,如 0a2 表示 gpio0a2
[level]: GPIO 引脚电平。
每个完整单元必须使用小写字母,内部不能有空格;
多个单元之间通过#进行分隔,最多支持 10
个单元;

例如:

rk3326-evb-lp3-v10#gpio0a2=0#gpio0c3=1.dtb

如果用GPIO作为硬件识别,必须在rkxx-u-boot.dtsi中保留对应的pinctrlgpio节点;ADC默认已使能。
例如:gpio0gpio1作为识别:

...
&pinctrl {
    u-boot,dm-spl; // 追加该属性,让该节点被保留在U-Boot DTB中。下同。
};
&gpio0 {
    u-boot,dm-spl;
};
&gpio1 {
    u-boot,dm-spl;
};
...

加载结果:

Android 12.0, Build 2022.6, v2
Found DTB in boot part
DTB: rk3588-xxx-evt#_saradc_ch5=20#_saradc_ch3=0.dtb   // 打印匹配的DTB,否则使用默认的"rkkernel.dtb"
HASH(c): OK
ANDROID: fdt overlay OK

注:rk-kernel.dtbrk 默认的 dtb,不体现在上述列表中,所有 dtb 都没匹配成功时默认被使用,打包脚本会使用 DTBS 的第一个 dtb 作为默认的 dtb。

上面提到dts编译dtb后是打包到resource.img,进一步打包到boot.img, 所以boot分区大小也决定了到底能存储多少个dtb,而且resource.img还包含了图片资源,如开机logo等。或许有的平台Kernel-5.4以上由于GKI问题,dtb.img不再打包到boot.img,而是放入vendor_boot.img,加入vendor_boot分区,需要BOARD_BOOT_HEADER_VERSION等于3,bootloader加载也会使用到这个值。

mkbootimg :打包固件生成bootrecovery.img,源文件来在android工程。

./scripts/mkbootimg --kernel zImage --second resource.img --ramdisk ramdisk.img
--out boot.img

unpack_bootimg : 用于bootrecovery.img解包,源文件来在android工程。

./scripts/unpack_bootimg --boot_img boot.img --out out/

repack-bootimg : 替换bootrecovery.img中的固件。

// 例如:只替换kernel
./scripts/repack-bootimg --boot_img boot.img --kernel zImage -o boot_repack.img
// 例如:只替换resource
./scripts/repack-bootimg --boot_img boot.img --second resource.img -o
boot_repack.img

pack_resource.sh : 打包 ./tools/images/ 目录下的充电图片进resource.img

./scripts/pack_resource.sh resource.img
Pack ./tools/images/ & resource.img to resource.img ...
Unpacking old image(resource.img):
rk-kernel.dtb 1
Pack to resource.img successed!
Packed resources:
rk-kernel.dtb battery_1.bmp battery_2.bmp battery_3.bmp battery_4.bmp
battery_5.bmp battery_fail.bmp battery_0.bmp 8
resource.img is packed ready

分区配置:./device/rockchip/common/build/rockchip/Partitions.mk,也可以从out/target/product/ribeye_bar/misc_info.txt查看动态分区信息。

可以通过如下指令结合查看当前系统分区大小:

ls -l /dev/block/by-name
cat /proc/partitions

、DTBO机制

如果需要兼容的dtb太多,boot分区又不能增大情况,可以考虑使用dtbo机制达到兼容,实现方式是以一个主dtb作为基础,所有板型共用部分,然后差异部分通过dtbo叠加到主dtb,进一步合成对应板型匹配的dtb,这样就减少了dtb的重复内容和占用大小。

DTBO默认未使用,如需开启配置如下:
CONFIG_CMD_DTIMG=y
CONFIG_OF_LIBFDT_OVERLAY=y
代码中加载dtbo逻辑由 board_select_fdt_index()函数的实现,这是一个_weak 函数,用户可以根据实际情况重新实现它。函数作用是在多份 DTBO 中获取用于执行 DTO 操作的那份 DTBO(返回 index 索引,最小从0 开始),默认的 weak 函数返回的 index 为 0。具体查阅:u-boot/common/androidbootloader.c 。

/*
* Default return index 0.
*/
__weak int board_select_fdt_index(ulong dt_table_hdr)
{
/*
* User can use "dt_for_each_entry(entry, hdr, idx)" to iterate
* over all dt entry of DT image and pick up which they want.
* *
Example:
* struct dt_table_entry *entry;
* int index;
* *
dt_for_each_entry(entry, dt_table_hdr, index) {
* *
.... (use entry)
* }
* *
return index;
*/
return 0;
}

需要注意DTO 操作使用的 DTB 和 DTBO 的编译跟普通的 DTB 编译有区别,语法上有特殊区别:使用 dtc 编译.dts 时,必须添加选项-@以在生成的.dtbo 中添加_symbols_节点。_symbols_节点包含带标签的所有节点的列表,DTO 库可使用这个列表作为参考。如下示例:

编译主.dts 的示例命令:

dtc -@ -O dtb -o my_main_dt.dtb my_main_dt.dts

编译叠加层 DT .dts 的示例命令

dtc -@ -O dtb -o my_overlay_dt.dtbo my_overlay_dt.dts

DTO语法参考:https://source.android.google.cn/docs/core/architecture/dto/syntax

DTO 执行完成后,在 U-Boot 的开机信息中可以看到结果:

// 成功时的打印
ANDROID: fdt overlay OK
// 失败时的打印
ANDROID: fdt overlay failed, ret=-19

通常引起失败的原因一般都是因为主/次设备书 blob 的内容存在不兼容引起,所以用户需要对它们的生成语法和兼容性要比较清楚。

DTO 执行成功后在给 kernel cmdline 里追加如下信息,表明当前使用哪份 DTBO 进行 DTO 操作:

androidboot.dtbo_idx=1 // idx从0开始,这里表示选取idx=1的那份DTBO进行DTO操作

DTO 执行成功后可以在U-Boot命令行使用 fdt 命令查看DTB内容,确认改动是否生效。

 

posted on 2022-12-27 20:30  sheldon_blogs  阅读(2533)  评论(0编辑  收藏  举报

导航