一、简介
u-boot是操作操作系统运行前的引导加载程序,它的作用是初始化硬件设备、建立内存空间的映射表,为最后调用操作系统内核做好准备。
主要任务是将内核映象从硬盘上读到RAM中,然后跳转到内核的入口点运行,即开始启动操作系统。
BootLoader的操作模式一般分为自启动模式和交互模式。
自启动模式:BootLoaderd从目标机上的某个固态设备(FLASH)上将操作系统加载到RAM中运行,整个过程没有用户的介入;
下载模式:目标机上的BootLoader将通过串口或网络等通信手段将内核映像和根文件系统映从PC机下载到到目标机中,或者直接进行系统的引导。也可以通过串口接收用户的命令。
二、u-boot启动流程
目前大多数 BootLoader 都分为stage1 和stage2 两大部分。依赖于CPU体系结构的代码,比如设备初始化代码等,通常都放在 stage1中,而且通常都用汇编语言来实现,以达到短小精悍的目的。而stage2 则通常用C 语言来实现,这样可以实现更复杂的功能,而且代码会具有更好的可读性和可移植性。
1、stage1(汇编代码部分)
(1)系统复位转入U-BOOT的stage1入口点
(2)设置异常向量 (本阶段硬件设备初始化)
(3)设置CPU的速度、时钟、频率及终端控制寄存
(4)初始化内存寄存器
(5)拷贝U-BOOT的stage2到RAM空间
(6)设堆栈、初始化数据段
2、stage2(c语言代码部分)
(1)跳转到stage2的C入口点
(2)初始化FLASH设备 (本阶段硬件设备初始化)
(3)初始化系统内存
(4)初始化NAND、显存、网络等设备
(5)将kernel和根文件系统映射从flash读到RAM空间中
(6)设置内核启动参数和调用内核
三、u-boot目录结构
从u-boot-2010.06版本开始把体系结构相关的内容合并,原先的cpu与lib_arch内容全部纳入arch中,并且其中增加inlcude文件夹;分离出通用库文件lib。
├── api 存放uboot提供的接口函数
├── arch 与体系结构相关的代码,uboot的重头戏
├── board 根据开发板定制的代码,主要包含SDRAM、FLASH驱动
├── common 通用的代码,涵盖各个方面,已命令行处理为主
├── disk 磁盘分区相关代码
├── doc 文档,一堆README开头的文件
├── drivers 驱动,很丰富,每种类型的设备驱动占用一个子目录
├── examples 示例程序
├── fs 文件系统,支持嵌入式开发板常见的文件系统
├── include 头文件,已通用的头文件为主
├── lib 通用库文件
├── nand_spl NAND存储器相关代码
├── net 网络相关代码,小型的协议栈
├── onenand_ipl 一个小loader
├── post 加电自检程序
└── tools 辅助程序,用于编译和检查uboot目标文件
目录详细图
四、u-boot.lds链接顺序
以u-boot-2010.6举例,u-boot的链接脚本\arch\arm\cpu\arm926ejs\u-boot.lds,它定义了目标程序各部分的链接顺序。
OUTPUT_FORMAT("elf32-littlearm","elf32-littlearm", "elf32-littlearm")
/*指定输出可执行文件是elf格式,32位ARM指令,小端*/
OUTPUT_ARCH(arm)
/*指定输出可执行文件的平台为ARM*/
ENTRY(_start)
/*指定输出可执行文件的起始代码段为_stext*/
SECTIONS
{
/*指定可执行文件的全局入口点,通常这个地址都放在ROM(flash)0x0位置。必须使编译器知道这个地址,通常都是修改此处来完成*/
.= 0x00000000;
.= ALIGN(4); /*代码以4字节对齐*/
.text:
{
arch/arm/cpu/arm920t/start.o (.text)
*(.text)
}
.= ALIGN(4);
.rodata: { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }
/*代码段结束后,有可能4bytes不对齐了,此时做好4bytes对齐,以开始后面的.rodata段*/
.= ALIGN(4);
.data: { *(.data) } /*和前面一样,4bytes对齐,以开始接下来的.data段*/
.= ALIGN(4);
.got: { *(.got) }
.= .;
//把__u_boot_cmd_start赋值为当前位置, 即起始位置*/
__u_boot_cmd_start= .; //指定u_boot_cmd段, uboot把所有的uboot命令放在该段.*/
.u_boot_cmd: { *(.u_boot_cmd) }
__u_boot_cmd_end= .; //把__u_boot_cmd_end赋值为当前位置,即结束位置*/
.= ALIGN(4);
__bss_start= .; //把__bss_start赋值为当前位置,即bss段的开始位置*/
.bss(NOLOAD) : { *(.bss) . = ALIGN(4); } //指定bss段*/
_end= .; //把_end赋值为当前位置,即bss段的结束位置*/
}
第一个链接的是arch/arm/cpu/arm926ejs/start.o,也就是u-boot的入口指令在start中,且以复位向量开头:
五、start.s与board.c分析
start.s主要作用是初始化运行环境;初始化内存;重新放置u-boot代码到内存中;跳入到内存中执行第二段初始化代码。
1、 关闭开门狗,屏蔽所有中断
2、 设置分频比
3、 bl cpu_init_crit() 关MMU,初始化内存
bl lowlevel_init() 配置内存,修改内存刷新率参数等
4、 relocate 判断当前代码是在NORFLASH还是RAM
copy_loop 将FLASH代码复制至RAM中
5、 stack_setup 栈设置
clear_bss _bss_start到_bss_end之间的数据清0
6、 ldr pc , start_armboot 跳转到第二阶段
board.c主要作用是初始化两个重要数据结构,对SDRAM的内存分配设置,对各种需要用到的外设进行初始化,最后循环跳入main_loop()函数,首先执行start_armboot分为board_init_f和board_init_r。
(1)执行的board_init_f部分:
1、为gd数据结构分配地址,并清零
2、执行init_fnc_ptr函数指针数组中的各个初始化函数,如下
board_early_init_f
timer_init
env_init
init_baudrateserial_init
console_init_f
display_banner
dram_init
3、A、 分配SDRAM高64KB为TLB,用于U-BOOT
B、分配SDRAM下一单元为U-BOOT代码段,数据段,BSS段
(BSS段是用来存放未初始化的全局变量与静态变量)
C、接着开辟malloc空间,存bd, gd , 3个字大小的异常堆空间
4、将relorate的地址值赋给gd结构体相应变量
(2)执行的board_init_r部分
1、对gd , bd数据结构赋值初始化
2、各种外设初始化:
初始化NORFLASH,NANDFLASH,
初始化ONENANDFLASH
初始化环境变量
初始化PCI
设置IP地址 初始化各类外设:IIC、LCD、键盘、USB
初始化控制台
建立IRQ中断堆栈
初始化以太网
初始化跳转表(定义了u-boot中基本的常用函数库)