U-Boot启动流程简单分析
目录一、从链接脚本分析启动流程的原理1u-boot.lds链接脚本是啥2u-boot.lds脚本文件的分析二、关于_start的分析1. 异常向量表初始化CPU 上电第一行代码2. 关看门狗、关中断、设置 CPU 模式3. 初始化时钟、DDR 内存最关键的硬件初始化4. 设置堆栈栈指针 sp为 C 语言运行做准备5. U-Boot 重定位Relocation从 Flash 搬到 DDR6. 跳转到 C 语言入口启动第二阶段通过前面的文章我们已经知道U-Boot相当于BIOSBootLoader其中BIOS则是上电后执行的第一段指令他负责初始化堆栈、向量表、时钟等基本信息而BootLoader则是对各种外设进行初始化即类似STM32中的裸机开发。那么我们首先就得分析分析这个BIOS到底是怎么初始化的。它类似于STM32中的startup.s启动文件。一、从链接脚本分析启动流程的原理1u-boot.lds链接脚本是啥我们写的C语言代码会被编译器arm-linux-gnueabihf-gcc编译成一个个的.o文件每个文件都包含了各自的代码段(.text)、数据段(.data)、只读数据段(.rodata)等等。而链接脚本就是链接器ld这个程序的施工说明书他规定了这些.o文件的拼接顺序、各自的每个段在总文件中的地址。最终所有的.o文件都在链接器的作用下合并生成一个完整的u-boot的ELF格式文件。当然最终链接生成的u-boot文件只有1种文件格式ELF格式。即给开发工具、调试器、加载程序使用的最大的特点是人能读懂。但是 ELF 格式绝对不能直接烧录到 Flash 中让 CPU 执行CPU 上电后只认连续的纯二进制机器码而 ELF 文件包含大量非代码的额外信息文件头、段表、符号表、调试信息等会破坏机器码的连续性导致 CPU 直接跑飞、死机。只有从 ELF 中剥离所有额外信息、提取出纯机器码的.bin文件才是 CPU 能直接识别、执行的格式。所以我们一般可以直接从链接脚本生成的ELF文件中分析启动流程而真正烧录的是把ELF文件解析后的二进制bin文件。2u-boot.lds脚本文件的分析在目录/home/hmy/u-boot-2026.04下可以看到一个u-boot.lds链接脚本文件我们打开来看看。这个链接脚本信息中大体上就规定了各个.o文件是怎么存放的然后上电需要执行哪一个函数(_start)。当然这个脚本文件并不是给我们人看的而是给链接器ld这个程序看的。所以他的语法看起来和C语言等高级语言不太一样。尽管如此我们还是能大致从链接脚本文件中看到一些信息从而分析启动流程的。我们常常说的.text段、.data段的范围就是在这里面定义的比如二、关于_start的分析当然链接脚本除了定义各种段的位置结构还定义了最终经过链接器生成的bin文件的执行入口ENTRY(_start)。这个_start就是 U-Boot 上电后 CPU 执行的第一条指令它是整个 U-Boot 启动流程的 “总开关”所有后续的初始化、内核引导都从这里开始。关于这个_start文件是用汇编代码写的由于我没有学过汇编所有我也看不懂。不过你只需要知道_start做了那些事情这些是我从网上抄的大家大致看看有个印象即可1. 异常向量表初始化CPU 上电第一行代码_start所在的位置就是 ARM 架构要求的异常向量表的起始地址。CPU 上电后首先会从 0 地址_start读取复位异常向量跳转到对应的reset复位处理函数这是 U-Boot 真正启动的第一步。同时这里也会初始化其他异常未定义指令、软中断、IRQ、FIQ 等的入口保证系统能正常处理中断和异常。2. 关看门狗、关中断、设置 CPU 模式关看门狗防止 CPU 在初始化过程中被看门狗复位保证启动流程不被打断。关中断在底层硬件初始化完成前屏蔽所有中断避免中断干扰初始化流程。设置 CPU 为 SVC 模式切换到 ARM 的特权模式管理模式获得最高权限为后续的内存、时钟初始化做准备。3. 初始化时钟、DDR 内存最关键的硬件初始化这是_start阶段最核心的工作初始化时钟配置 CPU、总线、外设的时钟让芯片跑在正确的频率下。初始化 DDR 内存初始化开发板的 DDR 控制器让 DDR 可以正常读写。这一步是 U-Boot 能从 Flash 重定位到 DDR 运行的前提没有 DDR后续的 C 语言代码、驱动初始化都无法执行。4. 设置堆栈栈指针 sp为 C 语言运行做准备C 语言函数调用、局部变量都依赖栈空间_start会在 DDR 中分配一块栈空间设置栈指针sp让 CPU 可以正常执行 C 语言代码。同时这一步也会初始化 BSS 段未初始化的全局变量把 BSS 段的内存清零符合 C 语言标准。5. U-Boot 重定位Relocation从 Flash 搬到 DDRU-Boot 一开始是在 Flash或 SRAM中运行的Flash 速度慢、空间有限。_start阶段会利用链接脚本中定义的__image_copy_start等符号计算 U-Boot 镜像的大小把整个 U-Boot 从 Flash完整拷贝到 DDR 的高地址中运行这就是「重定位」。重定位完成后U-Boot 就可以在高速的 DDR 中运行为后续的复杂初始化、命令行提供足够的空间。6. 跳转到 C 语言入口启动第二阶段完成所有底层汇编初始化后_start会最终跳转到 U-Boot 的 C 语言入口函数board_init_f()正式进入 U-Boot 的第二阶段C 语言阶段。从这里开始U-Boot 就会执行串口、SD 卡、网络、Flash 等外设的初始化最终加载 Linux 内核完成整个启动流程。由于这些初始化功能是用汇编代码写的所以我在学习的时候也很痛苦索性我直接跳过。等我真正需要裁剪移植U-Boot的时候再回头来看。下一节我们将直接看看根文件系统是如何构建的。