【RK3399】RK3399启动引导流程分析

【RK3399】RK3399启动引导流程分析主控芯片是 big LITTLE 大小核架构 双 Cortex A72 大核 upto2 0GHz 四 Cortex A53 小核结构 upto1 5GHz Cortex A72 处理器是 Armv8

大家好,欢迎来到IT知识分享网。

00. 目录

01. RK3399概述

主控芯片是Rockchip RK3399,big.LITTLE大小核架构,双Cortex-A72大核(up to 2.0GHz) + 四Cortex-A53小核结构(up to 1.5GHz);Cortex-A72处理器是Armv8-A架构下的一款高性能、低功耗的处理器。

主要特性

  • Dual-core Cortex-A72 up to 1.8GHz
    Quad-core Cortex-A53 up to 1.4GHz
  • NPU up to 3.0TOPS
  • Mali-T860MP4 GPU
  • Dual-channel DDR3/DDR3L/LPDDR3/LPDDR4
  • 4K UHD H265/H264/VP9
  • HDR10/HLG
  • H264 encoder
  • Dual MIPI CSI and ISP
  • USB Type-C

详细参数

在这里插入图片描述

02. SOC启动流程

2.1 BootROM介绍

一般来说,SoC厂家都会做一个ROM在SoC的内部,这个ROM很小,里面固化了上电启动的代码(一经固化,永不能改,是芯片做的时候,做进去的);这部分代码呢,我们管它叫做BootROM,也叫作一级启动程序。

2.2 初始化硬件

芯片上电后先接管系统的是SoC厂家的BootROM,它主要初始化系统,CPU 的配置,关闭看门狗,初始化时钟,初始化一些外设(比如 USB Controller、MMC Controller,Nand Controller 等);

2.3 加载程序到SRAM

当我们拿到一款新的SoC时,都会进行电路原理图设计,我们一般会在芯片外挂一些存储设备(eMMC、Nand、Nor、SDCard等)和内存(SDRAM、DDR等)电路图绘制好了。我们接着会绘制电路板,制作出板子。

有了板子还不行,我们还得往里面烧写程序。这个烧写程序,其实就是将可执行的二进制文件写到外部的存储设备上(eMMC、Nand、SD等)。系统上电启动的时候,会将他们读到内存中执行。

前面我们说了,上电后先接管系统的是SoC厂家的BootROM,其它可执行的程序(u-boot、Kernel)都放(烧写)到了外部存储器是先去执行 上;那么BootROM的代码除了去初始化硬件环境以外,还需要去外部存储器上面,将接下来可执行的程序读到内存来执行。

既然是读到内存执行,那么这个内存可以不可以是我们板载的 DDR呢?理论上是可以的,但是,SoC 厂家设计的DDR控制器呢,一般会支持很多种类型的DDR设备,并且会提供兼容性列表,SoC厂家怎么可能知道用户PCB上到底用了哪种内存呢?所以,直接把外部可执行程序读到DDR显然是不太友好的,一般来说呢,SoC都会做一个内部的小容量的SRAM ,BootROM将外部的可执行程序从外部存储器中读出来,放到SRAM去执行;

好了,现在我们引出了SRAM,引出了BootROM;那么 BootROM从具体哪个存储器读出二进制文件呢?SoC 厂家一般会支持多种启动方式,比如从eMMC 读取,从SDCard读取,从Nand Flash 读取等等;上电的时候,需要告诉它,它需要从什么样的外设来读取后面的启动二进制文件;

一般的设计思路是,做一组 Bootstrap Pin,上电的时候呢?BootROM去采集这几个IO的电平,来确认要从什么样的外部存储器来加载后续的可执行文件;比如呢,2 个 IO,2’b00 表示从Nand 启动,2’b01表示从eMMC 启动,2’b10 表示从SDCard 启动等等;

当 BootROM读到这些值后,就会去初始化对应的外设,然后来读取后面要执行的代码;这些 IO一般来说,会做成板载的拨码开关,用于调整芯片的启动方式;

这里,读取烧写的二进制的时候呢,需要注意一些细节,比如SoC 厂家告诉你,你需要先把SDCard初始化称为某种文件系统,然后把东西放进去才有效,之类的;因为文件系统是组织文件的方式,并不是裸分区;你按照A文件系统的方式放进去,然后 SoC 的 BootROM也按照 A 文件系统的方式读出来,才能够达成一致;

如果你对Mini2440这款开发板足够了解的话,你应该知道其采用的SoC型号为s3c2440,其内部有一个4kb的SRAM。其有两种启动方式:

  • 采用Nor Flash启动,0x00000000就是2MB Nor Flash实际的起始地址,由于uboot程序一般只有几百kb,可以全部烧录到Nor Flash中,因此uboot程序完全可以在Nor Flash中运行,没有拷贝到SDRAM中运行的必要;
  • 采用Nand Flash启动,片内4KB的SRAM被映射到了0x00000000,s3c2440的BootROM会自动把Nand Flash中的前4kb代码数据搬到内部SRAM中运行,那么问题来了,假设4KB代码运行到最后,我想继续运行Nand Flash剩余的代码怎么办?为了解决这个问题,uboot引入了SPL,全称Secondary Program Loader。

注意:无论是Nor Flash还是Nand Flash都是外挂在s3c2440上的的存储设备。

03. SPL介绍

前面说了,芯片上电后BootROM会根据 Bootstrap Pin 去确定从某个存储器来读可执行的二进制文件到SRAM并执行;理论上来说,这个二进制文件就可以是我们的u-boot.bin文件了;也就是 BootROM 直接加载u-boot.bin;

理论上是这样的,但是这里有一个问题,就是SRAM很贵,一般来说,SoC的片上 SRAM 都不会太大,一般4KB、8KB、16KB…256KB不等;但是呢,u-boot 编译出来却很大,好几百KB,放不下,就像我上面说的s3c2440的例子那样。

放不下怎么办?有两种办法:

  • 假设片内SRAM为4KB,uboot的前4KB程序实现uboot的重定位,即将uboot拷贝到SDRAM中运行;
  • 做一个小一点的boot程序,先让BootROM加载这个小的程序,后面再由这个小boot去加载uboot;

3.1 方案一

比如,我们的uboot有400KB,SRAM有4KB,外部SDRAM有64MB:如果使用第一种方案的话,uboot的前面4KB被加载进入SRAM 执行,uboot 被截断,我们就需要保证在uboot的前4KB代码,把板载的 SDRAM初始化好,把整个uboot拷贝到SDRAM,然后跳转到SDRAM执行;

比如我们之前介绍的Mini2440开发板从Nand Flash启动时,uboot程序就是采用的这种实现方式。

3.2 方案二

第二种方案的话,我们做一个小的uboot ,这个uboot就叫做SPL(Secondary Program Loader),它很小很小(小于SRAM大小),它先被BootROM加载到SRAM运行,那么这个SPL要做什么事情呢?最主要的就是要初始化内存控制器,然后将真正的大u-boot从外部存储器读取到SDRAM中,然后跳转到大uboot。

04. RK3399启动流程

在这里插入图片描述

如上图所示:

  • (0)上电后,BootROM开始执行,初始化时钟,关闭看门狗,关 Cache,关中断等等,根据 Bootstrap Pin 来确定启动设备,初始化外设;
  • (1) 使用外设驱动,从存储器读取SPL;

    —————- 以上部分是 SoC 厂家的事情,下面是用户要做的事情 —————-

  • (2) SPL被读到SRAM 执行,此刻,控制权以及移交到我们的SPL 了;
  • (3) SPL初始化外部SDRAM;
  • (4) SPL使用驱动从外部存储器读取uboot并放到SDRAM;
  • (5) 跳转到SDRAM中的uboot执行;
  • (6) 加载内核;

实际情况中,还需注意很多问题:

  • 编译阶段的链接地址,是否需要地址无关?
  • SPL的代码和uboot的代码是否有重合的地方?如果有,是否意味着SPL执行过的,跳转到uboot又要在执行一次?
  • 具体情况下,需要配置哪些硬件?怎么配置?

05. RK3399地址空间

5.1 地址映射

RK3399支持从内部BootROM启动,并且支持通过软件编程进行地址重映射。重映射是通过SGRF_PMU_CON0[15]控制的,当重映射设置为0是,地址0xFFFF0000被映射到BootROM,当重映射设置为1时,0xFFFF0000被映射到片内SRAM。

在这里插入图片描述

从这张图我们可以看到在进行重映射前:

  • 0x0000 0000 ~ 0xF800 0000:为DDR内存空间;
  • 0xFF8C 0000 ~ 0xFF98 0000:片内SRAM内存空间,一共192KB;
  • 0xFFFF 0000~ 0xFFFF 8000:为BootROM内存空间,一共32KB;
  • 其它空间:用于一些特定功能;

如果进行了地址重映射:

  • BootROM被映射到地址0xFFFD 0000;
  • 片内SRAM被映射到地址0xFFFF 0000;

06. 系统启动

RK3399提供从片外设备启动系统,如serial nand or nor flash、eMMC、SD/MMC卡。当这些设备中的启动代码没有准备好时,还可以通过 USB OTG 接口将系统代码下载到各个外设存储中。

所有引导代码都将存储在内部 BootROM中。其中支持以下功能:

  • SPI接口;
  • eMMC接口;
  • SD/MMC卡;

从图中可以得到以下几个结论:

(1) 上电后, A53核心从0xffff0000这个地址读取第一条指令,这个内部BootROM在芯片出货的时候已经由原厂烧写;

(2) 然后依次从Nor Flash、Nand Flash、eMMC、SD/MMC获取ID BLOCK,ID BLOCK正确则启动,都不正确则从USB端口下载;

  • 如果eMMC启动,则先读取SDRAM(DDR)初始化代码到内部SRAM,由于SRAM只有192KB,因此最多只能读取那么多,然后初始化DDR,再将eMMC上的代码(剩下的用户代码)复制到DDR运行;
  • 如果从USB下载,则先获取DDR初始化代码,下载到内部SRAM中,然后运行代码初始化DDR,再获取loader代码(用户代码),加载到DDR中并运行;

07. Rockchip引导流程

参考: Rockchip引导流程

针对不同的解决方案,Rockchip提供了两种不同的启动加载程序方法,其步骤和生成的镜像文件也是完全不同的。

  • TPL/SPL加载:使用Rockchip官方提供的TPL/SPL U-boot(就是我们上面说的小的uboot),该方式完全开源;
  • 官方固件加载:使用Rockchip idbLoader,它由Rockchip rkbin project的Rockchip ddr init bin和miniloader bin组合而成,该方式不开源;

需要注意的是:并不是所有平台都支持这两种启动加载程序方法。

上面我们介绍了SPL,那什么是TPL?实际上将我们上面所说的SPL初始化SDRAM等硬件工作的部分独立出去,就是TPL。那么我们总结一下:

  • TPL是Targer Program Loader,就是芯片级的初始化过程,这个时候的代码都是基于芯片平台的部分,它在启动过程中进行DDR初始化和一些其他的系统配置,以便后续的SPL能够正确地运行;
  • SPL是Secondary Program Loader,它从存储设备中读取trust(如ATF/OP-TEE)和uboot二进制文件,将它们加载到系统内存中并运行它们,进而启动完整的操作系统;

TPL和SPL的区别在于它们的职责不同。TPL主要负责初始化系统硬件,而SPL负责加载和运行其它软件组件,如trust和uboot。此外,在一些特殊情况下,如加密启动或安全启动模式下,TPL还可能执行其他额外的任务。

7.1 启动阶段

Rockchip处理器启动可以划分为5个阶段:

+--------+----------------+----------+-------------+---------+ | Boot | Terminology #1 | Actual | Rockchip | Image | | stage | | program | Image | Location| | number | | name | Name | (sector)| +--------+----------------+----------+-------------+---------+ | 1 | Primary | ROM code | BootROM | | | | Program | | | | | | Loader | | | | | | | | | | | 2 | Secondary | U-Boot |idbloader.img| 0x40 | pre-loader | | Program | TPL/SPL | | | | | Loader (SPL) | | | | | | | | | | | 3 | - | U-Boot | u-boot.itb | 0x4000 | including u-boot and atf | | | | uboot.img | | only used with miniloader | | | | | | | | | ATF/TEE | trust.img | 0x6000 | only used with miniloader | | | | | | | 4 | - | kernel | boot.img | 0x8000 | | | | | | | | 5 | - | rootfs | rootfs.img | 0x40000 | +--------+----------------+----------+-------------+---------+ 

当我们讨论从eMMC/SD/U盘/网络启动时,它们涉及到不同的概念:

  • 第一阶段始终在BootROM中,它加载第二阶段并可能加载第三阶段(当启用SPL_BACK_TO_BROM选项时);
  • 从SPI闪存启动意味着第二阶段和第三阶段固件(仅限SPL和U-Boot)在SPI闪存中,第四/五阶段在其他位置;
  • 从eMMC启动意味着所有固件(包括第二、三、四、五阶段)都在eMMC中;
  • 从SD Card启动意味着所有固件(包括第二、三、四、五阶段)都在SD Card 中;
  • 从U盘启动意味着第四和第五阶段的固件(不包括SPL和U-Boot)在磁盘中,可选地仅包括第五阶段;
  • 从Net/TFTP启动意味着第四和第五阶段的固件(不包括SPL和U-Boot)在网络上。

启动阶段涉及到了多个镜像文件:

  • 阶段一中的BootROM这个是SoC厂商提供的,我们不用关心;
  • 阶段二方式需要提供一个idbloader.img,这个我们后面具体说说;
  • 阶段三实际上就是uboot的镜像文件了,这里又搞出了两种,uboot.img(还需要搭配trust.img)和u-boot.itb(这个是因为它已经把ATF打包进去了);这两个文件里面除了都包含u-boot.bin原始二进制文件,又放了点其他东西,可以被idbloader.img识别,然后加载,这个我们后面具体说说;
  • 阶段四和阶段五是内核镜像和根文件系统;

这里我们具体说一下阶段二,阶段三涉及到的几个镜像文件。

idbloader.img

idbloader.img文件是一个Rockchip格式的预加载程序,在SoC启动时工作,它包含:

  • 由Rockchip BootROM知道的IDBlock 头;
  • DDR初始化程序,由BootROM加载到SRAM,运行在SRAM内部;
  • 下一级加载程序,由MaskROM加载并运行在DDR上;

u-boot.img

u-boot.itb

u-boot.itb实际上是u-boot.img的另一个变种,也是通过mkimage构建出来的,里面除了u-boot.dtb和u-boot-nodtb.bin这两个uboot源码编译出来的文件之外,还包含了bl31.elf、bl32.bin、tee.bin等ARM trust固件。其中bl31.elf是必须要有的,bl32.bin、tee.bin是可选的,可以没有。

trust.img

因为RK3399是ARM64,所以我们还需要编译 ATF (ARM Trust Firmware),ATF 主要负责在启动uboot之前把CPU从安全的EL3切换到 EL2,然后跳转到uboot,并且在内核启动后负责启动其他的CPU。

ATF将系统启动从最底层进行了完整的统一划分,将secure monitor的功能放到了bl31中进行,这样当系统完全启动之后,在CA或者TEE OS中触发了smc或者是其他的中断之后,首先是遍历注册到bl31中的对应的service来判定具体的handle,这样可以对系统所有的关键smc或者是中断操作做统一的管理和分配。ATF的code boot整个启动过程框图如下:

在这里插入图片描述

7.2 引导流程

Rockchip提供了外部uboot加载的流程图,如下图示:

在这里插入图片描述

如上图所示:

  • 引导流程1是典型的使用Rockchip miniloader的Rockchip引导流程;
  • 引导流程2用于大多数SoC,使用U-Boot TPL进行DDR初始化,使用SPL加载加载u-boot.itb文件;

注1:如果loader1具有多个阶段,则程序将返回到BootROM,BootROM将载入并运行到下一个阶段。例如,如果loader1是TPL和SPL,则BootROM将首先运行到TPL,TPL初始化DDR并返回到BootROM,BootROM然后将加载并运行到SPL。

注2:如果启用了trust,在安全模式(armv8中的EL3)下,loader1需要同时加载trust和U-Boot,然后运行到trust中,trust在非安全模式(armv8中的EL2)下进行初始化,并运行到U-Boot。

注3:对于trust(在trust.img或u-boot.itb中),armv7仅有一个带或不带TA的tee.bin,armv8具有bl31.elf并且可选包含bl32。

注4:在boot.img中,内容可以是Linux的zImage和其dtb,可以选择grub.efi,也可以是AOSP boot.img,ramdisk可选。

7.3 TPL/SPL方式

在TPL/SPL加载方式中,我们基于uoot源码编译出TPL/SPL,其中:TPL负责实现DDR初始化,TPL初始化结束之后会回跳到BootROM程序,BootROM程序继续加载SPL,由SPL加载u-boot.itb文件。

TPL:被BootROM加载到内部SRAM,起始地址为0xff8c2000;结束地址不能超过0xff,所以TPL程序最大不能超过184KB;

SPL:被BootROM加载到DDR,起始地址为0x00000000;结束地址绝对不能超过0x00040000, 因为0x00040000地址被用来加载bl31_0x00040000.bin, 因此SPL程序最大不能超过256KB:反汇编如下:

0000000000000000 <__image_copy_start>: 0:  b 4 <__image_copy_start+0x4> 4:  b 28 <reset> 0000000000000008 <_TEXT_BASE>: ... 0000000000000010 <_end_ofs>: 10: 0001c618 .inst 0x0001c618 ; undefined 14: 00000000 udf #0 0000000000000018 <_bss_start_ofs>: 18: 00 .inst 0x00 ; undefined 1c: 00000000 udf #0 0000000000000020 <_bss_end_ofs>: 20: 004003c0 .inst 0x004003c0 ; undefined 24: 00000000 udf #0 0000000000000028 <reset>: 28: a b 450 <save_boot_params> 000000000000002c <save_boot_params_ret>: 2c: 10007ea0 adr x0, 1000 <vectors> ...... 

这里我们具体说一下采用这种方式RK3399的启动流程:

  • BootROM首先将eMMC中0x40扇区开始的184KB数据加载到片内SRAM中;由于TPL和SPL加在一起是超过184KB的,所以无法全部加载到SRAM,但是把TPL全部加载到SRAM中还是绰绰有余的,这里加载地址为0xff8c2000;
  • BootROM跳转到0xff8c2000执行TPL代码,主要是DDR的初始化,当然还有一些其他硬件的初始化;需要注意的是,执行完TPL代码之后,会返回到BootROM程序,你把它当做汇编指令bl TPL那样会更好理解;
  • BootROM加载SPL代码到DDR中,这里加载地址为0x00000000,然后跳转到地址0x00000000去执行,需要注意的是这个时候不会再返回到BootROM了,因此SPL会初始化eMMC并将eMMC中0x4000扇区的uboot加载到0x00地址处,然后跳转到该处执行uboot程序;

补充说明:上面描述的只是一个大概流程,当然中间SPL还会加载bl31.bin(bl32.bin、tee.bin非必须)去执行,但是这不是重点,所以就不做概述。

由于BootROM不是开源的,我们没法去研究BootROM源码,当然也我们可以修改common/spl/spl.c文件board_init_r函数在SPL代码执行时将地址0x000000000、0xff8c2000、0x等地址数据打印出来(printf函数要加在boot_from_devices函数执行之后),和源二进制文件进行比对来验证自己的猜想:

board_init_r addr 0x00000000 = 0x # 和u-boot-spl.bin前4字节匹配 addr 0x00000004 = 0x # 和u-boot-spl.bin文件偏移0x4处的4个字节匹配 addr 0x00000008 = 0x0 # 同样匹配 addr 0x00040000 = 0xaa0003f4 # 和bl31_0x00040000.bin文件前4字节匹配 addr 0x00050018 = 0xb # 和bl31_0x00040000.bin文件偏移0x10018处的4个字节匹配 addr 0xff8c2000 = 0x33334b52 # 这个地址数据和u-boot-tpl.bin有点对不上,可能后期被改变了?可以尝试在TPL阶段代码执行时输出这个看看 

在该方式中,我们需要用到以下源代码:

  • uboot源码:编译生成u-boot-spl.bin、u-boot-tpl.bin、u-boot-nodtb.bin、u-boot.dtb;
  • ATF源码:编译生成bl31.elf;

通过编译和工具我们最终可以生成:

  • idbloader.img :由u-boot-spl.bin、u-boot-tpl.bin通过工具合并得到;
  • u-boot.itb:由bl32.elf、u-boot-nodtb.bin、u-boot.dtb、u-boot.its通过工具合并得到;

7.4 官方固件方式

在官方固件加载方式中,我们基于Rockchip rkbin官方给的ddr.bin、miniloader.bin来实现的;

(1) 通过tools/mkimage将官方固件ddr, miniloader打包成BootROM程序可识别的、带有ID Block header的文件idbloader.img;

  • ddr.bin:等价于上面说的TPL,用于初始化DDR;
  • miniloader.bin:Rockchip修改的一个bootloader,等价于上面说的SPL,用于加载uboot;

这个文件打包出来实际上也是超过192KB的,因此也是分为二阶段执行的。

(2) 通过tools/loaderimage工具将u-boot.bin打包成u-boot.img;其中u-boot.bin是由uboot源码编译生成;

补充说明:使用Rockchip miniloader的 idbloader 时,需要将u-boot.bin通过tools/loaderimage转换为可加载的miniloader格式。

(3) 使用Rockchip工具tools/trust_merge将bl31.bin打包成trust.img;其中bl31.bin由ATF源码编译生成;

补充说明:使用Rockchip miniloader的idbloader 时,需要将bl31.bin通过tools/trust_merge转换为可加载的miniloader格式。

08. 问题讨论

09. 参考

(1) rk3399移植 u-boot

(2) u-boot (3) — spl

(3) Rockchip Boot option

(4) U-Boot 之一 源码文件、启动阶段(TPL、SPL)、FALCON、设备树

(5) RK3399 TRM

(6) RK3399 Datasheet

(7) [Rockchip RK3399TRM V1.3 Part1.pdf](https://www.t-firefly.com/download/Firefly-RK3399/docs/TRM/Rockchip RK3399TRM V1.3 Part1.pdf)

(8) [Rockchip RK3399TRM V1.3 Part2.pdf](https://www.t-firefly.com/download/Firefly-RK3399/docs/TRM/Rockchip RK3399TRM V1.3 Part2.pdf)

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/122160.html

(0)
上一篇 2025-10-18 20:15
下一篇 2025-10-18 20:26

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信