u-boot 初始化好硬件,分配完内存。最后,u-boot 要将控制权交给 Linux。
在嵌入式系统的启动流程中,U-Boot(Universal Boot Loader)作为独立的引导加载程序,具备完全自主执行的能力,无需外部干预即可完成自身的初始化过程。相比之下,Linux内核的启动则呈现出完全不同的特性——它必须依赖U-Boot提供的预配置环境才能正常运行。
具体而言,U-Boot承担着硬件抽象层的关键职责,负责完成以下核心任务:
硬件初始化与寄存器配置:U-Boot直接与底层硬件交互,完成CPU、内存控制器、时钟树、GPIO等关键硬件组件的寄存器配置工作,确保硬件平台处于可用状态。
内存重定位服务:U-Boot负责将Linux内核镜像从存储设备(如NAND Flash、eMMC等)加载到DRAM的指定地址空间,并处理内核镜像的重定位工作,解决地址依赖问题。
系统环境准备:在内核启动前,U-Boot需要配置MMU(内存管理单元)的基础映射关系、设置栈指针、准备设备树(Device Tree)等运行时环境。
启动参数传递:U-Boot通过标准化的参数传递机制(如ARM架构的寄存器约定),将启动参数、设备树地址、机器ID等关键信息传递给Linux内核。
当所有硬件初始化、内存配置和环境准备工作完成后,U-Boot通过跳转指令将CPU控制权移交给Linux内核。在调试信息中可以观察到,地址0x80800000正是此次控制权交接的关键节点——这个地址代表Linux内核的入口点(Entry Point),标志着从引导加载阶段向操作系统内核执行阶段的完整过渡。
这种分层设计体现了嵌入式系统架构的模块化思想:U-Boot专注于硬件抽象和启动服务,而Linux内核则专注于操作系统功能的实现,两者通过标准化接口实现有序的控制权移交。
从哪找内核镜像加载到DDR
对于imx6ull EVK pro开发板而言,支持3种启动方式,也就是u-boot可以从3个地方获取linux内核,如NAND启动/EMMC启动、 SD卡启动、USB启动等。
除了以上几种启动方式以外,u-boot还支持从网络下载远程服务器中的镜像的方式启动Linux内核,u-boot直接从远程服务器将镜像下载到本地DDR中运行,并且可以挂载根文件系统到远程服务器。
linux内核启动源码
bootz 函数
当u-boot执行bootcmd的命令后,最终会调用do_bootz函数启动Linux内核。
bootz命令会调用do_bootz函数
do_bootz函数
- bootz_start 设置并找到linux镜像的入口点;
- 由于需要加载linux镜像,所以必须关闭中断;
bootz_start函数
调用
do_bootm_states 函数do_bootm_linux 函数
- kernel_entry,而这个函数指针指向的是在内存中加载linux镜像的入口地址,也就是linux第一个将要执行的代码。
- 当内核正常启动时会打印“Starting kernel …”,说明已经成功的加载了linux镜像,校验成功,并找到了入口地址,若未打印该字符型串,则应该检查一下传参是否正确
- 启动Linux内核,kernel_entry函数为汇编函数,其传参形式和前面我们分析汇编调用c函数的传参方式(ARM程序调用规则ATPCS)是类似的,也就是r0存放第一个参数0、r1存放第二个参数machid(机器ID告诉Linux内核我们用的是哪个cpu,从而调用相应的初始化函数)、r2存放第三个参数(设备树首地址),注意我们使用的是设备树的方式传参,而非传统的ATAG方式传参,传统的传参是将struct tag地址赋值给r2寄存器。
u-boot mainline 从v1.1.3开始便支持设备树,其对ARM的支持则是和ARM内核支持Device Tree同期完成。 由于我们在configs/mx6ull_npi_defconfig配置文件中定义了CONFIG_OF_LIBFDT=y,从而使能了设备树传参形式, u-boot将从启动介质中将设备树读取到内存。当然,设备树的存放位置并不是随意存放的,它必须保证不能破坏u-boot、不能破坏操作系统及其用到的内存空间, 不能占用他们的内存。
总结:
- 首先根据启动方式,从存放镜像的介质中加载到ddr。
- 检查、校验是哪类镜像。
- 传递参数给内核。
- 运行内核。






