启动流程
硬件复位
接通电源时,芯片内部的各个CPU核心都会现处于复位状态。板级PMIC(片上电源管理单元)会按照既定时序,依次给SOC的各个电源域上电。 只有核心电压、时钟等运行条件稳定后,硬件复位信号被释放,主核CPU0开始从一个固定的地理地址执行第一条指令。这个固定地址对应的就是BootRom
BootROM执行
上电执行指令
上电后,CPU0从0xffff0000这个地址读取第一条指令,CPU上电后的第一行代码
寻找介质
BootROM依次从Nor Flash、Nand Flash、eMMC、SD/MMC获取ID_BLOCK。 找到后会在启动介质上检查他是否格式正确,确认无误后,才会继续加载第一阶段loader。 ID_BLOCK在第一阶段启动镜像打包时候加入的启动头。通常包含在idbloader.img这类竞选中,供BootROM识别和加载。
初始化DDR+搬运启动代码
loader可用后,先读取SDRAM(DDR)初始化代码到内部SRAM,由于SRAM内容很小,因此只能读取这么多。然后初始化DDR,再将启动介质上的剩余的用户代码复制到DDR运行。
MASKROM模式
如果BootROM在各个启动介质上都没有找到合法的ID_BLCOK,就会进入USB下载模式。 初始化USB接口,等主机发送DDR初始化代码,并且把这段代码加载到片内SRAM中运行。 待DDR初始化成功后,在继续接收后续loader镜像,将其下载到DDR并执行。
引导流程(MiniLoaderAll.bin)
从BootROM开始往后,由两种不同的启动加载程序方法,步骤和生成的镜像文件完全不同。
- miniloader路线
- TPL/SPL路线
MiniLoaderAll.bin包含全部组件的miniloader合包,对应的原厂文件是rk3xxx.loader.vxxx.xxx.bin
MiniLoaderAll.bin的组成
- rk3xxx_ddr_xxxxMHz_vx.xx.bin:DDR的固件,负责初始化DDR控制器,PHY,DDR training,为后续程序提供一个可用的DDR环境
- rk3xxx_usbplug_vx.xx.bin:USB固件程序,为我们提供烧录能力。比起BootROM的USB init 这时候提供较为完整的协议和命令封装。
- rk3xxx_miniloader_vx.xx.bin:负责拉起下一阶段的trust.img和uboot.img
MiniLoader 路线
BootROM —> ddr.bin —> miniloader —>trust.ing/uboot.img —> BL31 —> U-boot BootROM会从启动介质固定位置读出idblaoder.img的第一部分:把ddr.bin加载到SRAM中执行。ddr.bin完成DDR控制器和PHY的初始化,让外部DDR可用。 读取第二部分:miniloader.bin,DDR可用,miniloader.bin可用被加载到DDR中运行。 miniloader在DDR完成真正的Stage2装载:
- 从固定偏移加载uboot.img
- 从固定偏移加载trust.img
- 将其中的BL31,U-BOOT放到格子的运行地址
- 先跳入BL31(EL3)
- 再由BL31跳入U-Boot(EL2)
Trust/BL31
在进入U-Boot之前,还要先进入一个更高特权级的固件,也就是ATF中的BL31对应trust.img
ATF
Arm Tursted Firmware-A,是ARMv8平台上的可信固件框架。定义了一套标准启动模型,常见的阶段包括BL1,BL2,BL31和可选的BL32以及BL33 其中BL31是AArch64下的EL3运行时固件,BL32是可选的安全OS,BL33则是普通时间里的后续引导程序,通常是U-BOOT ATF定义了启动链的几个标准阶段
- BL1:最早期的入口,(SOC内部的BootROM)
- BL2:前级装载阶段,(ddr.bin+miniloader)
- BL31:EL3运行时固件,(ATF 提供,最终打包成trust.img)
- BL32:可选的安全OS
- BL33:普通世界里的后续引导程序,(U-boot)
BL31
- 建立TrustZone安全边界
- 提供PSCI电源管理服务
- 常驻在EL3,接收后续普通世界的软件调用
trust.img
BL31被打包进了trust.img 在miniloader路线下,miniloader会分别加载uboot.img和trust.img;其中trust.img对应BL31,系统会先进入BL31,再由BL31跳转到U-Boot。
U-Boot
U-Boot是系统上电后,内核启动前最完整的一层引导程序,负责完成板级硬件初始化,加载内核与设备树,并决定系统接下来的启动路劲。
uboot.img
对应的就是真正执行的U-Boot本体 前面miniloader已经完成DDR初始化和后续镜像装载,BL31也已经建好了EL3的安全运行环境,接下来的核心任务
- 完成更完整的板级硬件初始化
- 初始化存储、显示、USB、网络等基础外设
- 检查启动条件
- 加载内核并跳转执行
resource.img
内容通常有两类:
- 开机Logo:U-Boot阶段从resource.img 取出logo并且显示
- 传递给内核使用的DTB:U-Boot自己运行时可以有自己的设备树,而resource.img中保存的DTB,主要是后面传给Linux Kernel用的那一份
misc.img
启动控制分区,主要负责OTA、recovery等模式
U-boot如何加载内核并且跳转
完成板级硬件初始化和启动模式的引导决策后,U-boot会开始执行其最主要的工作:读取内核、设备树DTB以及ramdisk内存,并且将底层硬件信息传递给内核,最终跳转执行。三个核心步骤:
从哪里开始读
烧录工具根据parameter.txt在eMMC头部生成了标准GPT分区表,U-Boot通过解析eMMC上的GPT分区表,来获取各个镜像的实际存储物理偏移。U-Boot阶段对各分区的处理逻辑如下:
- uboot@0x4000/trust@0x600:前一阶段已执行完毕,U-Boot不再读取
- misc@0x8000:U-Boot读取改分区标志位,判断系统是正常启动还是进入Recovery/Fastboot模式
- resource@0xc000:U-Boot从中提取Logo进行显示,并获取基础DTB
- boot@0x28000:带有文件系统的启动分区,读取其中的Image、.dtb以及启动配置文件
- rootfs@0x40000:真正的根文件系统,U-Boot阶段不可读取,留给内核启动后挂载。
放到内存的什么位置(DDR地址映射)
分区表决定了镜像在eMMC里的位置,而决定了他们在DDR中的加载地址是U-Boot的环境变量和启动命令。 U-boot源码中,通常通过预设的环境变量来严格控制各镜像加载到DDR的物理地址:
- kernel_addr_r: 内核镜像的加载地址
- fdt_addr_r:设备树DTB的加载地址
- ramdisk_addr_r: initramfs的加载地址 明确地址后,U-Boot会执行类似ext4load或fatload命令,将eMMC对应分区终端镜像拷贝至DDR。
传递参数并跳转执行
镜像加载完毕后,U-Boot还需要向Linux内核传递启动参数(bootargs),告知内核根文件系统的位置(root=/dev/mmcblk2p8),控制台配置(如console=ttyFIQ0)等关键信息。 U-Boot会将拼接好的bootargs字符串,动态注入内存DTB的/chosen/bootargs节点里。 U-Boot执行booti汇编指令完成跳转,将DTB地址放入x0寄存器,并将PC指针指向内核在DDR的入口。
Kernel与Inittramfs
内核启动过程中start_kernel()
U-boot通过booti指令将控制权交接后,Linux内核正式启动。内核前期的核心工作集中再start_kernel()函数中。依次完成架构层面的MMU、Cache与终端控制器配置,并解析U-Boot传入的完整设备树(此时合并DTBO)以获取全局硬件布局。 随后就是常规的内核启动流程—内存映射、进度调教等,最后注册各类基础总线,根据设备树节点匹配并调用对应驱动的probe()函数,逐步将整个SOC的底层外设全部点亮
早期用户空间(Initramfs阶段)
核心驱动加载完毕后,内核需要挂载存放这完整操作系统的真实根文件系统 真实的根文件系统可能位于特殊介质上,或者依赖尚未加载的复杂驱动。因此系统引入了Initramfs(临时根文件系统)作为过渡 内核会将U-Boot加载内存中的ramdisk(通常是gzip压缩的cpio归档文件)作为根目录挂载到内存中,并执行其中的/init脚本。该阶段的核心任务包括
- 在内存中创建/proc,/sys,/dev等必要虚拟文件系统
- 加载是必须的但是未被静态编译进内核的主存储器模块(如特定的eMMC/NVMe控制器驱动)。
- 解析U-Boot传入的bootargs(尤其是root=参数),识别eMMC上的真实根文件系统系统设备节点(如/dev/mmmcblk2p8)
- 执行fsck工具检查真实文件系统的完整性和健康状态
- 将真实的rootfs分区临时挂载到指定目录
- 执行switch_root指令,清理当前内存中的临时环境,将系统的根目录(/)切换到真实的eMMC分区,并跳转执行真实系统下的/sbin/init(通常是systemd的入口) 至此,内核引导与前期准备彻底完成,启动流程进入用户空间
Rootfs与Userdata 挂载操作系统与存储扩容
当内核在inittramfs阶段完成必要的底层驱动加载后,就会真实的奥做系统中运行,这就依赖后的两个大体积镜像rootfs.img和userdata.img。
rootfs.img(根文件系统)
内核通过switch_root操作,定位到eMMC上电rootfs分区,对应到我们的就是偏移地址0x00040000,将其挂载为系统的真实根目录(/) 之后,内核将执行权交给改文件系统的/sbin/init进程,由其拉起所有上层业务(如网络、SSH)。至此,内核层级引导结束,完整的用户空间正式接管系统
userdata.img (用户数据分区)
这是一个独立于系统核心文件外,专门预留给用户存放私有数据的文件系统分区。简单来说可以直接理解为我们电脑上的外挂磁盘,用于存放业务app或日志等
当真实rootfs启动就绪后,系统脚本会自动将这个分区挂载到指定目录(通常/user/data或者/data)
