[TOC]
基础简介
Feature
v2017(next-dev) 是 RK 从 U-Boot 官方的 v2017.09 正式版本中切出来进行开发的版本,目前已经支持 RK 所有主流在售芯片。支持的功能主要有:
- 支持 RK Android 固件启动;
- 支持 Android AOSP 固件启动;
- 支持 Linux Distro 固件启动;
- 支持 Rockchip miniloader 和 SPL/TPL 两种 Pre-loader 引导;
- 支持 LVDS、EDP、MIPI、HDMI、CVBS、RGB 等显示设备;
- 支持 eMMC、Nand Flash、SPI Nand flash、SPI NOR flash、SD 卡、 U 盘等存储设备启动;
- 支持 FAT、EXT2、EXT4 文件系统;
- 支持 GPT、RK parameter 分区表;
- 支持开机 LOGO、充电动画、低电管理、电源管理;
- 支持 I2C、PMIC、CHARGE、FUEL GUAGE、USB、GPIO、PWM、GMAC、eMMC、NAND、Interrupt 等;
- 支持 Vendor storage 保存用户的数据和配置;
- 支持 RockUSB 和 Google Fastboot 两种 USB gadget 烧写 eMMC;
- 支持 Mass storage、ethernet、HID 等 USB 设备;
- 支持通过硬件状态动态选择 kernel DTB;
Version
RK 的 U-Boot 一共有两个版本:v2014旧版本和v2017新版本,内部名称分别为rkdevelop和next-dev。用户有两个方式确认当前U-Boot是否为v2017版本。
方式1:确认根目录Makefile的版本号是否为2017。
#
# SPDX-License-Identifier: GPL-2.0+
#
VERSION = 2017
PATCHLEVEL = 09
SUBLEVEL =
EXTRAVERSION =
NAME =
......
方式2:确认开机第一行正式打印是否为 U-Boot 2017.09。
U-Boot 2017.09-01818-g11818ff-dirty (Nov 14 2019 - 11:11:47 +0800)
......
项目开源:v2017已开源且定期更新到Github:https://github.com/rockchip-linux/u-boot
内核版本:v2017要求RK内核版本 >= 4.4
DM
DM (Driver Model) 是 U-Boot 标准的 device-driver 开发模型,跟 kernel 的 device-driver 模型非常类似。v2017版本也遵循 DM 框架开发各功能模块。建议读者先阅读DM文档,了解DM架构原理和实现。
README:
./doc/driver-model/README.txt
Terminology
-----------
Uclass - a group of devices which operate in the same way. A uclass provides
a way of accessing individual devices within the group, but always
using the same interface. For example a GPIO uclass provides
operations for get/set value. An I2C uclass may have 10 I2C ports,
4 with one driver, and 6 with another.
Driver - some code which talks to a peripheral and presents a higher-level
interface to it.
Device - an instance of a driver, tied to a particular port or peripheral.
简要概括:
- uclass:设备驱动模型
- driver: 驱动
- device:设备
核心代码:
./drivers/core/
Security
U-Boot在ARM TrustZone的安全体系中属于Non-Secure World,无法直接访问任何安全的资源(如:安全 memory、安全 otp、efuse...),需要借助 trust 间接访问。RK平台上U-Boot的CPU运行模式:
32位平台: Non-Secure PL1
64位平台: EL2(Always be Non-Secure)
Boot-order
RK平台根据前级Loader代码是否开源,目前有两套启动方式:
// 前级loader闭源
BOOTROM => ddr bin => Miniloader => TRUST => U-BOOT => KERNEL
// 前级loader开源
BOOTROM => TPL => SPL => TRUST => U-BOOT => KERNEL
TPL 相当于 ddr bin,SPL 相当于 miniloader。TPL+SPL 的组合实现了跟 RK 闭源 ddr.bin+miniloader 一致的功能,可相互替换。
Driver-probe
U-Boot虽然引入了device-driver开发模型,但初始化阶段不会像kernel那样自动发起已注册device-driver的probe。driver的probe必须由用户主动调用发起。接口如下:
int uclass_get_device(enum uclass_id id, int index, struct udevice **devp);
int uclass_get_device_by_name(enum uclass_id id, const char *name,
struct udevice **devp);
int uclass_get_device_by_seq(enum uclass_id id, int seq, struct udevice **devp);
int uclass_get_device_by_of_offset(enum uclass_id id, int node, struct udevice **devp);
int uclass_get_device_by_ofnode(enum uclass_id id, ofnode node, struct udevice **devp);
int uclass_get_device_by_phandle_id(enum uclass_id id,
int phandle_id, struct udevice **devp);
int uclass_get_device_by_phandle(enum uclass_id id,
struct udevice *parent, struct udevice **devp);
int uclass_get_device_by_driver(enum uclass_id id,
const struct driver *drv, struct udevice **devp);
int uclass_get_device_tail(struct udevice *dev, int ret, struct udevice **devp);
......
上述接口的核心调用:
int device_probe(struct udevice *dev); // 建议用户一定要了解内部实现!
Shell
U-Boot的Shell叫CLI(cmdline line interface),即命令行模式,用户可以根据自己需求自定义CMD。CMD除了通过Shell调用,还能通过run_command()和run_command_list()以代码的形式调用。
int run_command(const char *cmd, int flag)
int run_command_list(const char *cmd, int len, int flag)
Boot-Command
U-Boot 最终通过CONFIG_BOOTCOMMAND定义的启动命令引导kernel。在执行CONFIG_BOOTCMD之前还会执行CONFIG_PREBOOT预启动命令,通常这个命令定义为空。
TPL/SPL/U-Boot-proper
U-Boot 通过使用不同的编译条件可以用同一套代码获取三种不同功能的Loader:TPL/SPL/U-Boot-proper。
TPL(Tiny Program Loader)和 SPL(Secondary Program Loader)是比 U-Boot 更早阶段的 Loader:
-
TPL:运行在 sram 中,负责完成 ddr 初始化;
-
SPL:运行在 ddr 中,负责完成系统的 lowlevel 初始化、后级固件加载(trust.img 和 uboot.img);
-
U-Boot proper:运行在ddr中,即我们通常所说的"U-Boot",它负责引导kernel;
说明:U-Boot proper 这一说法主要是为了和 SPL 区分开。出于习惯,后续章节提到的 U-Boot proper 我们都简称为 U-Boot。
启动流程:
BOOTROM => TPL(ddr bin) => SPL(miniloader) => TRUST => U-BOOT => KERNEL
更多参考:doc/README.TPL 和 doc/README.SPL
Build-Output
U-Boot编译成功后会在根目录下生成一些重要文件(支持TPL/SPL编译时才有TPL/SPL的生成文件):
// U-Boot阶段
./u-boot.map // MAP表文件
./u-boot.sym // SYMBOL表文件
./u-boot // ELF文件,类同内核的vmlinux(重要!)
./u-boot.dtb // u-boot自己的dtb文件
./u-boot.bin // 可执行二进制文件,会被打包成uboot.img用于烧写
// SPL阶段
./spl/u-boot-spl.map // MAP表文件
./spl/u-boot-spl.sym // SYMBOL表文件
./spl/u-boot-spl // ELF文件,类同内核的vmlinux(重要!)
./spl/u-boot-spl.dtb // spl自己的dtb文件
./spl/u-boot-spl.bin // 可执行二进制文件,会被打包成loader用于烧写
// TPL阶段
./tpl/u-boot-tpl.map // MAP表文件
./tpl/u-boot-tpl.sym // SYMBOL表文件
./tpl/u-boot-tpl // ELF文件,类同内核的vmlinux(重要!)
./tpl/u-boot-tpl.dtb // tpl自己的dtb文件
./tpl/u-boot-tpl.bin // 可执行二进制文件,会被打包成loader用于烧写
Environment-Variables
ENV(Environment-Variables) 是U-Boot支持的一种全局数据管理和传递方式,原理是构建一张HASH映射表,把用户的数据以"键值-数据" 作为表项进行管理。
EVN 通常用于定义平台配置参数:固件加载地址、网络配置(ipaddr、serverip)、bootcmd、bootargs等,用户可以在命令行下使用printenv命令打印出来。
- 用户可选择是否把ENV数据保存到本地存储上
- ENV数据仅限于U-Boot使用,无法直接传递给内核、内核也无法直接解析
- 用户层可以通过U-Boot提供的fw_printenv工具访问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
U-Boot DTS
U-Boot有自己的DTS文件,编译时会自动生成相应的DTB文件,被添加在u-boot.bin末尾。文件目录:
arch/arm/dts/
各平台具体使用哪个DTS文件,通过defconfig中的 CONFIG_DEFAULT_DEVICE_TREE 指定。
Relocation
通常在开机阶段,U-Boot由前一级的bootloader加载到DRAM的低地址。U-Boot完成board_f.c 的流程后会把自己重定向到内存末尾某个预留的地址上(称为relocation,这个地址根据U-Boot内存布局而定),完成relocation再继续完成board_r.c 流程。可以通过开机信息识别:
U-Boot 2017.09-gabfd1c5e3d-210202-dirty #cjh (Mar 08 2021 - 16:57:31 +0800)
Model: Rockchip RK3568 Evaluation Board
PreSerial: 2, raw, 0xfe660000
DRAM: 2 GiB
Sysmem: init
// relocate到ddr首地址偏移0x7d304000的地址。如果为0,则没有做relocation。
Relocation Offset: 7d304000, fdt: 7b9f8ed8
Using default environment
......