1. UBOOT的主要功能
UBOOT的主要任务是初始化硬件(如:串口,内存),然后将内核从Flash中读到RAM中,然后跳转到内核的入口点去运行,也就是启动操作系统。
UBOOT和BIOS的差异:PC机的引导程序由BIOS和位于硬盘MBR中的引导程序(GRUB或LILO)一起组成。BIOS在完成硬件检测和资源分配后,将硬盘中的引导程序(GRUB/LILO)读到系统内存中然后将控制权交给引导程序。引导程序的组要任务是将内核从硬盘读到内存,然后跳转到内核的入口点去运行。
传统的个人电脑上面BIOS会有一个int19软件中断功能,在初始化完成后,BIOS会进入int19中断,寻找启动介质,如软盘,光盘,硬盘,flash或者网络等等,读取第一个扇区的内容到内存的0000:7C00处,跳入这个地址执行。这里int19就是一个bootloader,启动引导器。所以BIOS具有Boot Loader的功能。当然,目前的BIOS功能已经被扩充了很多,例如电源管理方面的ACPI接口,USB驱动,PXE网络引导功能,硬盘加密,TPM接口,BIOS配置界面,BIOS自动恢复等等。
2. 零地址跳转
下面介绍一下系统上电后如何跳转到程序执行的第一条指令。系统上电后,所有的处理器通常都从处理器制造商预先安排的地址开始执行。如X86处理器复位后一般在4G-8字节处,而ARM处理器一般是在0x00000000起开始执行。
3. UBOOT执行的2个stage
UBOOT的执行分为两个阶段,第一个阶段stage1主要使用汇编语言编写,第二个阶段stage2主要使用C语言编写。
UBOOT stage1阶段的主要任务:
1)系统复位转入UBOOT的stage1入口点
2)设置异常向量
3)设置CPU的速率、时钟频率及终端控制寄存器
4)初始化内存控制器
5)拷贝UB00T的stage2代码到RAM空间
6)设置堆栈、初始化数据段
UBOOT stage2阶段的主要任务:
1)跳转到stage2的C入口点
2)初始化Flash设备
3)初始化系统内存
4)初始化NAND、显示、网络设备等
5)将kernel和根文件系统从flash读到RAM中
6)设定内核启动参数并调用内核
4. UBOOT的CPU寻址空间映射图
4. UBOOT各目录结构介绍
Board目录
和开发板有关的文件。每一个开发板都以一个子目录出现在当前目录中,比如:SMDK2410,子目录中存放与开发板相关的文件,主要包含SDRAM、FLASH、网卡驱动等
Common目录
实现UBOOT支持的命令。
Cpu目录
与特定CPU架构相关的代码,每一款UBOOT下支持的CPU在该目录下对应一个子目录,比如有子目录arm920t等。每个子目录中都包括cpu.c、interrupt.c和start.S
Disk目录
对磁盘的支持。
Doc目录
UBOOT的说明文档。
Drivers目录
UBOOT支持的设备驱动程序都放在该目录,比如各种网卡、支持CFI的FLASH、串口、USB总线等。
Examples目录
可在UBOOT下运行的示例程序, 如:hello_world.c、timer.c等。
Fs目录
文件系统的支持:如:cramfs、fat、fdos、jffs2、registers等。
Include目录
UBOOT使用的头文件,该目录下configs目录有与开发板相关的配置头文件,如smdk2410.h。该目录下的asm目录有与CPU体系结构相关的头文件。
Net目录
与网络协议栈相关的文件,如:bootp、nfs、tftp等协议的实现。
Post目录
上电自检文件目录
Tools目录
生成UBOOT的工具,如mkimage,crc等等。
UBOOT的stage1代码通常放在cpu/xxxx/start.S文件中,他用汇编语言写成。
UBOOT的stage2代码通常放在lib_xxxx/board.c文件中,他用c语音写成。
5. UBOOT的编译
设置好交叉编译环境,参见文档《1建立交叉编译环境》
make mini2440_config (mini2440_config在Makefile中有定义)
make
6. UBOOT启动kernel
这里以mini2440作为一个例子进行说明。
在mini2440中,由于u-boot.bin文件大小已经大于了0x40000,所以作者修改了ENV的OFFSET。现在的Nand分区是这样的:
uboot地址范围是0x0-0x60000,共384KB
uboot参数区地址范围是0x60000-0x80000,共128KB
kernel地址范围是0x80000-0x500000,共5MB
在include/configs/mini2440.h中有nand分区的定义:
而在友善之臂的Supervivi定义的bootloader是0x0-0x40000,params是0x40000-0x60000,kernel是0x60000-0x500000;(友善之臂附带的linux kernel定义的地址和bootloader一致)。
如果u-boot是用supervivi的k选项将linux kernel传到nand flash的。那么,Kernel的地址就会和u-boot中的params冲突,导致U-boot无法引导内核。
解决办法:
修改linux kernel中对Nand的分区定义,使其与u-boot保持一致。kernel的nand分区信息在arch/arm/mach-s3c2440/mach-mini2440.c中的名为friendly_arm_default_nand_part的结构体数组里。
如下图所示,为默认情况下,kernel中对Nand分区的定义:
根据U-boot的nand分区配置,我们需要将其修改为如下所示:
回到Kernel根目录,执行make zImage命令,会重新生成/arch/arm/boot/zImage。现在又有一个问题:uboot目前只能支持uImage启动,不支持zImage启动,因此我们需要使用mkimage工具把zImage转换为uImage;
我们可以通过两种方式获取到mkimage工具:
1. uboot源码根目录下的tools目录下
2. 执行以下命令安装apt-get install uboot-mkimage
uImage是在zImage之前加上一个长度为0x40字节(64字节)的头部,来说明zImage这个映像文件的映象名、映像类型、加载地址、入口地址、生成时间、大小等信息。换句话说,如果直接从uImage的0x40位置开始执行,zImage和uImage没有任何区别。U-boot引导内核是通过bootm完成的,bootm加载uImage的时候,会先对uImage进行解压(去掉0x40字节的头部),解压地址为内核入口地址(由mkimage的-e选项指定)。当解压完成时的uImage跟原zImage是完全相同的。
转换zImage为uImage的命令如下所示:
mkimage -n "linux-2.6.32.2" -A arm -O Linux -T kernel -C none -a 0x30008000 -e 0x30008040 -d zImage uImage
执行mkimage --help可以查看mkimage的帮助信息;
-A用于指定平台架构,我们的是arm平台
-O用于指定操作系统,我们的是Linux操作系统
-T用于指定映像类型,映像是内核镜像,指定为Kernel
-C用于指定压缩类型,此处我们不压缩,设为none
-a用于指定夹在地址,0x30008000
-e用于指定入口地址,uImage去除0x40字节的头部信息之后,即为zImage,这便是kernel的入口地址,指定为0x30008040
-d用于指定data文件,从内容上看,zImage不含格式信息,就是一个普通的数据文件,因此此处指的就是zImage
uImage即为输出文件名。
u-boot能引导的内核已经制作好了,接下来我们测试一下能否正常工作;
1)执行usbslave 1 0x30008000 #通过usb线将uImage下载至内存0x30008000中(在主机上执行dnw...)
2)执行nand erase 0x80000 0x500000 #擦除此区域,nand使用之前需要先擦除,以便接下来将linux kernel保存到这个地址处。
3)执行nand write 0x30008000 0x80000 0x500000 #从内存的0x30008000地址处开始拷贝0x500000字节的内容到nandflash的0x80000地址处。
4)修改u-boot引导参数(UBOOT根据这些参数去加载内核)
setenv bootcmd 'nand read 0x30008000 0x80000 0x500000;bootm 0x30008000'
setenv bootargs 'noinitrd root=/dev/mtdblock3 initrd=/linuxrc console=ttySAC0'
saveenv
5)重启开发板
从nand启动u-boot然后引导内核启动。
7. UBOOT代码详析
参见:https://www.cnblogs.com/lcw/p/3802644.html