为何U-boot proper需再次执行DRAM初始化与代码重定位?
你的BeagleBone Black启动流程分析完全正确——ROM加载MLO(也就是SPL)到内部SRAM,SPL完成DRAM初始化后把U-boot proper加载到DRAM,再跳转到它执行。但U-boot proper还要做一遍这些操作,其实是U-boot设计里通用性、可靠性和功能完整性的体现,我给你拆解几个核心原因:
1. U-boot Proper是独立的“完整bootloader”
U-boot从设计之初就不是只能依赖SPL运行的组件——有些平台没有SPL(比如内存足够大、ROM能直接加载完整U-boot的场景),U-boot proper必须能自己完成所有初始化步骤。
所以哪怕在有SPL的BeagleBone场景下,U-boot proper依然保留了完整的初始化流程,相当于给自己加了个“安全网”:万一SPL的DRAM初始化有遗漏(比如只初始化了部分内存、或者某些内存参数配置不全),U-boot proper可以补全,确保后续运行稳定。
2. 代码重定位是U-boot的核心需求,和SPL无关
SPL只是把U-boot proper加载到DRAM的一个固定链接地址(比如CONFIG_SYS_TEXT_BASE),但U-boot最终会把自己重定位到DRAM的高地址区域,原因有两个:
- 避免被后续文件覆盖:加载内核、设备树、ramdisk这些文件时,通常会用DRAM的低地址区域,把U-boot移到高处能防止自身代码被覆盖。
- 支持位置无关运行:重定位后U-boot可以在DRAM任意位置执行,这让它能适配不同内存布局,甚至支持内存地址随机化、加密这类高级特性。
看你贴的代码片段,relocate_code()就是干这个的:它计算重定位偏移量,把整个U-boot镜像复制到目标地址,然后跳转到重定位后的here:入口。这个步骤是U-boot proper的标准流程,哪怕是ROM直接加载U-boot,它也会做重定位。
3. board_init_f()不止初始化DRAM
你提到的board_init_f()里的工作远不止DRAM初始化,它还要做:
- 初始化全局数据结构
gd(代码里的mov r9, r0就是把gd的地址存到r9寄存器,后续所有U-boot模块都会用到这个结构) - 初始化串口、定时器这类基础外设(方便后续输出调试信息、处理超时逻辑)
- 探测完整内存容量:有些平台的SPL只初始化了固定大小的DRAM,U-boot proper会重新检测整个内存的实际大小
- 设置环境变量的临时存储区
这些都是U-boot proper提供交互命令、加载内核等功能的基础,SPL根本不会处理这些,所以U-boot proper必须自己完成。
额外补充:SPL和U-boot的职责边界
SPL的定位是最小化启动加载器,它只做最必要的事:初始化足够的DRAM,加载U-boot proper到内存,然后跳转。而U-boot proper是功能完整的bootloader,要提供命令行交互、内核加载、环境变量管理等功能,所以它需要一套完整的初始化流程来支撑这些功能。
比如BeagleBone Black的SPL(MLO)只初始化了DDR3的基本时序和部分内存,而U-boot proper会重新检测内存的完整容量,初始化更多外设,为后续启动Linux内核做足准备。
内容的提问来源于stack exchange,提问作者SRK

