跳到主要内容

[TOC]

SPL

固件引导

SPL 的作用是代替miniloader完成 trust.img 和 uboot.img的加载和引导工作。SPL 目前支持引导两种固件:

  • FIT 固件:默认使能;
  • RKFW 固件:默认关闭,需要用户单独配置和使能;

FIT 固件

FIT(flattened image tree)格式是 SPL 支持的一种比较新颖的固件格式,支持多个 image 打包和校验。FIT 使用 DTS 的语法对打包的 image 进行描述,描述文件为 u-boot.its,最终生成的 FIT 固件为 u-boot.itb。

FIT的优点:复用 dts 的语法和编译规则,比较灵活,固件解析可以直接使用 libfdt 库。

u-boot.its 文件:

  • /images :静态定义了所有可获取的资源配置(最后可用、可不用),类似 dtsi 的角色;
  • /configurations:每个 config 节点描述了一套可启动的配置,类似一个板级 dts。
  • 使用default = 指定当前选用的默认配置;

范例:

/dts-v1/;

/ {
description = "Configuration to load ATF before U-Boot";
#address-cells = <1>;

images {
uboot@1 {
description = "U-Boot (64-bit)";
data = /incbin/("u-boot-nodtb.bin");
type = "standalone";
os = "U-Boot";
arch = "arm64";
compression = "none";
load = <0x00200000>;
};

atf@1 {
description = "ARM Trusted Firmware";
data = /incbin/("bl31_0x00010000.bin");
type = "firmware";
arch = "arm64";
os = "arm-trusted-firmware";
compression = "none";
load = <0x00010000>;
entry = <0x00010000>;
};

atf@2 {
description = "ARM Trusted Firmware";
data = /incbin/("bl31_0xff091000.bin");
type = "firmware";
arch = "arm64";
os = "arm-trusted-firmware";
compression = "none";
load = <0xff091000>;
};

optee@1 {
description = "OP-TEE";
data = /incbin/("bl32.bin");
type = "firmware";
arch = "arm64";
os = "op-tee";
compression = "none";
load = <0x08400000>;
};

fdt@1 {
description = "rk3328-evb.dtb";
data = /incbin/("arch/arm/dts/rk3328-evb.dtb");
type = "flat_dt";
compression = "none";
};
};

configurations {
default = "config@1";
config@1 {
description = "rk3328-evb.dtb";
firmware = "atf@1";
loadables = "uboot@1", "atf@2", "optee@1" ;
fdt = "fdt@1";
};
};
};

u-boot.itb 文件:

                          mkimage + dtc
[u-boot.its] + [images] ==> [u-boot.itb]

上述是itb文件的生成过程。FIT 固件可以理解为一种特殊的 DTB 文件,只是它的内容是 image。用户可以用 fdtdump 命令查看 itb文件:

cjh@ubuntu:~/uboot-nextdev/u-boot$ fdtdump u-boot.itb | less

/dts-v1/;
// magic: 0xd00dfeed
// totalsize: 0x497 (1175)
// off_dt_struct: 0x38
// off_dt_strings: 0x414
// off_mem_rsvmap: 0x28
// version: 17
// last_comp_version: 16
// boot_cpuid_phys: 0x0
// size_dt_strings: 0x83
// size_dt_struct: 0x3dc

/ {
timestamp = <0x5d099c85>;
description = "Configuration to load ATF before U-Boot";
#address-cells = <0x00000001>;
images {
uboot@1 {
data-size = <0x0009f8a8>;
data-offset = <0x00000000>;
description = "U-Boot (64-bit)";
type = "standalone";
os = "U-Boot";
arch = "arm64";
compression = "none";
load = <0x00600000>;
};
atf@1 {
data-size = <0x0000c048>; // 编译过程自动增加了该字段,描述atf@1固件大小
data-offset = <0x0009f8a8>; // 编译过程自动增加了该字段,描述atf@1固件偏移
description = "ARM Trusted Firmware";
type = "firmware";
arch = "arm64";
os = "arm-trusted-firmware";
compression = "none";
load = <0x00010000>;
entry = <0x00010000>;
};
atf@2 {
data-size = <0x00002000>;
data-offset = <0x000ab8f0>;
description = "ARM Trusted Firmware";
type = "firmware";
arch = "arm64";
os = "arm-trusted-firmware";
compression = "none";
load = <0xfff82000>;
};
fdt@1 {
data-size = <0x00005793>;
data-offset = <0x000ad8f0>;
description = "rk3308-evb.dtb";
type = "flat_dt";
......
};
......
};
};

更多 FIT 信息请参考:

./doc/uImage.FIT/

RKFW 固件

为了能更直接替换掉 miniloader 且不用修改后级固件的分区、打包格式。因此RK平台增加了RKFW 格式(即独立分区的固件:trust.img 和 uboot.img)的引导。

配置:

CONFIG_SPL_LOAD_RKFW           // 使能开关
CONFIG_RKFW_TRUST_SECTOR // trust.img分区地址,需要和分区表的定义保持一致
CONFIG_RKFW_U_BOOT_SECTOR // uboot.img分区地址,需要和分区表的定义保持一致

代码:

./include/spl_rkfw.h
./common/spl/spl_rkfw.c

存储优先级

U-Boot dts 中通过u-boot,spl-boot-order 指定存储设备的启动优先级。

/ {
aliases {
mmc0 = &emmc;
mmc1 = &sdmmc;
};

chosen {
u-boot,spl-boot-order = &sdmmc, &nandc, &emmc;
stdout-path = &uart2;
};
......
};

编译打包

代码编译

U-Boot 根据不同的编译路径 对同一份U-Boot代码编译获得SPL固件,当编译 SPL 时会自动生成CONFIG_SPL_BUILD 宏。U-Boot会在编译完 u-boot.bin 之后继续编译 SPL,并创建独立的输出目录./spl/

  // 编译u-boot
......
DTC arch/arm/dts/rk3399-puma-ddr1866.dtb
DTC arch/arm/dts/rv1108-evb.dtb
make[2]: `arch/arm/dts/rk3328-evb.dtb' is up to date.
SHIPPED dts/dt.dtb
FDTGREP dts/dt-spl.dtb
CAT u-boot-dtb.bin
MKIMAGE u-boot.img
COPY u-boot.dtb
MKIMAGE u-boot-dtb.img
COPY u-boot.bin

// 编译spl,有独立的spl/目录
LD spl/arch/arm/cpu/built-in.o
CC spl/board/rockchip/evb_rk3328/evb-rk3328.o
LD spl/dts/built-in.o
CC spl/common/init/board_init.o
COPY tpl/u-boot-tpl.dtb
CC spl/cmd/nvedit.o
CC spl/env/common.o
CC spl/env/env.o
.....
LD spl/drivers/block/built-in.o

......

编译结束后得到:

./spl/u-boot-spl.bin

固件打包

系统模块

GPT

SPL 使用GPT分区表。

配置:

CONFIG_SPL_LIBDISK_SUPPORT=y
CONFIG_SPL_EFI_PARTITION=y
CONFIG_PARTITION_TYPE_GUID=y

驱动:

./disk/part.c
./disk/part_efi.c

接口:

int part_get_info(struct blk_desc *dev_desc, int part, disk_partition_t *info);
int part_get_info_by_name(struct blk_desc *dev_desc,
const char *name, disk_partition_t *info);

A/B system

SPL 支持A/B 系统启动。

配置:

CONFIG_SPL_AB=y

驱动:

./common/spl/spl_ab.c

接口:

int spl_get_current_slot(struct blk_desc *dev_desc, char *partition, char *slot);
int spl_get_partitions_sector(struct blk_desc *dev_desc, char *partition,u32 *sectors);

启动优先级

  • SPL 使用 u-boot,spl-boot-order 定义的启动顺序,位于rkxxxx-u-boot.dtsi:

    chosen {
    stdout-path = &uart2;
    u-boot,spl-boot-order = &sdmmc, &sfc, &nandc, &emmc;
    };
  • Maskrom 的启动优先级:

     spi nor > spi nand > emmc > sd
  • Pre-loader(SPL) 的启动优先级:

    sd > spi nor > spi nand > emmc

    把 sd 卡的优先级提到最高可以方便系统从 sd 卡启动。

ATAGS

SPL 与 U-Boo 通过 ATAGS 机制实现传参。传递的信息有:启动的存储设备、打印串口等。

配置:

CONFIG_ROCKCHIP_PRELOADER_ATAGS=y

驱动:

./arch/arm/include/asm/arch-rockchip/rk_atags.h
./arch/arm/mach-rockchip/rk_atags.c

接口:

int atags_set_tag(u32 magic, void *tagdata);
struct tag *atags_get_tag(u32 magic);

kernel boot

通常kernel是由U-Boot加载和引导,SPL 也可以支持加载 kernel。目前支持加载 android head version 2 的 boot.img,支持 RK格式固件。

启动顺序:

Maskrom -> ddr -> SPL -> Trust -> Kernel

pinctrl

配置:

CONFIG_SPL_PINCTRL_GENERIC=y
CONFIG_SPL_PINCTRL=y

驱动:

./drivers/pinctrl/pinctrl-uclass.c
./drivers/pinctrl/pinctrl-generic.c
./drivers/pinctrl/pinctrl-rockchip.c

DTS 配置:

以 sdmmc 为例:

&pinctrl {
u-boot,dm-spl;
};

&pcfg_pull_none_4ma {
u-boot,dm-spl;
};

&pcfg_pull_up_4ma {
u-boot,dm-spl;
};

&sdmmc {
u-boot,dm-spl;
};

&sdmmc_pin {
u-boot,dm-spl;
};

&sdmmc_clk {
u-boot,dm-spl;
};

&sdmmc_cmd {
u-boot,dm-spl;
};

&sdmmc_bus4 {
u-boot,dm-spl;
};

&sdmmc_pwren {
u-boot,dm-spl;
};

注意事项:

SPL 启用pinctrl时要修改 defconfig 里的 CONFIG_OF_SPL_REMOVE_PROPS 定义,删除其中的pinctrl-0 pinctrl-names 字段。

secure boot

[TODO]

驱动模块

MMC

配置:

CONFIG_SPL_MMC_SUPPORT=y  // 默认已使能

驱动:

./common/spl/spl_mmc.c

接口:

int spl_mmc_load_image(struct spl_image_info *spl_image,
struct spl_boot_device *bootdev);

MTD block

SPL 统一 nand、spi nand、spi nor 接口到 block 层。

配置:

// MTD 驱动支持
CONFIG_MTD=y
CONFIG_CMD_MTD_BLK=y
CONFIG_SPL_MTD_SUPPORT=y
CONFIG_MTD_BLK=y
CONFIG_MTD_DEVICE=y

// spi nand 驱动支持
CONFIG_MTD_SPI_NAND=y
CONFIG_ROCKCHIP_SFC=y
CONFIG_SPL_SPI_FLASH_SUPPORT=y
CONFIG_SPL_SPI_SUPPORT=y

// nand 驱动支持
CONFIG_NAND=y
CONFIG_CMD_NAND=y
CONFIG_NAND_ROCKCHIP=y
CONFIG_SPL_NAND_SUPPORT=y
CONFIG_SYS_NAND_U_BOOT_LOCATIONS=y
CONFIG_SYS_NAND_U_BOOT_OFFS=0x8000
CONFIG_SYS_NAND_U_BOOT_OFFS_REDUND=0x10000
// nand page size需要按真实大小定义,如果使用容量大于等于512MB的NAND,一般需要配置为4096
#define CONFIG_SYS_NAND_PAGE_SIZE 2048

// spi nor 驱动支持
CONFIG_CMD_SF=y
CONFIG_CMD_SPI=y
CONFIG_SPI_FLASH=y
CONFIG_SF_DEFAULT_MODE=0x1
CONFIG_SF_DEFAULT_SPEED=50000000
CONFIG_SPI_FLASH_GIGADEVICE=y
CONFIG_SPI_FLASH_MACRONIX=y
CONFIG_SPI_FLASH_WINBOND=y
CONFIG_SPI_FLASH_MTD=y
CONFIG_ROCKCHIP_SFC=y
CONFIG_SPL_SPI_SUPPORT=y
CONFIG_SPL_MTD_SUPPORT=y
CONFIG_SPL_SPI_FLASH_SUPPORT=y

驱动:

./common/spl/spl_mtd_blk.c

接口:

int spl_mtd_load_image(struct spl_image_info *spl_image,
struct spl_boot_device *bootdev);

OTP

用于存储不可更改数据,secure boot 中用到。

配置:

CONFIG_SPL_MISC=y
CONFIG_SPL_ROCKCHIP_SECURE_OTP=y

驱动:

./drivers/misc/misc-uclass.c
./drivers/misc/rockchip-secure-otp.S

接口:

int misc_read(struct udevice *dev, int offset, void *buf, int size);
int misc_write(struct udevice *dev, int offset, void *buf, int size);

Crypto

Secure-boot 会使用crypto完成hash、ras的计算。

配置:

CONFIG_SPL_DM_CRYPTO=y

// crypto v1 支持平台:rk3399/rk3368/rk3328/rk3229/rk3288/rk3128
CONFIG_SPL_ROCKCHIP_CRYPTO_V1=y

// crypto v2 支持平台:px30/rk3326/rk1808/rk3308
CONFIG_SPL_ROCKCHIP_CRYPTO_V2=y

驱动:

./drivers/crypto/crypto-uclass.c
./drivers/crypto/rockchip/crypto_v1.c
./drivers/crypto/rockchip/crypto_v2.c
./drivers/crypto/rockchip/crypto_v2_pka.c
./drivers/crypto/rockchip/crypto_v2_util.c

接口:

u32 crypto_algo_nbits(u32 algo);
struct udevice *crypto_get_device(u32 capability);
int crypto_sha_init(struct udevice *dev, sha_context *ctx);
int crypto_sha_update(struct udevice *dev, u32 *input, u32 len);
int crypto_sha_final(struct udevice *dev, sha_context *ctx, u8 *output);
int crypto_sha_csum(struct udevice *dev, sha_context *ctx,
char *input, u32 input_len, u8 *output);
int crypto_rsa_verify(struct udevice *dev, rsa_key *ctx, u8 *sign, u8 *output);

Uart

SPL 串口通过 rkxxxx-u-boot.dtsi 的 chosen 节点指定。以 rk3308 为例:

chosen {
stdout-path = &uart2;
};

&uart2 {
u-boot,dm-pre-reloc;
clock-frequency = <24000000>;
status = "okay";
};