1.第一阶段(Stage 1)
第一阶段的启动代码在 cpu\<cpu type>\start.s中,完成的工作主要有:
CPU自身初始化:包括 MMU,Cache,时钟系统,SDRAM 控制器等的初始化
重定位:把自己从非易失性存储器搬移到 RAM中
分配堆栈空间,设置堆栈指针
清零 BSS 数据段
跳转到第二阶段入口函数 start_armboot()
/Uboot114/u-boot-1.1.4/cpu/omap4/start.S
2.第二阶段(Stage 2)
第二阶段是 u-boot 的主体,入口点是 lib_arm\board.c 中的 start_armboot()函数,完成的主要工作包括:
这里说明下_armboot_start,_bss_start都是编译器根据代码长度,确定地址代码段,RW段,bss为初始化段
为 U-boot 内部私有数据分配存储空间,并清零
依次调用函数指针数组 init_sequence 中定义的函数进行一系列的初始化,代码如下:
init_fnc_t *init_sequence[] = {
cpu_init, /* basic cpu dependent setup */
board_init, /* basic board dependent setup */
interrupt_init, /* set up exceptions */
env_init, /* initialize environment */
init_baudrate, /* initialze baudrate settings */
serial_init, /* serial communications setup */
console_init_f, /* stage 1 init of console */
display_banner, /* say that we are here */
#if defined(CONFIG_DISPLAY_CPUINFO)
print_cpuinfo, /* display cpu info (and speed) */
#endif
#if defined(CONFIG_DISPLAY_BOARDINFO)
checkboard, /* display board info */
#endif
dram_init, /* configure available RAM banks */
display_dram_config,
NULL,
};
如果系统支持 NOR Flash,调用 flash_init ()和 display_flash_config ()初始化并显示检测到的器件信息(AT91SAM9260EK不需要)
如果系统支持LCD或VFD,调用lcd_setmem()或vfd_setmem()计算帧缓冲(Framebuffer)大小,然后在 BSS 数据段之后为 Framebuffer 分配空间,初始化 gd->fb_base 为Framebuffer的起始地址
调用 mem_malloc_init()进行存储分配系统(类似于 C 语言中的堆)的初始化和空间分配
如果系统支持 NAND Flash,调用 nand_init()进行初始化
如果系统支持 DataFlash,调用 AT91F_DataflashInit()和 dataflash_print_info()进行初始化并显示检测到的器件信息
调用 env_relocate ()进行环境变量的重定位,即从 Flash中搬移到 RAM 中,导入默认的环境变量表
如果系统支持 VFD,调用 drv_vfd_init()进行 VFD 设备初始化
从环境变量中读取 IP 地址和 MAC 地址,初始化 gd->bd->bi_ip_addr和gd->bd->bi_enetaddr
调用 jumptable_init()进行跳转表初始化,跳转表在global_data中,具体用途尚不清楚
调用 console_init_r()进行控制台初始化
如果需要,调用 misc_init_r()进行杂项初始化
调用enable_interrupts()打开中断
如果需要,调用 board_late_init()进行单板后期初始化,
进入主循环:根据用户的选择启动 linux自动执行bootcmd指令启动系统,或者进入命令循环执行用户输入的命令
/Uboot114/u-boot-1.1.4/lib_arm/board.c
3.U-boot 的初始化
3.1 私有数据 global_data
global_data /Uboot114/u-boot-1.1.4/include/asm-arm/global_data.h
bd_info /Uboot114/u-boot-1.1.4/include/asm-arm/u-boot.h
3.2 初始化序列 init_sequence
Init_sequence是一个函数指针数组,数组中每一个元素都指向一个初始化函数。
init_sequence /Uboot114/u-boot-1.1.4/lib_arm/board.c
(1)cpu_init u-boot-1.1.4\cpu\omap4\cpu.c
这个函数的功能是设置irq和fiq模式的堆栈起始点。所以这个函数实际上什么也没做。
(2)board_init \u-boot-1.1.4\board\omap4430sdp.c
(3)interrupt_init \cpu\omap4\interrupts.c
(4)env_init \common\env_nowhere.c
(5)serial_init \cpu\omap4\serial.c
4 命令处理
4.1 命令数据结构
U-boot的命令用struct cmd_tbl_t来实现。cmd_tbl_t的主要数据成分是命令名称(name)和命令处理函数(cmd),此外还包括最大参数个数(maxargs),是否可重复执行(repeatable),使用方法和帮助信息(usage,help)等。这个数据结构在文件include/command.h中定义:
包含在 include/command.h中的宏 U_BOOT_CMD用来定义命令:
#define Struct_Section __attribute__ ((unused,section (".u_boot_cmd")))
#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \
cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage, help}
例如 bootm命令的定义:
U_BOOT_CMD(
bootm, CFG_MAXARGS, 1, do_bootm,
"bootm - boot application image from memory\n",
"[addr [arg ...]]\n - boot application image stored in memory\n"
"\tpassing arguments 'arg ...'; when booting a Linux kernel,\n"
"\t'arg' can be the address of an initrd image\n"
);
展开后就变成:
cmd_tbl_t __u_boot_cmd_bootm __attribute__ ((unused,section (".u_boot_cmd"))) =
{
"bootm",
CFG_MAX_ARGS,
1,
do_bootm,
"bootm - boot application image from memory\n",
"[addr [arg ...]]\n - boot application image stored in memory\n"
"\tpassing arguments 'arg ...'; when booting a Linux kernel,\n"
"\t'arg' can be the address of an initrd image\n"
};
这样就为bootm命令定义了一个cmd_tbl_t 结构。注意定义的结构是放在”.u_boot.cmd”段中的,这是为了实现命令的查找功能。
4.2 命令查找
要想执行命令,必须先找到这个命令对应的cmd_tbl_t,然后才能调用命令处理函数。文件common/command.c 中的find_command()用来查找命令。这个函数根据命令名称来搜索命令列表,返回命令的cmd_tbl_t指针。
4.3 主循环
主循环的实现函数main_loop()在common/main.c中。这个函数包含了很多与AT91SAM9260EK关系不大的条件编译语句。
其中最重要的函数有两个:abortboot ()和 run_command()。
(1) abortboot ()
这个函数用来判断在指定的时间期限内用户是否选择了中止系统的引导。函数的定义也在common/main.c中。
(2) run_command()
这个函数用来执行命令。它可以执行一条命令,也可以执行用”;”分隔的多条命令(例如“nandread 20400000 0 200000; nand read 21100000 200000 400000; bootm 20400000”)。
5 linux 的引导
5.1 映象格式
映象文件必须满足U-boot的格式要求,才能被识别和引导。U-boot中映象文件必须以一个固定格式的头部开始。这个头部由struct image_header_t描述,image_header_t的定义在文件include/image.h中。
U-boot以源代码的形式提供了一个映象文件制作工具 mkimage(在 tools目录下),这个工具可以为指定的映象文件增加一个 image_header_t头部。
5.2 linux引导
通过前面的分析,我们知道,如果启动过程中用户不按键中止引导,命令序列bootcmd将会被执行。bootcmd的内容是"mmcinit; fatload mmc 0 0x81c00000 uImage; bootm 0x81c00"。这意味着将执行三条命令:
由此可见,linux的引导是通过bootm命令实现的。这个命令的处理函数是do_bootm(),在文件common/cmd_bootm.c中。U-boot可以引导多种操作系统。例如linux,vxworks,netbsd,QNX等等。
linux引导的第二阶段do_bootm_linux(),这个函数在lib_arm/armlinux.c 中。
5.3 linux的内核参数传递
linux引导的最后阶段,需要设置传递给内核的参数块,参数块的地址是物理内存起点+0x100。Linux 2.6 要求使用tagged list的方式设置参数块。do_bootm_linux()中使用 setup_start_tag(),setup_end_tag(),setup_XXX-tag()来完成参数块的设置。setup_start_tag(),setup_memory_tags(),setup_commandline_tag(),setup_initrd_tag()和setup_end_tag()。这些函数的定义都在lib_arm/armlinux.c中。
(1) setup_start_tag()
(2) setup_memory_tags()
每个memory tag表示一个存储区间。setup_memory_tags()设置所有的存储区间。
(3) setup_commandline_tag()
(4) setup_initrd_tag()
(5) setup_end_tag()
这个函数表示整个tagged list的结束。
至于tag类型的定义和基本操作,则是在include/asm-arm/setup.h中。
这里有最甜蜜的幸福,最博大的付出,最深刻的背叛,最强烈的绝望,最勇敢的坚强,最有力的反击!站在是非之外,看红尘最美不过月华清明、百花围簇!备注:在这个道德沦丧的时代,如果你想背弃誓言,轻易转身,那就不用再回头,因为没有人站在原地等你!