跳到主要内容

[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_packageOTA 升级
retry_count进 recovery 升级次数,比如升级时意外掉电,依据该值重新进入 recovery 升级
wipe_dataerase user data (and cache), then reboot
wipe_cachewipe cache (but not user data), then reboot
show_textshow the recovery text menu, used by some bootloader
sideload
sideload_auto_rebootan option only available in user-debug build, reboot the device without waiting
just_exitdo nothing, exit and reboot
localesave the locale to cache, then recovery will load locale from cache when reboot
shutdown_afterreturn shutdown
wipe_all擦除整个 userdata 分区
wipe_abwipe the current A/B device, with a secure wipe of all the partitions in RECOVERY_WIPE
wipe_package_sizewipe package size
prompt_and_wipe_dataprompt the user that data is corrupt, with their consent erase user data (and cache), then reboot
fw_updateSD 卡固件升级
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。如下图:

UBoot_nextdev_DTO

图片来自:https://source.android.google.cn/devices/architecture/dto

需要注意:DTO 操作使用的 DTB 和 DTBO 的编译跟普通的 DTB 编译有区别,语法上有特殊区别:

使用 dtc 编译.dts 时,您必须添加选项**-@**以在生成的.dtbo 中添加__symbols__节点。__symbols__节点包含带标签的所有节点的列表,DTO 库可使用这个列表作为参考。如下示例:

  1. 编译主.dts 的示例命令:
dtc -@ -O dtb -o my_main_dt.dtb my_main_dt.dts
  1. 编译叠加层 DT .dts 的示例命令:
dtc -@ -O dtb -o my_overlay_dt.dtbo my_overlay_dt.dts

DTO 启用

  1. 配置使能:
CONFIG_CMD_DTIMG=y
CONFIG_OF_LIBFDT_OVERLAY=y
  1. 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 结果

  1. DTO 执行完成后,在 U-Boot 的开机信息中可以看到结果:
// 成功时的打印
ANDROID: fdt overlay OK

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

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

  1. DTO 执行成功后在给 kernel 的 cmdline 里追加如下信息,表明当前使用哪份 DTBO 进行 DTO 操作:
androidboot.dtbo_idx=1	// idx从0开始,这里表示选取idx=1的那份DTBO进行DTO操作
  1. 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 校验固件的状态,有三种状态,如下

    1. green: If in LOCKED state and the key used for verification was not set by the end user
    2. yellow: If in LOCKED state and the key used for verification was set by the end user
    3. orange: If in the UNLOCKED state
  • androidboot.hardware:启动设备,如 rk30board

  • androidboot.verifymode:指定验证分区的真实模式/状态(即验证固件的完整性)

  • androidboot.selinux:SELinux 是一种基于域-类型模型(domain-type)的强制访问控制(MAC)安全系统。有三种模式:

    1. enforcing:强制模式,代表 SELinux 运作中,且已经正确的开始限制 domain/type 了
    2. permissive:宽容模式:代表 SELinux 运作中,不过仅会有警告讯息并不会实际限制 domain/type 的存取。这种模式可以运来作为 SELinux 的 debug 之用
    3. disabled:关闭,SELinux 并没有实际运作
  • androidboot.mode:安卓启动方式,有 normal 与 charger。

    1. normal:正常开机启动
    2. 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 值,这样可以确定不同硬件版本:

RK3326_PX30_HW_ID1

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

RK3326_PX30_HW_ID2

不同的 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 支持
rk3128xNY
rk3126NY
rk322xYY
rk3288NY
rk3368NY
rk3328NY
rk3399NY
rk3308NY
px30YY
rk3326YY

配置项

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-boot-tool

SD 配置

SD启动/升级:各平台SDK发布的U-Boot已经默认使能该功能,用户不需要额外配置。

USB 配置

U盘启动/升级:各平台SDK发布的U-Boot 默认没有使能。因为U-Boot原生的USB扫描命令很耗时,所以用户自己按需开启:

  • 步骤1:烧写升级用的整套固件到本地存储(eMMC/Nand/...等),确认这套固件正常可用。

  • 步骤2:插上U盘开机进入U-Boot命令行模式。执行usb startusb 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才增加的功能,所以相关仓库需要满足如下条件:

    1. 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
    1. 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>
    1. 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有相关补丁。