[TOC]
系统模块
Android BCB
BCB(Bootloader Control Block)是Android控制系统启动流程而设计的一种和bootloader交互的机制。数据结构定义在 misc 分区偏移 16KB 或者 0 位置。
数据结构:
struct android_bootloader_message {
char command[32];
char status[32];
char recovery[768];
/* The 'recovery' field used to be 1024 bytes. It has only ever
* been used to store the recovery command line, so 768 bytes
* should be plenty. We carve off the last 256 bytes to store the
* stage string (for multistage packages) and possible future
* expansion. */
char stage[32];
/* The 'reserved' field used to be 224 bytes when it was initially
* carved off from the 1024-byte recovery field. Bump it up to
* 1184-byte so that the entire bootloader_message struct rounds up
* to 2048-byte. */
char reserved[1184];
};
command:启动命令,目前支持以下三个:
| 参数 | 功能 |
|---|---|
| bootonce-bootloader | 启动进入 U-Boot fastboot |
| boot-recovery | 启动进入 recovery |
| boot-fastboot | 启动进入 recovery fastboot(简称 fastbootd) |
recovery:为进入 recovery mode 的附带命令,开头为"recovery\n",后面可以带多个参数,以"--"开头,以"\n"结尾,例如"recovery\n--wipe_ab\n--wipe_package_size=345\n--reason=wipePackage\n":
| 参数 | 功能 |
|---|---|
| update_package | OTA 升级 |
| retry_count | 进 recovery 升级次数,比如升级时意外掉电,依据该值重新进入 recovery 升级 |
| wipe_data | erase user data (and cache), then reboot |
| wipe_cache | wipe cache (but not user data), then reboot |
| show_text | show the recovery text menu, used by some bootloader |
| sideload | |
| sideload_auto_reboot | an option only available in user-debug build, reboot the device without waiting |
| just_exit | do nothing, exit and reboot |
| locale | save the locale to cache, then recovery will load locale from cache when reboot |
| shutdown_after | return shutdown |
| wipe_all | 擦除整个 userdata 分区 |
| wipe_ab | wipe the current A/B device, with a secure wipe of all the partitions in RECOVERY_WIPE |
| wipe_package_size | wipe package size |
| prompt_and_wipe_data | prompt the user that data is corrupt, with their consent erase user data (and cache), then reboot |
| fw_update | SD 卡固件升级 |
| factory_mode | 工厂模式,主要用于做一些设备测试,如 PCBA 测试 |
| pcba_test | 进入 PCBA 测试 |
| resize_partition | 重新规划分区大小,android Q 的动态分区支持 |
| rk_fwupdate | 指定rk SD/USB固件升级,作用域仅限于U-Boot |
U-Boot阶段一般不需要用到和关心上述参数,仅供用户学习参考。
AArch32
ARMv8 的 64 位芯片支持从AArch64 退化到 AArch32 模式运行(跟ARMv7 兼容),代码必须用 32 位编译。
用户可以通过这个宏来确认当前是否为ARMv8的 AArch32 模式:
CONFIG_ARM64_BOOT_AARCH32=y
DTBO/DTO
为了便于用户对本章节内容的理解,这里先阅读附录章节,明确专业术语: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。
原理介绍
DTO(Devcie Tree Overlay) 是 Android P 后引入且必须强制启用的功能,可让次设备树 Blob(DTBO) 叠加在已有的主设备树 Blob 上。DTO 可以维护系统芯片 SoC 设备树,并动态叠加针对特定设备的设备树,从而向树中添加节点并对现有树中的属性进行更改。
主设备树 Blob(*.dtb)一般由 Vendor 厂商提供,次设备树 Blob(*.dtbo)可由 ODM/OEM 等厂商提供,最后通过 bootloader 合并后再传递给 kernel。如下图:

图片来自:https://source.android.google.cn/devices/architecture/dto
需要注意: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 启用
- 配置使能:
CONFIG_CMD_DTIMG=y
CONFIG_OF_LIBFDT_OVERLAY=y
- board_select_fdt_index()函数的实现。这是一个__weak 函数,用户可以根据实际情况重新实现它。函数作用是在多份 DTBO 中获取用于执行 DTO 操作的那份 DTBO(返回 index 索引,最小从 0 开始),默认的 weak 函数返回的 index 为 0。
/*
* 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 结果
- 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内容,确认改动是否生效。
Cmdline
cmdline 是 U-Boot 向 kernel 传递参数的一个重要手段,诸如传递启动存储,设备状态等。目前 cmdline 参数有多个来源,由 U-Boot 进行拼接、过滤重复数据之后再传给 kernel。U-Boot 阶段的 cmdline 被保存在 bootargs 环境变量中。
U-Boot 最终是通过修改的kernel DTB里的 /chosen/bootargs 实现cmdline传递。
数据来源
-
parameter.txt 文件
如果是 RK 格式的分区表,可以在 parameter.txt 里存放 cmdline 信息,例如:
CMDLINE: console=ttyFIQ0 androidboot.baseband=N/A androidboot.selinux=permissive androidboot.hardware=rk30board androidboot.console=ttyFIQ0 init=/init mtdparts=rk29xxnand:0x00002000@0x00002000(uboot),0x00002000@0x00004000(trust),如果是 GPT 格式的分区表,在 parameter.txt 里存放cmdline 信息是无效的。
-
kernel dts 的
/chosen/bootargs,例如:chosen {
bootargs = "earlyprintk=uart8250,mmio32,0xff30000 swiotlb=1 console=ttyFIQ0
androidboot.baseband=N/A androidboot.veritymode=enforcing
androidboot.hardware=rk30board androidboot.console=ttyFIQ0
init=/init kpti=0";
}; -
U-Boot:根据当前运行的状态,U-Boot 会动态追加一些内容到 cmdline。比如:
storagemedia=emmc androidboot.mode=emmc ...... -
boot/recovery.img 固件头里的通常也会有cmdline字段信息。
数据含义
下面列出 RK 平台常用的 cmdlinie 参数含义。更多可参考内核文档:Documentation/admin-guide/kernel-parameters.txt。
-
sdfwupdate: sd 升级卡标志,recovery程序需要;
-
root=PARTUUID:指定 rootfs(system) 分区的UUID,仅 GPT 表支持
-
skip_initramfs:kernel 不使用 uboot 加载的 ramdisk,而使用 rootfs(system) 里的ramdisk
-
storagemedia:存储启动类型;
-
console:kernel 打印口的配置信息;
-
earlycon:在串口节点未建立之前,指定串口及其配置
-
loop.max_part:max_part 用来设定每个 loop 的设备所能支持的分区数目
-
rootwait:用于文件系统不能立即可用的情况,例如 emmc 初始化未完成,这个时候如果不设置 root_wait 的话,就会 mount rootfs failed,而加上这个参数的话,则可以等待 driver 加载完成后,在从存储设备中 copy 出 rootfs,再 mount 的话,就不会提示失败了
-
ro/rw:加载 rootfs 的属性,只读/读写
-
firmware_calss.path:指定驱动位置,如 wifi、bt、gpu 等
-
dm="lroot none 0, 0 4096 linear 98:3 0, 4096 4096 linear 98:32" root=/dev/dm-0:Will boot to a rw dm-linear target of 8192 sectors split across two block devices identified by their major:minor numbers.After boot, udev will rename this target to /dev/mapper/lroot (depending on the rules).No uuid was assigned.参考链接https://android.googlesource.com/kernel/common/+/android-3.18/Documentation/device-mapper/boot.txt
-
androidboot.slot_suffix:AB System 时为 kernel 指定从哪个 slot 启动
-
androidboot.serialno:为 kernel 及上层提供序列号,例如 adb 的序列号等
-
androidboot.verifiedbootstate:安卓需求,为上层提供 uboot 校验固件的状态,有三种状态,如下
- green: If in LOCKED state and the key used for verification was not set by the end user
- yellow: If in LOCKED state and the key used for verification was set by the end user
- orange: If in the UNLOCKED state
-
androidboot.hardware:启动设备,如 rk30board
-
androidboot.verifymode:指定验证分区的真实模式/状态(即验证固件的完整性)
-
androidboot.selinux:SELinux 是一种基于域-类型模型(domain-type)的强制访问控制(MAC)安全系统。有三种模式:
- enforcing:强制模式,代表 SELinux 运作中,且已经正确的开始限制 domain/type 了
- permissive:宽容模式:代表 SELinux 运作中,不过仅会有警告讯息并不会实际限制 domain/type 的存取。这种模式可以运来作为 SELinux 的 debug 之用
- disabled:关闭,SELinux 并没有实际运作
-
androidboot.mode:安卓启动方式,有 normal 与 charger。
- normal:正常开机启动
- charger:关机后接电源开机,androidboot.mode 被设置为 charger,这个值由 uboot 检测电源充电后设置到 bootargs 环境变量内
-
androidboot.wificountrycode:设置 wifi 国家码,如 US,CN
-
androidboot.baseband:配置基带,RK 无此功能,设置为 N/A
-
androidboot.console:android 信息输出口配置
-
androidboot.vbmeta.device=PARTUUID:指定 vbmeta 在存储中的位置
-
androidboot.vbmeta.hash_alg:设置 vbmeta hash 算法,如 sha512
-
androidboot.vbmeta.size:指定 vbmeta 的 size
-
androidboot.vbmeta.digest:给 kernel 上传 vbmeta 的 digest,kernel 加载 vbmeta 后计算 digest,并与此 digest 对比
-
androidboot.vbmeta.device_state:avb2.0 指定系统 lock 与 unlock
ENV 操作
框架支持
ENV 是 U-Boot 框架中非常重要的一种数据管理方式,通过 hash table 构建"键值"和"数据"进行映射管理,支持"增/删/改/查"操作。通常,我们把它管理的键值和数据统称为:环境变量。U-Boot 支持把 ENV 数据保存在各种存储介质:NOWHERE/eMMC/FLASH/EEPROM/NAND/SPI_FLASH/UBI...
配置:
// 默认配置:ENV保存在内存
CONFIG_ENV_IS_NOWHERE
// ENV保存在各种存储介质
CONFIG_ENV_IS_IN_MMC
CONFIG_ENV_IS_IN_NAND
CONFIG_ENV_IS_IN_EEPROM
CONFIG_ENV_IS_IN_FAT
CONFIG_ENV_IS_IN_FLASH
CONFIG_ENV_IS_IN_NVRAM
CONFIG_ENV_IS_IN_ONENAND
CONFIG_ENV_IS_IN_REMOTE
CONFIG_ENV_IS_IN_SPI_FLASH
CONFIG_ENV_IS_IN_UBI
// 任意已经接入到BLK框架层的存储介质(mmc除外),RK平台推荐使用 !
CONFIG_ENV_IS_IN_BLK_DEV
框架代码:
./env/nowhere.c
./env/env_blk.c
./env/mmc.c
./env/nand.c
./env/eeprom.c
./env/embedded.c
./env/ext4.c
./env/fat.c
./env/flash.c
......
相关接口
// 获取环境变量
char *env_get(const char *varname);
ulong env_get_ulong(const char *name, int base, ulong default_val);
ulong env_get_hex(const char *varname, ulong default_val);
// 修改或创建环境变量,value为NULL时等同于删除操作
int env_set(const char *varname, const char *value);
int env_set_ulong(const char *varname, ulong value);
int env_set_hex(const char *varname, ulong value);
// 把保存在存储介质上的ENV信息全部加载出来
int env_load(void);
// 把当前所有ENV信息保存到存储介质上
int env_save(void);
- env_load():用户不需要调用,U-Boot 框架会在合适的启动流程里调用;
- env_save():用户在需要的时刻主动调用,会把所有的 ENV 信息保存到 CONFIG_ENV_IS_NOWHERE_XXX 指定的存储介质;
高级接口
RK 提供了两个统一处理 ENV 的高级接口,具有创建、追加、替换的功能。主要是为了处理bootargs 环境变量,但同样适用于其他环境变量操作。
/**
* env_update() - update sub value of an environment variable
*
* This add/append/replace the sub value of an environment variable.
*
* @varname: Variable to adjust
* @valude: Value to add/append/replace
* @return 0 if OK, 1 on error
*/
int env_update(const char *varname, const char *varvalue);
/**
* env_update_filter() - update sub value of an environment variable but
* ignore some key word
*
* This add/append/replace/igore the sub value of an environment variable.
*
* @varname: Variable to adjust
* @valude: Value to add/append/replace
* @ignore: Value to be ignored that in varvalue
* @return 0 if OK, 1 on error
*/
int env_update_filter(const char *varname, const char *varvalue, const char *ignore);
1 env_update()使用规则:
- 创建:如果 varname 不存在,则创建 varname 和 varvalue;
- 追加:如果 varname 已存在,varvalue 不存在,则追加 varvalue;
- 替换:如果 varname 已存在,varvalue 已存在,则用当前的 varvalue 替换原来的。比如:原来存在“storagemedia=emmc”,当前传入 varvalue 为“storagemedia=rknand”,则最终更新为"storagemedia=rknand"。
2 env_update_filter()是 env_update()的扩展版本:在更新 env 的同时把 varvalue 里的某个关键字剔除;
3 特别注意:env_update()和 env_update_filter()都是以空格和“=”作为分隔符对 ENV 内容进行单元分割,所以操作单元是:单个词、"key=value"组合词:
- 单个词:sdfwupdate、……
- "key=value"组合词:storagemedia=emmc、 init=/init、androidboot.console=ttyFIQ0、……
- 上述两个接口无法处理长字符串单元。比如无法把“console=ttyFIQ0 androidboot.baseband=N/A androidboot.selinux=permissive“作为一个整体单元进行操作。
存储位置
env_save()可以把 ENV 保存到存储介质,RK 平台的 ENV 的存储位置和大小定义如下:
if ARCH_ROCKCHIP
config ENV_OFFSET
hex
depends on !ENV_IS_IN_UBI
depends on !ENV_IS_NOWHERE
default 0x3f8000
help
Offset from the start of the device (or partition)
config ENV_SIZE
hex
default 0x8000
help
Size of the environment storage area
endif
- 通常,ENV_OFFSET 和 ENV_SIZE 都不建议修改。
通用选项
目前常用的存储介质一般有:eMMC/sdmmc/Nandflash/Norflash 等。但 U-Boot 原生的 Nand、Nor 类 ENV 驱动都走 MTD 框架,而 RK 所有已支持的存储都是走 BLK 框架层,因此这些 ENV 驱动无法使用。
为此,RK 为接入 BLK 框架层的存储提供 CONFIG_ENV_IS_IN_BLK_DEV 配置选项:
- eMMC/sdmmc 的情况,依然选择 CONFIG_ENV_IS_IN_MMC;
- Nand、Nor 的情况,可以选择 CONFIG_ENV_IS_IN_BLK_DEV;
请用户使用前先阅读Kconfig的CONFIG_ENV_IS_IN_BLK_DEV 定义。
// 已经默认被指定好,不需要修改
CONFIG_ENV_OFFSET
CONFIG_ENV_SIZE
// 通常不需要使用到
CONFIG_ENV_OFFSET_REDUND (optional)
CONFIG_ENV_SIZE_REDUND (optional)
CONFIG_SYS_MMC_ENV_PART (optional)
注意:无论选择哪个 CONFIG_ENV_IS_IN_XXX 配置,请先阅读 Kconfig 中的定义说明,里面都有子配置说明。
fw_printenv工具
fw_printenv是U-Boot提供的一个给linux使用的env工具。通过这个工具,用户可以在linux上访问、修改env的内容。使用该工具要求env区域必须位于一个kernel可见的分区上(建议独立分区),本质上是通过kernel sys下的存储节点访问到env区域。
工具获取方式:
./make.sh env
执行完命令后获得:
./tools/env/fw_printenv // env读写工具
./tools/env/fw_env.config // env配置文件
./tools/env/README // env读写工具说明文档
使用方法请参考README文档。
HW-ID DTB
RK平台的U-Boot支持检测硬件上的GPIO或者ADC状态动态加载不同的Kernel DTB,暂称为HW-ID DTB(Hardware id DTB)功能。
设计原理
通常硬件设计会经常更新版本和一些元器件,比如:屏幕、wifi 模组等。如果每一个硬件版本都要对应一套软件,维护起来就比较麻烦。所以需要 HW_ID 功能实现一套软件可以适配不同版本的硬件。
针对不同硬件版本,软件上需要提供对应的 dtb 文件,同时还要提供 ADC/GPIO 硬件唯一值用于表征当前硬件版本(比如:固定的 adc 值、固定的某 GPIO 电平)。
用户把这些和硬件版本对应的 dtb 文件全打包进同一个 resource.img,U-Boot 引导 kernel 时会检测硬件唯一值,从 resource.img 里找出和当前硬件版本匹配的 dtb 传给 kernel。
硬件参考
目前支持 ADC 和 GPIO 两种方式确定硬件版本。
ADC 参考设计
RK3326-EVB/PX30-EVB 主板上有预留分压电阻,不同的电阻分压有不同的 ADC 值,这样可以确定不同硬件版本:

配套使用的 MIPI 屏小板预留有另外一颗下拉电阻:

不同的 mipi 屏会配置不同的阻值,配合 EVB 主板确定一个唯一的 ADC 参数值。
目前 V1 版本的 ADC 计算方法:ADC 参数最大值为 1024,对应着 ADC_IN0 引脚被直接上拉到供电电压 1.8V,MIPI 屏上有一颗 10K 的下拉电阻,接通 EVB 板后 ADC=1024*10K/(10K + 51K) =167.8。
GPIO 参考设计
目前没有 GPIO 的硬件参考设计,用户可自己定制。
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 个单元;
范例:
rk3326-evb-lp3-v10#saradc_ch2=111#saradc_ch1=810.dtb
rk3326-evb-lp3-v10#_saradc_ch2=569.dtb
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
DTB打包
kernel 仓库:scripts/mkmultidtb.py。通过该脚本可以把多个 dtb 打包进同一个 resource.img。
用户需要打开脚本文件把要打包的 dtb 文件写到 DTBS 字典里面,并填上对应的 ADC/GPIO 的配置信息。
...
DTBS = {}
DTBS['PX30-EVB'] = OrderedDict([('rk3326-evb-lp3-v10', '#_saradc_ch0=166'),
('px30-evb-ddr3-lvds-v10', '#_saradc_ch0=512')])
...
上例中,执行 scripts/mkmultidtb.py PX30-EVB 就会生成包含 3 份 dtb 的 resource.img::
- rk-kernel.dtb:rk 默认的 dtb,不体现在上述字典中。所有 dtb 都没匹配成功时默认被使用。打包脚本会使用 DTBS 的第一个 dtb 作为默认的 dtb;
- rk3326-evb-lp3-v10#_saradc_ch0=166.dtb:包含 ADC 信息的 rk3326 dtb 文件;
- px30-evb-ddr3-lvds-v10#_saradc_ch0=512.dtb:包含 ADC 信息的 px30 dtb 文件;
功能启用
配置选项:
CONFIG_ROCKCHIP_HWID_DTB=y
驱动代码:
./arch/arm/mach-rockchip/resource_img.c // 具体实现:rockchip_read_hwid_dtb()
DTS 配置:
如果用GPIO作为硬件识别,必须在rkxx-u-boot.dtsi中保留对应的pinctrl和gpio节点;ADC默认已使能。
例如:gpio0和gpio1作为识别:
...
&pinctrl {
u-boot,dm-spl; // 追加该属性,让该节点被保留在U-Boot DTB中。下同。
};
&gpio0 {
u-boot,dm-spl;
};
&gpio1 {
u-boot,dm-spl;
};
...
加载结果
......
mmc0(part 0) is current device
boot mode: None
DTB: rk3326-evb-lp3-v10#_saradc_ch0=166.dtb // 打印匹配的DTB,否则默认"rk-kernel.dtb"
Using kernel dtb
......
AB系统
所谓的 A/B System 即把系统固件分为两份,分别称为slot-a,slot-b。系统可以从任意一个 slot 启动,当一个 slot 启动失败后还可以从另一个启动,同时升级时可以直接将固件拷贝到另一个 slot 上而无需进入系统升级模式。详细原理和流程请参考进阶原理章节。
目前RK平台的pre-loader和U-Boot都可以支持A/B系统。
芯片支持
| 芯片 | Android 支持 | Linux 支持 |
|---|---|---|
| rk3128x | N | Y |
| rk3126 | N | Y |
| rk322x | Y | Y |
| rk3288 | N | Y |
| rk3368 | N | Y |
| rk3328 | N | Y |
| rk3399 | N | Y |
| rk3308 | N | Y |
| px30 | Y | Y |
| rk3326 | Y | Y |
配置项
A/B System需要依赖LIBAVB,如下:
// A/B依赖的库
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
// 使能A/B功能
CONFIG_ANDROID_AB=y
分区表
A/B System对分区表有要求:需要支持 A/B 的分区必须增加后缀 _a 和 _b。parameter.txt 参考如下:
FIRMWARE_VER:8.1
MACHINE_MODEL:RK3326
MACHINE_ID:007
MANUFACTURER: RK3326
MAGIC: 0x5041524B
ATAG: 0x00200800
MACHINE: 3326
CHECK_MASK: 0x80
PWR_HLD: 0,0,A,0,1
TYPE: GPT
CMDLINE: mtdparts=rk29xxnand:0x00002000@0x00004000(uboot_a),0x00002000@0x00006000(uboot_b),0x00002000@0x00008000(trust_a),0x00002000@0x0000a000(trust_b),0x00001000@0x0000c000(misc),0x00001000@0x0000d000(vbmeta_a),0x00001000@0x0000e000(vbmeta_b),0x00020000@0x0000e000(boot_a),0x00020000@0x0002e000(boot_b),0x00100000@0x0004e000(system_a),0x00300000@0x0032e000(system_b),0x00100000@0x0062e000(vendor_a),0x00100000@0x0072e000(vendor_b),0x00002000@0x0082e000(oem_a),0x00002000@0x00830000(oem_b),0x0010000@0x00832000(factory),0x00008000@0x842000(factory_bootloader),0x00080000@0x008ca000(oem),-@0x0094a000(userdata)
注意事项
旧的U-Boot使能A/B系统之后,用户如果访问带a/b的分区,则传递给part_get_info_by_name()的分区名必须带slot后缀,例如:"boot_a"或 "boot_b"。这样会增加很多冗余代码:用户必须先获取当前系统的slot,再进行字符串拼接,最终得到分区名。
新的代码优化了这个问题。如果用户的代码版本在下面这个提交点之后,则访问a/b的分区时可带、可不带slot后缀,框架层会自动探测当前系统使用哪个slot。例如:上述情况可直接使用"boot"。
commit c6666740ee3b51c3e102bfbaf1ab95b78df29246
Author: Joseph Chen <chenjh@rock-chips.com>
Date: Thu Oct 24 15:48:46 2019 +0800
common: android/rkimg: remove/clean android a/b (slot) code
- the partition disk layer takes over the responsibility of slot suffix
appending, we remove relative code to make file clean;
- put android a/b code together and name them to be eary understood,
this makes file esay to read.
Change-Id: Id8c838da682ce6098bd7192d7d7c64269f4e86ba
Signed-off-by: Joseph Chen <chenjh@rock-chips.com>
AVB安全启动
AVB 为 Android Verified Boot,谷歌设计的一套固件校验流程,主要用于校验 boot system 等固件。Rockchip Secure Boot 参考通信中的校验方式及 AVB,实现一套完整的 Secure Boot 校验方案。
Feature
- 安全校验
- 完整性校验
- 防回滚保护
- persistent partition 支持
- chained partitions 支持,可以与 boot,system 签名私钥一致,也可以由 oem 自己保存私钥,但必须由 PRK 签名
配置
开启 AVB 需要 trust 支持:
CONFIG_OPTEE_CLIENT=y
CONFIG_OPTEE_V1=y
CONFIG_OPTEE_ALWAYS_USE_SECURITY_PARTITION=y // 安全数据存储到security分区
CONFIG_OPTEE_V1:适用平台有 312x,322x,3288,3228H,3368,3399。CONFIG_OPTEE_V2:适用平台有 3326,3308。CONFIG_OPTEE_ALWAYS_USE_SECURITY_PARTITION:eMMC 的 rpmb 不能用时才开这个宏,默认不开。
开启 AVB 相关配置:
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
// 上面几个为必选,下面选择为支持 AVB 与 A/B 特性,两个特性可以分开使用。
CONFIG_ANDROID_AB=y //这个支持 A/B
CONFIG_ANDROID_AVB=y //这个支持 AVB
// 下面宏为仅有 efuse 的平台使用
CONFIG_ROCKCHIP_PRELOADER_PUB_KEY=y
// 下面宏需要严格unlock校验时候打开
CONFIG_RK_AVB_LIBAVB_ENABLE_ATH_UNLOCK=y
// 安全校验开启
CONFIG_AVB_VBMETA_PUBLIC_KEY_VALIDATE=y
// 如果需要cpuid作为challenge number,开启以下宏
CONFIG_MISC=y
CONFIG_ROCKCHIP_EFUSE=y
CONFIG_ROCKCHIP_OTP=y
参考
因为AVB涉及的内容比较多,其余原理、配置请参考进阶原理章节。
Fastboot
Fastboot是Android提供的一种借助USB和U-Boot进行交互的方式,一般用于获取设备信息、烧写固件等。
配置选项
// 使能配置
CONFIG_FASTBOOT
CONFIG_FASTBOOT_FLASH
CONFIG_USB_FUNCTION_FASTBOO
// 参数配置
CONFIG_FASTBOOT_BUF_ADDR
CONFIG_FASTBOOT_BUF_SIZE
CONFIG_FASTBOOT_FLASH_MMC_DEV
CONFIG_FASTBOOT_USB_DEV
触发方式
Fastboot 默认使用 Google adb 的 VID/PID,有如下几种触发方式:
- kernel的命令行执行:reboot fastboot
- U-Boot的命令行执行:fastboot usb 0
- 开机长按组合键:ctrl+f
命令支持
fastboot flash < partition > [ < filename > ]
fastboot erase < partition >
fastboot getvar < variable > | all
fastboot set_active < slot >
fastboot reboot
fastboot reboot-bootloader
fastboot flashing unlock
fastboot flashing lock
fastboot stage [ < filename > ]
fastboot get_staged [ < filename > ]
fastboot oem fuse at-perm-attr-data
fastboot oem fuse at-perm-attr
fastboot oem at-get-ca-request
fastboot oem at-set-ca-response
fastboot oem at-lock-vboot
fastboot oem at-unlock-vboot
fastboot oem at-disable-unlock-vboot
fastboot oem fuse at-bootloader-vboot-key
fastboot oem format
fastboot oem at-get-vboot-unlock-challenge
fastboot oem at-reset-rollback-index
命令详解
-
fastboot flash < partition > [ < filename > ]
功能:分区烧写
举例: fastboot flash boot boot.img
-
fastboot erase < partition >
功能:擦除分区
举例:fastboot erase boot
-
fastboot getvar < variable >
功能:获取设备信息
举例:fastboot getvar version-bootloader
< variable > 参数:
version /* fastboot 版本 */
version-bootloader /* uboot 版本 */
version-baseband
product /* 产品信息 */
serialno /* 序列号 */
secure /* 是否开启安全校验 */
max-download-size /* fastboot 支持单次传输最大字节数 */
logical-block-size /* 逻辑块数 */
erase-block-size /* 擦除块数 */
partition-type : < partition > /* 分区类型 */
partition-size : < partition > /* 分区大小 */
unlocked /* 设备lock状态 */
off-mode-charge
battery-voltage
variant
battery-soc-ok
slot-count /* slot 数目 */
has-slot: < partition > /* 查看slot内是否有该分区名 */
current-slot /* 当前启动的slot */
slot-suffixes /* 当前设备具有的slot,打印出其name */
slot-successful: < _a | _b > /* 查看分区是否正确校验启动过 */
slot-unbootable: < _a | _b > /* 查看分区是否被设置为unbootable */
slot-retry-count: < _a | _b > /* 查看分区的retry-count次数 */
at-attest-dh
at-attest-uuid
at-vboot-state -
fastboot getvar all
功能:获取所有设备信息
-
fastboot set_active < slot >
功能:设置重启的 slot
举例:fastboot set_active _a
-
fastboot reboot
功能:重启设备,正常启动
举例:fastboot reboot
-
fastboot reboot-bootloader
功能:重启设备,进入 fastboot 模式
举例:fastboot reboot-bootloader
-
fastboot flashing unlock
功能:解锁设备,允许烧写固件
举例:fastboot flashing unlock
-
fastboot flashing lock
功能:锁定设备,禁止烧写
举例:fastboot flashing lock
-
fastboot stage [ < filename > ]
功能:下载数据到设备端内存,内存起始地址为 CONFIG_FASTBOOT_BUF_ADDR
举例:fastboot stage permanent_attributes.bin
-
fastboot get_staged [ < filename > ]
功能:从设备端获取数据
举例:fastboot get_staged raw_unlock_challenge.bin
-
fastboot oem fuse at-perm-attr
功能:烧写 permanent_attributes.bin 及 hash
举例:
fastboot stage permanent_attributes.bin
fastboot oem fuse at-perm-attr
-
fastboot oem fuse at-perm-attr-data
功能:只烧写 permanent_attributes.bin 到安全存储区域(RPMB)
举例:
fastboot stage permanent_attributes.bin
fastboot oem fuse at-perm-attr-data
-
fastboot oem at-get-ca-request
-
fastboot oem at-set-ca-response
-
fastboot oem at-lock-vboot
功能:锁定设备
举例:fastboot oem at-lock-vboot
-
fastboot oem at-unlock-vboot
功能:解锁设备,现支持 authenticated unlock
举例:
fastboot oem at-get-vboot-unlock-challenge fastboot get_staged raw_unlock_challenge.bin
./make_unlock.sh(见 make_unlock.sh 参考)
fastboot stage unlock_credential.bin fastboot oem at-unlock-vboot
可以参考《how-to-generate-keys-about-avb.md》
-
fastboot oem fuse at-bootloader-vboot-key
功能:烧写 bootloader key hash
举例:
fastboot stage bootloader-pub-key.bin
fastboot oem fuse at-bootloader-vboot-key
-
fastboot oem format
功能:重新格式化分区,分区信息依赖于$partitions
举例:fastboot oem format
-
fastboot oem at-get-vboot-unlock-challenge
功能:authenticated unlock,需要获得 unlock challenge 数据
举例:参见 16. fastboot oem at-unlock-vboot
-
fastboot oem at-reset-rollback-index
功能:复位设备的 rollback 数据
举例:fastboot oem at-reset-rollback-index
-
fastboot oem at-disable-unlock-vboot
功能:使 fastboot oem at-unlock-vboot 命令失效
举例:fastboot oem at-disable-unlock-vboot
SD和U盘
本章节主要介绍RK平台上的SD 和U盘的固件启动、升级。
机制原理
启动卡和升级卡制作完成后,都会在固件头部的固定存储偏移位置打上固定的tag,用于标记当前是启动卡还是升级卡。U-Boot 识别到这个标记后就会走启动或升级流程。其中:
- 启动卡:卡内只有一份完整的固件。U-Boot直接使用这份完成固件正常启动系统;
- 升级卡:卡内有两份固件。制作升级卡时,PC工具会烧录两份固件:一份仅含进入recovery模式必须的分区镜像(记为A固件),一份是完整的update.img 固件(记为B固件)。U-Boot使用A固件引导系统进入recovery模式,然后由recovery程序使用B固件完成升级工作。
特别注意:
- SD卡启动/升级是从bootrom这一级就开始支持;
- U盘启动/升级仅从U-Boot这一级开始支持,即用户至少要保证U-Boot能正常工作!
固件制作
RK平台上的SD和U盘启动卡、升级卡的制作流程是完全一致的,仅需两个步骤:
- 使用SDK目录下的
RKTools/linux/Linux_Pack_Firmware/rockdev/工具生成 update.img。 - 使用SDDiskTool 工具把update.img 烧录到SD或U盘。如图:
- 选择可移动磁盘
- 选择
固件升级或者SD启动 - 点击
开始创建

SD 配置
SD启动/升级:各平台SDK发布的U-Boot已经默认使能该功能,用户不需要额外配置。
USB 配置
U盘启动/升级:各平台SDK发布的U-Boot 默认没有使能。因为U-Boot原生的USB扫描命令很耗时,所以用户自己按需开启:
-
步骤1:烧写升级用的整套固件到本地存储(eMMC/Nand/...等),确认这套固件正常可用。
-
步骤2:插上U盘开机进入U-Boot命令行模式。执行
usb start和usb info命令确认能正常识别U盘,否则请先调通U盘的识别。 -
步骤3:将满足步骤1的kernel DTB 拷贝一份命名成kern.dtb放到U-Boot的
./dts/目录下。这份kern.dtb会在编译U-Boot时被自动打包进uboot.img。kern.dtb 用途:当本地存储分区的kernel dtb 有损坏时,U-Boot 使用kern.dtb 确保USB 能被正常初始化。
-
步骤4:U-Boot使能U盘启动/升级配置
CONFIG_ROCKCHIP_USB_BOOT=y重新编译烧写uboot.img。
如果该过程提示uboot的固件过大无法打包生成,是因为步骤3加入kern.dtb引起的,请先裁掉一些不用的U-Boot配置。
功能生效
如何确认SD、U盘启动或升级功能生效。
用户可以擦除本地存储(eMMC、Nand...)上的kernel、resource、boot、recovery 等关键分区,确认插上SD/U盘后能进入kernel。
注意事项
-
U盘初始化时会调用
usb start命令,整个过程相对耗时; -
如果启动/升级卡要支持GPT分区表,则SDDiskTool工具的版本要求 >= v1.59;
-
如果启动/升级卡要支持AB系统,则SDDiskTool工具的版本要求 >= v1.61;
-
因为U盘启动/升级功能是2019.11才增加的功能,所以相关仓库需要满足如下条件:
- U-Boot 仓库要更新至如下提交点(建议):
commit 369e944c844f783508b7839ae86a3418e2f63bc7
Author: Joseph Chen <chenjh@rock-chips.com>
Date: Thu Dec 12 18:07:07 2019 +0800
fdt/Makefile: make u-boot-dtb.bin 8-byte aligned
The dts/kern.dtb is appended after u-boot-dtb.bin for U-disk boot.
Make sure u-boot-dtb.bin is 8-byte aligned to avoid data-abort on
calling: fdt_check_header(gd->fdt_blob_kern).
Signed-off-by: Joseph Chen <chenjh@rock-chips.com>
Change-Id: Id5f2daf0c5446e7ea828cb970d3d4879e3acda86或者单独增加如下几个补丁的改动(估计比较困难):
369e944 fdt/Makefile: make u-boot-dtb.bin 8-byte aligned
b3b57ac rockchip: board: fix always entering recovery on normal boot U-disk
e0cee41 rockchip: resource: add sha1/256 verify for kernel dtb
5e817a0 tools: rockchip: resource_tool: add sha1 for file entry
fc474da lib: sha256: add sha256_csum()
0ed06f1 rockchip: support boot from U-disk
01f0422 common: bootm: skip usb_stop() if usb is boot device
5704c89 fdtdec: support pack "kern.dtb" to the end of u-boot.bin
3bdef7e gpt: return 1 directly when test the mbr sector- rkbin 仓库要包含这个提交:
commit f9c0b0b72673a65865b00a8824908ca6f12ecc32
Author: Joseph Chen <chenjh@rock-chips.com>
Date: Thu Nov 7 09:21:36 2019 +0800
tools: resource: add sha1 for file entry
Base on U-Boot next-dev branch:
(5e817a0 tools: rockchip: resource_tool: add sha1 for file entry)
Change-Id: Ife061cabacab488dbecf2a3245d58cc660091dbd
Signed-off-by: Joseph Chen <chenjh@rock-chips.com>- kernel 仓库要包含这个提交:
commit 078785057478c789bb033ba06925fa3a07e3130a
Author: Tao Huang <huangtao@rock-chips.com>
Date: Thu Nov 7 17:53:38 2019 +0800
rk: scripts/resource_tool: add sha1 for file entry
From u-boot 5e817a0ea427 ("tools: rockchip: resource_tool: add sha1 for file entry").
Merge all C files to one resource_tool.c
Change-Id: If63ba77d1f5a3660bd6ef87769bb456fa086ae71
Signed-off-by: Tao Huang <huangtao@rock-chips.com> -
如果用户手上的SDK比较旧,除了单独增加上述的补丁,建议跟负责recovery的工程师确认是否recovery有相关补丁。