基于RK3568打包TPL,SPL,UBoot镜像并运行:实战角度(持续更新)
1 RK3568框图
- boot流程
- 上电,ROM-32KB重映射到物理地址0xFFFF0000
- CPU从0xFFFF0000读取第一条指令并执行
- CPU开始运行BootROM中芯片原厂固化的启动逻辑
- 从可用的存储设备中读取ID BLOCK数据,并校验合法性
- 存储设备访问顺序:SPI Nor Flash,SPI Nand Flash,Nand Flash,eMMC Flash,SD卡,USB OTG
- 注意:这里的SPI Flash是FSPI(Flash SPI),是专用于SPI Flash启动通道的SPI,不是普通通用的SPI0/SPI1/SPI2;Nor/Nand Flash,都是挂载在FSP上,先使用Nor协议探测,探测失败,尝试Nand Flash
- 注意:RK3568有3个SDMMC接口(使用dw-mshc),一个EMMC控制器(使用dwcmshc),BootROM启动的时候,检测的是eMMC上挂载是eMMC存储芯片还是SD卡。
- 校验通过,则从对应的存储设备中加载TPL(内存初始化程序)到SRAM-64KB中执行,完成DDR初始化
- DDR内存初始化完成后,依次加载SPL->UBoot->Kernel到DDR内存中运行
- 从可用的存储设备中读取ID BLOCK数据,并校验合法性
2 编译构建环境介绍
2.1 源码目录
rk3568_linux_sdk/ ├── Makefile # 顶层构建入口,调用 build.sh 执行编译 ├── build.sh # 主构建脚本,驱动整个 SDK 的编译流程 ├── rkflash.sh # 固件烧录脚本,用于将镜像烧写到设备 ├── README.md # 项目说明文档 │ ├── app/ # 应用程序源码 ├── buildroot/ # Buildroot 构建系统(轻量级嵌入式 Linux 构建框架) ├── common/ # 公共资源与脚本 ├── debian/ # Debian 根文件系统构建相关 ├── device/ # 设备相关配置 │ └── rockchip/ # Rockchip 平台特定配置(板级配置、分区表等) ├── docs/ # 开发文档 ├── external/ # 外部第三方库/组件 ├── kernel/ # Linux 内核源码(基于 Rockchip 分支) ├── output/ # 编译输出目录(镜像、目标文件等) ├── prebuilts/ # 存放交叉编译工具链 ├── rkbin/ # Rockchip 闭源二进制文件(DDR 初始化、BL31/ATF、M0 固件等) ├── rockdev/ # Rockchip 设备镜像打包与分区布局,存放编译输出固件,编译SDK后才会生成该文件夹 ├── tools/ # 主机端开发工具(打包、签名、调试工具等) ├── u-boot/ # U-Boot 引导加载程序 └── yocto/ # Yocto 构建系统(用于复杂嵌入式 Linux 发行版构建)2.2 编译环境
- 编译工具安装
- make
- cmake
- libncurses-dev: 用于生成内核配置菜单
- bison 和 flex: 语法分析器生成工具
- 编译配置
SDK/device/rockchip/rk3566_rk3568/ alientek_rk3568_defconfig rockchip_defconfig rockchip_rk3568_evb1_ddr4_v10_32bit_defconfig rockchip_rk3568_evb1_ddr4_v10_defconfig rockchip_rk3568_evb8_lp4_v10_32bit_defconfig rockchip_rk3568_evb8_lp4_v10_defconfig rockchip_rk3568_uvc_evb1_ddr4_v10_defconfig 维度 内存差异:DDR4 vs LPDDR4 架构位宽:64位 vs 32_bit 板级定制差异:官方EVB系列 vs Alientek(正点原子) UVC:针对USB视频类应用- 编译参数
./build.sh lunch:选择板级配置文件 ./build.sh uboot:编译u-boot ./build.sh kernel:编译kernel ./build.sh modules:编译内核模块 ./build.sh rootfs:编译根文件系统 ./build.sh buildroot:编译buildroot根文件系统 ./build.sh debian:编译Debian根文件系统 ./build.sh recovery:编译recovery ./build.sh all:编译整个SDK,包括 uboot、kernel、rootfs、recovery ./build.sh cleanall:清理整个SDK ./build.sh firmware:将镜像打包到rockdev目录 ./build.sh updateimg:将所有镜像打包成一个update.img固件- 编译脚本:SDK\device\rockchip\common\scripts\
编译uboot:mk-loader.sh → u-boot/make.sh → make all。产出u-boot 二进制 打包uboot镜像:mk-loader.sh → u-boot/make.sh → pack_uboot/trust/loader_image。产出uboot.img, trust.img, *_loader_*.bin 编译kernel:mk-kernel.sh → kernel/make.sh。产出vmlinux, modules, dtb 打包kernel镜像:mk-kernel.sh, mk-ramdisk.sh, mk-fitimage.sh → boot.img 生成 。产出boot.img (或FIT格式) 打包recovery镜像:mk-recovery.sh, mk-buildroot.sh, mk-ramdisk.sh → Buildroot编译 → ramdisk打包。产出recovery.img 打包misc镜像:mk-misc.sh, mk-firmware.sh → 生成48KB misc.img misc.img 打包rootfs镜像:mk-rootfs.sh, mk-buildroot.sh, mk-image.sh → Buildroot/Debian/Yocto编译。产出rootfs.img / rootfs.ext4 打包userdata镜像:mk-firmware.sh, mk-image.sh, partition-helper → 遍历参数表 → 创建镜像。产出userdata.img- 执行编译
- 选择配置:./build.sh lunch,选择alientek_rk3568_defconfig
- 执行编译:./build.sh uboot
2.3 UBoot编译打包跟踪
- ./build.sh uboot调用链
./build.sh uboot └─ main("uboot") [build.sh:254-541] ├─ 环境变量初始化 (SCRIPTS_DIR, SDK_DIR, RK_OUTDIR等) ├─ run_build_hooks("init", "uboot") [第427行] │ └─ run_hooks($RK_BUILD_HOOK_DIR, "init", "uboot") │ └─ 执行所有 init hook(初始化配置、加载defconfig等) ├─ source $RK_CONFIG [第439行] ← 加载output/.config ├─ source $PARTITION_HELPER [第481行] ├─ run_build_hooks("pre-build", "uboot") [第526行] ← pre-build阶段 ├─ run_build_hooks("build", "uboot") [第533行] ← ★核心构建阶段 │ └─ run_hooks($RK_BUILD_HOOK_DIR, "build", "uboot") │ └─ 查找并执行 build hook 脚本 │ └─ device/rockchip/common/scripts/mk-loader.sh │ └─ build_hook "uboot" │ ├─ 导出 UMAKE="./make.sh CROSS_COMPILE=$RK_UBOOT_TOOLCHAIN" │ ├─ TARGET="uboot" │ └─ build_uboot() │ ├─ cd u-boot/ │ ├─ 组装 UARGS │ ├─ $UMAKE $RK_UBOOT_CFG $RK_UBOOT_CFG_FRAGMENTS $UARGS │ │ └─ u-boot/make.sh <defconfig> [options] │ │ ├─ process_args() ← 解析参数/配置 │ │ ├─ prepare() ← 校验rkbin路径 │ │ ├─ select_toolchain() ← 选择交叉编译器 │ │ ├─ select_chip_info() ← 从.config识别芯片型号 │ │ ├─ fixup_platform_configure() │ │ ├─ select_ini_file() ← 选择RKBOOT/RKTRUST INI文件 │ │ ├─ sub_commands() ← 如果是uboot/loader/trust则打包 │ │ ├─ clean_files() │ │ ├─ make all --jobs=N ← ★ U-Boot本身编译 │ │ └─ pack_images() │ │ ├─ pack_uboot_image() ← 打包uboot.img │ │ │ └─ scripts/uboot.sh │ │ ├─ pack_trust_image() ← 打包trust.img │ │ │ └─ scripts/atf.sh 或 scripts/tos.sh │ │ └─ pack_loader_image() ← 打包Loader.bin │ │ └─ scripts/loader.sh │ ├─ cd .. │ ├─ ln -rsf *_loader_*.bin → $RK_FIRMWARE_DIR/MiniLoaderAll.bin │ ├─ ln -rsf uboot.img → $RK_FIRMWARE_DIR/uboot.img │ ├─ ln -rsf trust.img → $RK_FIRMWARE_DIR/trust.img ← 如存在 │ └─ finish_build └─ run_build_hooks("post-build", "uboot") [第540行] ← post-build阶段(打包firmware)- 输出成果物
- SDK/u-boot/uboot.img:U-Boot主镜像
- SDK/u-boot/trust.img:Trust(ATF/OP-TEE)镜像
- SDK/u-boot/rk3568_loader_v*.bin:MiniLoader引导加载器。TPL(ddr.bin,RK私有闭源)+ SPL(RK闭源或SPL开源)
2.3.1 ./build.sh uboot详细分析
- device\rockchip\common\scripts\build.sh
- main()
- run_build_hooks init $OPTIONS
- 解析deconfig,加载配置
- run_build_hooks pre-build $OPTIONS
- 各模块检查配置、生成 .config
- run_build_hooks build $OPTIONS
- 匹配执行编译
- run_build_hooks post-build $OPTIONS
- 编译产物打包为固件
- run_build_hooks init $OPTIONS
- run_build_hooks
- 根据参数查找指定目录下的.sh脚本
- 对脚本进行排序
- 按照顺序依次执行脚本:执行成功,则继续执行下一个脚本;否则直接退出
- main()
- 编译配置
- 直接修改.config中的配置是没有作用的,因为在解析deconfig和加载配置之后,.config就会被覆盖掉
- TPL+SPL打包
- script/loader.sh --ini rkbin/RKBOOT/RK3568MINIALL.ini
- tools/boot_merger RK3568MINIALL.ini
- RK3568MINIALL.ini
- 描述TPL的路径和文件名
- 描述SPL的路径和文件名
- 描述usb下载插件的路径和文件名
- script/loader.sh --ini rkbin/RKBOOT/RK3568MINIALL.ini
3 TPL(闭源)+SPL(闭源):启动日志
3.1 启动信息:闭源TPL+闭源SPL
- 启动顺序
TPL → SPL → U-Boot → ATF/OPTEE → Kernel- TPL+SPL存储位置
- 从启动日志可以看到SPL从0x4000扇区读取uboot镜像,所以TPL+SPL的存储区域,不在0x4000扇区
- 结合之前doc/README.rockchip,一级tools/rksd.c,rknand.c,rkspi.c的实现,对于不同的存储器,TPL+SPL存储在不同的位置
- eMMC/SD卡:存储在0x40扇区
- SPI Flash:从起始位置0x0读取。此时Flash接入的是FSPI
- NAND:从page0位置读取
3.1.1 TPL:DDR初始化
- 物质基础
- DDR颗粒
- DDR Controller
- DDR PHY
- 基础参数
BW=32 Col=10 Bk=8 CS0 Row=16 CS1 Row=16 CS=2 Die BW=16 Size=4096MB BW=32:DDR控制器总线位宽32bit Col=10:列地址宽度10bit,2^10 = 1024列 Bk=8:Bank数量,8个Bank CS0 Rw=16:行地址宽度,16bit CS1 Row=16:行地址宽度,16iit CS=2:片选信号数量,2个片选(CS0,CS1) Die BW=16:单颗DDR颗粒(DIE)位宽,16bit Size=4096MB:总DDR容量,4GB- 时钟训练:1560MHz
change to: 324MHz PHY drv:clk:36,ca:36,DQ:29,odt:0 vrefinner:24%, vrefout:41% dram drv:40,odt:0 clk skew:0x65 change to: 528MHz ... clk skew:0x58 change to: 780MHz ... clk skew:0x58 change to: 1560MHz(final freq) ... vref_ca:00000071 clk skew:0x30- DDR PHY训练
- 本质:寻找 DQ 数据相对于 DQS 选通时钟的有效采样窗口(Eye Diagram 眼图窗口)
- 每一组 DQS 管理 4 根 DQ 信号(LPDDR4X:4 DQS Lane,每 Lane 4bit DQ,16bit/CS)
- cs0 / cs1 对应两个 DDR 片选,独立训练互不干扰
- 所有数值单位:PHY 内部延迟步长(delay tap,tap 值),tap 越大代表相位偏移越大
- Read Training
- 读训练:SoC 接收 DDR 发来的数据,调节 DQS 采样时钟相位,在窗口中间位置采样 DQ
- DQS0:0x32, DQS1:0x32, DQS2:0x32, DQS3:0x2e:DQS0~DQS3 四组差分选通信号各自独立最优相位
- min/mid/max/rang含义:单根 DQ 的眼图扫描结果;range代表有效眼图窗口宽度,窗口越大,信号裕量越大,高低温、电压波动下越稳定
- Wirte Training
- 写训练:SoC 发送数据给 DDR 颗粒,调节 DQ 数据相对于 DQS 随路时钟的发送相位,保证 DDR 颗粒端满足建立 / 保持时间
- DQS0:0x27, DQS1:0x19, DQS2:0x19, DQS3:0xa
- min/mid/max/rang含义:单根 DQ 的眼图扫描结果;range代表有效眼图窗口宽度,窗口越大,信号裕量越大,高低温、电压波动下越稳定
- 本质:寻找 DQ 数据相对于 DQS 选通时钟的有效采样窗口(Eye Diagram 眼图窗口)
- DDR PHY训练日志
cs 0: the read training result: DQS0:0x32, DQS1:0x32, DQS2:0x32, DQS3:0x2e, min : 0xb 0xd ... mid :0x26 0x29 ... max :0x42 0x45 ... range:0x37 0x38 ... the write training result: DQS0:0x27, DQS1:0x19, DQS2:0x19, DQS3:0xa, min :0x6a 0x70 ... mid :0x87 0x8b ... max :0xa4 0xa6 ... range:0x3a 0x36 ... cs 1: the read training result: DQS0:0x33, DQS1:0x33, DQS2:0x33, DQS3:0x30, min : 0xb 0xd ... mid :0x26 0x29 ... max :0x42 0x45 ... range:0x37 0x38 ... the write training result: DQS0:0x27, DQS1:0x19, DQS2:0x19, DQS3:0xa, min :0x6b 0x71 ... mid :0x88 0x8c ... max :0xa6 0xa8 ... range:0x3b 0x37 ...- CA训练:Command/Address 地址命令总线
- 本质:针对地址命令通路,保证 DDR 颗粒能够正确采样 SoC 发出的地址与命令
- 原理:SoC DDR PHY 不断调整 CA 信号相对于 DDR CLK 时钟的输出相位,来回扫描相位
- 相位太超前 → DDR 无法正确采样命令,报错
- 相位太滞后 → DDR 同样采样失败
- 找到【能够稳定识别地址命令】的相位区间 [min, max]。PHY 最终选用 mid 中点位置输出 CA 信号,建立充足的建立 / 保持时间
- CA训练日志
CA Training result: cs:0 min :0x51 0x4d 0x47 0x40 0x47 0x40 0x4b ,0x4b 0x46 0x42 0x3a 0x3f 0x3b 0x4b , cs:0 mid :0x8d 0x8c 0x84 0x81 0x84 0x7f 0x7b ,0x88 0x86 0x7f 0x79 0x7c 0x7b 0x7a , cs:0 max :0xc9 0xcb 0xc2 0xc2 0xc2 0xbe 0xac ,0xc6 0xc7 0xbc 0xb8 0xb9 0xbb 0xaa , cs:0 range:0x78 0x7e 0x7b 0x82 0x7b 0x7e 0x61 ,0x7b 0x81 0x7a 0x7e 0x7a 0x80 0x5f , cs:1 min :0x4f 0x56 0x47 0x47 0x48 0x45 0x4d ,0x4c 0x4d 0x43 0x43 0x40 0x44 0x4c , cs:1 mid :0x90 0x91 0x86 0x84 0x88 0x81 0x7f ,0x8c 0x89 0x83 0x7d 0x7f 0x80 0x7c , cs:1 max :0xd1 0xcc 0xc6 0xc2 0xc8 0xbe 0xb1 ,0xcd 0xc6 0xc3 0xb8 0xbf 0xbd 0xad , cs:1 range:0x82 0x76 0x7f 0x7b 0x80 0x79 0x64 ,0x81 0x79 0x80 0x75 0x7f 0x79 0x61 ,3.1.2 SPL初始化
- 启动顺序
SPL → ATF (BL31 EL3) → OPTEE (BL32) → U-Boot (BL33 Normal World)- 镜像加载日志
U-Boot SPL board init U-Boot SPL 2017.09-gaaca6ffec1-211203 #zzz (Dec 03 2021 - 18:42:16) unknown raw ID phN unrecognized JEDEC id bytes: 00, 00, 00 //从MMC2启动 Trying to boot from MMC2 MMC error: The cmd index is 1, ret is -110 Card did not respond to voltage select! mmc_init: -95, time 10 spl: mmc init failed with error: -95 //从MMC1启动 Trying to boot from MMC1 SPL: A/B-slot: _a, successful: 0, tries-remain: 7 Trying fit image at 0x4000 sector ## Verified-boot: 0 ## Checking atf-1 0x00040000 ... sha256(6204b6f381...) + OK ## Checking uboot 0x00a00000 ... sha256(d4bf748090...) + OK ## Checking fdt 0x00b45ed8 ... sha256(036cca5880...) + OK ## Checking atf-2 0xfdcc1000 ... sha256(5563d929da...) + OK ## Checking atf-3 0x0006a000 ... sha256(b04372ab0f...) + OK ## Checking atf-4 0xfdcce000 ... sha256(2f8839c803...) + OK ## Checking atf-5 0xfdcd0000 ... sha256(b46eaa95b8...) + OK ## Checking atf-6 0x00068000 ... sha256(6e9d32ba23...) + OK ## Checking optee 0x08400000 ... sha256(66bbd17352...) + OK Jumping to U-Boot(0x00a00000) via ARM Trusted Firmware(0x00040000) Total: 236.477 ms //跳转到ARM Trusted Firmware执行 INFO: Preloader serial: 2 NOTICE: BL31: v2.3():v2.3-365-gae7c295ca:derrick.huang NOTICE: BL31: Built : 15:37:13, May 17 2022 INFO: GICv3 without legacy support detected. INFO: ARM GICv3 driver initialized in EL3 INFO: pmu v1 is valid 220114 INFO: dfs DDR fsp_param[0].freq_mhz= 1560MHz INFO: dfs DDR fsp_param[1].freq_mhz= 324MHz INFO: dfs DDR fsp_param[2].freq_mhz= 528MHz INFO: dfs DDR fsp_param[3].freq_mhz= 780MHz INFO: Using opteed sec cpu_context! INFO: boot cpu mask: 0 INFO: BL31: Initializing runtime services INFO: BL31: Initializing BL32 //OP-TEE程序执行 I/TC: I/TC: OP-TEE version: 3.13.0-641-g4167319d3 #hisping.lin (gcc version 10.2.1 20201103 (GNU Toolchain for the A-profile Architecture 10.2-2020.11 (arm-10.16))) #8 Wed Mar 16 15:14:56 CST 2022 aarch64 I/TC: Primary CPU initializing I/TC: Primary CPU switching to normal world boot INFO: BL31: Preparing for EL3 exit to normal world INFO: Entry point address = 0xa00000 INFO: SPSR = 0x3c9- 镜像加载:MMC2/MMC1,A/B-solt
- SPL分别从MMC2(SD卡)和MMC1(eMMC)启动,最终MMC1启动成功
- 如何理解MMC2和MMC1,它们分别对应什么SoC上的什么硬件IP
- RK A/B 分区机制:当前启动槽位为slot_a;本次启动成功标记为 0(尚未完成成功计数),剩余重试次数 7 次;升级失败时依靠重试计数实现回滚
- 从eMMC的0x4000扇区读取FIT打包镜像:trust分区内fit镜像,包含ATF、OP-TEE、U-Boot、DTB
- 0x4000,是uboot.img镜像烧录的位置,uboot.img镜像打包的规则是什么
- SPL分别从MMC2(SD卡)和MMC1(eMMC)启动,最终MMC1启动成功
- 镜像校验和加载到DDR
- 校验顺序:ATF-1,uboot,fdt,ATF-2,ATF-3,ATF-4,ATF-5,ATF-6,OP-TEE
- 说明镜像打包顺序大概也是如此
- 镜像加载到DDR
- atf-1 0x00040000
- uboot 0x00a00000
- fdt 0x00b45ed8
- atf-2 0xfdcc1000
- atf-3 0x0006a000
- atf-4 0xfdcce000
- atf-5 0xfdcd0000
- atf-6 0x00068000
- optee 0x08400000
- 校验顺序:ATF-1,uboot,fdt,ATF-2,ATF-3,ATF-4,ATF-5,ATF-6,OP-TEE
- SPL跳转ATF:Jumping to U-Boot(0x00a00000) via ARM Trusted Firmware(0x00040000)
- ATF BL31:EL3安全世界
- OP-TEE BL32启动
3.2 关键概念理解
3.2.1 ARM安全世界
- Exception Levels:异常等级
- EL0:无特权执行,用于运行普通的应用程序
- 无法访问任何系统配置寄存器
- 所有对系统资源的访问都受到严格限制
- EL1:操作系统内核
- 可以访问管理内存管理单元(MMU)、处理中断等所需的系统寄存器
- EL2:虚拟机监视器,提供对虚拟化的支持
- 可以访问控制第二级地址翻译(Stage 2 MMU)的寄存器,用于管理多个虚拟机
- EL3:安全监视器,等级最高
- 可以访问所有系统控制资源
- ATF的核心部分就运行在EL3,负责安全世界和普通世界之间的切换
- 异常切换:单行道规则
- 向上切换:通过异常请求更高级的服务。EL0->EL1,SVC(Supervisor Call);EL1->EL2,HVC(Hypervisor Call);EL2->EL3,SMC(Secure Monitor Call)
- 向下切换:通过异常返回回到低特权等级
- EL0->EL3,需要硬件授权(SCR_EL3.SMD);EL1->EL3,架构允许;EL0->EL2,架构允许。
- EL0:无特权执行,用于运行普通的应用程序
- Secure World和Normal World
- Secure World:运行一个可信操作系统(如OP-TEE),专门处理指纹、支付等敏感任务
- Normal World:运行Linux/Android等富操作系统,功能丰富但不被信任处理敏感信息
- 两个世界在同一颗CPU核心上分时运行,通过运行在EL3的ATF进行安全切换
- TurstZone:隔离出安全内存,外设,中断
- 是贯穿整个SoC(系统级芯片)的系统级安全架构
- 这样一个架构由哪些关键模块组成
- 核心思想:硬件强制隔离
- 给硬件打上Secure World的标签,只有来自Secure World的请求才能访问硬件
- 如何隔离:CPU,内存,存储器;电源,时钟,中断,寄存器
- 物质基础
- NS(Non-Secure Bit):AXI系统总线在每个读写信道增加一个额外的NS控制信号。CPU在Normal World执行时,发出的所有内存访问请求,NS都会被硬件强制设为1;相反,在Secure World发出的请求,NS位为0。总线上从设备会检查请求的NS位,如果非安全请求视图访问被标记为安全的内存区域,硬件会直接拒绝这次访问
- 地址空间控制器(TZASC):TZASC是一个硬件过滤器,位于总线与内存之间。他将物理内存划分为安全区域和非安全区域。只有Secure World的请求才能访问安全内存区域
- 安全外设与中断::通过AXI-to-APB桥等硬件逻辑,可以配置某些外设(如键盘、加解密引擎)为“安全外设”;通过通用中断控制器(GIC),可以将中断配置为安全或非安全
- 是贯穿整个SoC(系统级芯片)的系统级安全架构
- ATF:ARM Trusted Firmware
- 运行在EL3
- 管理安全世界的物质
- 配置TZASC
- 配置GIC
- 管理CPU上下文:Secure World和Normal World切换时,ATF需要保存和恢复CPU的寄存器状态
- 通过SMC调用提供ATF服务
- Normal World需要ATF的服务,会执行一条**SMC(Secure Monitor Call)**指令
- 指令产生一个异常,将CPU控制器交给EL3的ATF
- ATF根据SMC的ID,执行相应服务,然后返回结果
- ATF都提供了哪些服务,如何获取这些服务
- PSCI(电源状态协调接口),用于管理CPU的开关、休眠等
- OP-TEE
- 为可信应用(Trusted Application),提供以下安全运行环境
- 强大的隔离性:通过硬件TrustZone,将TA与普通实际的所有软件彻底隔离。即使普通世界被攻破,也无法访问TA的代码和数据
- 安全的执行环境:确保TA执行时的上下文不被普通世界或其他TA窥探或修改
- 可信的通信通道:定义标准API,确保普通世界的CA与TA的每一次通信都是安全可控的
- 安全的存储服务:提供加密存储服务,TA可在此安全地存储密钥等敏感数据,防止被普通世界读取
- 为可信应用(Trusted Application),提供以下安全运行环境
- 硬件信任根
- BL1 -> BL2 -> BL31 -> BL32 -> BL33:Boot Loader
- 信任链:逐级验证下一级软件的签名,任何一级验证失败,启动过程都会停
- 无信任链:系统不加验证地执行任何代码,一旦引导程序被恶意替换,整个系统就会被攻击者完全控制
- BL1:BootROM,信任根。加载并验证下一个镜像BL2的完整性和签名
- BL2:TPL+SPL。可信启动固件,运行安全世界,完成DDR初始化
- BL31:EL3 Runtime Firmware。
- BL32:Secure-EL1 Playlaod,如OP-TEE。通常是一个可信执行环境(TEE) 的操作系统。运行在安全世界,为可信应用(TA) 提供安全的运行环境
- BL33:Non-trusted Firmware,如U-Boot。职责是加载并启动操作系统
- BL1 -> BL2 -> BL31 -> BL32 -> BL33:Boot Loader
- 32位和64位架构差异
- 32位,没有ATF,可以选择集成OP-TEE:BL1 -> BL2 -> BL32 -> BL33
- 64位,完整信任链:BL1 -> BL2 -> BL31 -> BL32 -> BL33
3.3.2 OP-TEE详解
- 是什么
- OP-TEE是运行在安全世界的软件,TrustZone是它的物质基础
- OP-TEE OS运行在安全世界的EL1特权级,是TEE的操作系统
- OP-TEE与TAs(Trusted Applications)相辅相成,OP-TEE通过GlobalPlatform TEE Internal Core API向TAs提供服务
- OP-TEE Client运行在Normal World,提供TEE Client API库(libteec.so),供普通客户但应用
- 开源网站
- OP-TEE操作系统:https://github.com/OP-TEE/optee_os
- 运行在安全世界(Secure World)的可信操作系统(TEE OS) 核心
- OP-TEE客户端:https://github.com/OP-TEE/optee_client
- 运行在普通世界(Normal World)用户空间的客户端库,提供TEE Client API
- OP-TEE Linux驱动:https://github.com/OP-TEE/optee_linuxdriver
- 运行在Linux内核空间的设备驱动,处理普通世界与安全世界间的通信
- OP-TEE 示例应用:https://github.com/linaro-swg/optee_examples
- 包含示例性质的客户端应用(CA)和可信应用(TA)
- OP-TEE 测试套件:https://github.com/OP-TEE/optee_test
- xtest测试套件,用于验证OP-TEE的功能和API一致性
- OP-TEE操作系统:https://github.com/OP-TEE/optee_os
- 应用场景
- OP-TEE已被广泛应用于全盘加密、安全存储密钥、区块链钱包
3.3.3 A/B分区
- A/B分区是什么
- A/B System即把系统固件分为两份,系统可以从其中一个slot上启动
- 当一份启动失败后,可以从另一份上启动
- 升级时,可以直接将固件拷贝到另一个slot上,而无需进入系统升级模式:把带升级的镜像直接写入备份slot,下次启动的标记从新的系统启动
- A/B分区启动的三个物质层面
- 编译配置:支持A/B分区检测
- 分区表:在制作存储器镜像的时候,预留两个分区用来存储系统固件
- 启动代码:检测分区固件,选择从某个分区启动
- Preloader A/B分区物质基础:对于preloader,也就是TPL+SPL。这里就需要给uboot.img准备两份分区来存放uboot.img
- uboot A/B分区物质基础:对于uboot,去启动kernel。内核镜像需备份
//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 //分区表:的分区必须增加后缀_a 和_b 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@0x0000c 000(misc),0x00001000@0x0000d000(vbmeta_a),0x00001000@0x0000e000(vbmeta_b),0x00020 000@0x0000e000(boot_a),0x00020000@0x0002e000(boot_b),0x00100000@0x0004e000(system _a),0x00300000@0x0032e000(system_b),0x00100000@0x0062e000(vendor_a),0x00100000@0x 0072e000(vendor_b),0x00002000@0x0082e000(oem_a),0x00002000@0x00830000(oem_b),0x00 10000@0x00832000(factory),0x00008000@0x842000(factory_bootloader),0x00080000@0x00 8ca000(oem),-@0x0094a000(userdata)3.3.4 DDR内存
物质基础
- 组成
- 存储单元:本质是电子开关+电容
- DDR芯片:Bank组成。每个Bank由多个Row组成,每个Row由多个Column组成
- 信号:DQ(数据线),DQS(数据选通),CA(命令/地址),CK(时钟)
- 组成
DDR初始化:对抗物理差异
- 时钟频率选择
- CA粗调:使用默认延迟值。通常由PCB等长布线保证
- MRS配置:Mode Register Set。配置模式寄存器,告诉DRAM芯片,CAS延迟是多少,突出长度是多少,写延迟是多少
- ZQ校准:控制器通过MRS命令触发DRAM内部的ZQ校准单元。DRAM会调整其DQ引脚的输出驱动强度和ODT(片上终端电阻)
- 读训练:控制器依赖CA粗调发送读命令,从阵列中读出已知数据,PHY逐步调整读DQS的延迟,扫描出DQ数据稳定的串口,选取中心点
- 写训练:依赖读路径已完成校准。采用写->回读->校验的方式调整写DQ相对写DQS的发送延迟
- CA训练:对CA信号线进行精细校准
时钟频率选择
- CLK是DDR芯片内部的全局心跳和时间基准
- 驱动内部时钟树:CLK进入DDR芯片内部,通过内部时钟树,分布到命令解码器、地址锁存器、模式寄存器等所有同步逻辑逻辑电路的触发器中
- 命令/地址的采样基准
- CLK是DDR芯片内部的全局心跳和时间基准
DQS采样训练
- DQS用于DQ总线,在极高频率下,信号从控制器到DRAM需要时间。CLK无法准确预测数据何时到达
- DQS和DQ从控制器同时出发,经过相同额物理路ing延迟,伴随数据一起达到接收端
- DQS训练的目的就是让DQS和DQ的信号保持同步,避免相位延迟导致数据采样错误
4 开源SPL:实战
4.1 SPL功能
- SPL相关内容
- 读取FIT镜像并校验
- 跳转ATF执行
- 跳转OP-TEE执行
- 实战项目内容
- 掌握SPL镜像打包方式
- 增加FIT镜像校验——能否增加自定义校验
- 掌握ATF集成方法和应用场景
- 掌握OP-TEE集成方法和应用场景
- 掌握A/B分区烧录和跳转策略
- 结合实际使用场景进行SPL特化
- 问题锚点
- TPL中是否包含二进制设备树?若存在,打包规则是如何定义的?
- SPL中是否包含二进制设备树?若存在,打包规则是如何定义的?
4.2 TPL(闭源)+SPL(开源):项目规划
- SPL编译打包流程验证
- 开启SPL相关的编译生成
- 修改TPL+SPL打包规则,使用开源SPL:rkbin/RKBOOT/RK3568MINIALL.ini
- 下载TPL+SPL = MiniLoaderAll.bin,查看日志进行验证
- SPL功能控制:仅针对我想实现的功能进行修改
- 镜像加载和校验:跟踪校验了镜像HASH值源码;尝试增加身份签名校验
- A/B分区:通过直接在代码中控制读取A或B分区实现代码控制;再修改代码,实现A/B分区自主选择
- 镜像跳转:目前镜像转交给了ATF,由ATF跳转到OP-TEE,再跳转到U-Boot执行
- 尝试能否自定义编译ATF
- 尝试能否自定义编译OP-TEE
- 尝试能否跳过ATF,直接进入OP-TEE
- 尝试能否跳过OP-TEE,直接进入U-Boot
- 尝试跳过ATF和OP-TEE,直接进入U-Boot