上电引导
对于上电引导都是由特定处理器芯片厂商自己开发的程序,这个上电引导程序通常比较简单,会初始化硬件,提供下载模式等,然后才会加载通常的bootloader。
marvell(pxa935) : bootROM + OBM [l4] + BLOB
informax(im9815) : bootROM + barbox + U-boot
mediatek(mt6516/6517) : bootROM + pre-loader[l5] + U-boot
broadcom(bcm2157) : bootROM + boot1/boot2 + U-boot
bootloader与内核的通讯协议
cmdline不过是bootloader传递给内核的信息中的一部分。bootloader和内核的通信方式根据构架的不同而异。对于ARM构架来说,启动相关的信息可以通过内核文档(Documentation/arm/Booting)获得。其中介绍了bootloader与内核的通信协议
(1)数据格式:可以是标签列表(tagged list)或设备树(device tree).
cmdline 也是一种tag
(2)存放地址:r2寄存器中存放的数据所指向的内存地址。
bootloader必须创建和初始化内核标签列表。一个有效的标签列表以ATAG_CORE标签开始,且以ATAG_NONE标签结束。ATAG_CORE标签可以是空的,也可以是非空。
一个空ATAG_CORE标签其 size 域设置为 '2' (0x00000002)。ATAG_NONE标签的 size 域必须设置为 '0'。
第一阶段
U-boot的第一条指令从cpu/armv7/start.S文件开始,第一阶段主要做了如下事情:
- 设置CPU进入SVC模式(系统管理模式),cpsr[4:0]=0xd3。
- 关中断。
... - 最后会调用board_init_f
第二阶段
第二阶段从文件arch/arm/lib/board.c的board_init_f()函数开始。
有一个init_sequence一个接一个的初始化
cpu_init:
根据需要设定IRQ,FIR堆栈。如果使用中断的话,中断堆栈就接在后面。
board_init:
设置LOCKTIME,配置MPLL,UPLL,配置IO ports,设置gd->bd->bi_arch_number(553),gd->bd->bi_boot_params = 0x30000100设置boot参数地址,使能Icache和Dcache。
interrupt_init:
使用timer 4来作为系统clock, 即时钟滴答, 10ms一次,到点就产生一个中断,但由于此时中断还没打开所以这个中断不会响应。
env_init:
该函数主要做关于环境变量的工作,这个环境变量可以不用存放在nor或者nand flash上,直接在内存中生成(default_environment)。不过对于那些掉电需要保存的参数来说,保存在flash上无疑是最可靠的方式。有的uboot还支持冗余存储,也就是存两份做备份。
init_baudrate:
后面的启动如果说在参数里设置了新的波特率的话就会用新的波特率来初始化。
display_banner:
打印uboot的一些信息,版本信息:NC-Boot 1.5 日期-时间 ,coed范围,bss开始地址,IRQ、FIR堆栈地址。
dram_init:
设置板级数据中
的SDRAM开始地址和大小
mem_malloc_init()函数,分配堆空间
devices_init函数,创建了devlist,但是只有一个串口设备注册在内。
调用board_init_r
main_loop common/main.c。
bootm_start(cmdtp, flag, argc, argv))
其主要的目的是从bootm命令指定的内存地址中获取内核uImage的文件头(也就是在用uboot的mkiamge工具处理内核zImage时添加的那64B的数据)。
这个boot.img是zImage和ramdisk.img合成之后的,而且还加了专门的头,这个head和U-boot原始的不一样,具体的源码路径可以参考:system/core/mkbootimg/。
跳过64字节的head,开始校验kernel的Image数据,校验码ok之后会打印:Verifying Checksum ... OK
bootm_load_os(images.os, &load_end, 1);
这个函数的作用是通过获取的文件头信息,将文件头后面所跟的内核映像放置到文件头信息规定的地址
do_bootm_linux。
这里设备ID号可以从环境变量中获得!如果环境变量中有,就会覆盖之前赋值过的设备ID(最终通过r1传递给内核)
kernel_entry
kernel_entry = (void (*)(int, int, uint))images->ep;
这里让函数指针指向内核映像的入口地址
kernel_entry(0, machid, bd->bi_boot_params);
跳入内核入口地址.
r1=0、r1=machid、r2=启动参数指针