… …
73 theKernel = (void (*)(int, int, uint))images->ep; /* 获取内核入口地址 */ … …
86 #if defined (CONFIG_SETUP_MEMORY_TAGS) || \\ 87 defined (CONFIG_CMDLINE_TAG) || \\ 88 defined (CONFIG_INITRD_TAG) || \\ 89 defined (CONFIG_SERIAL_TAG) || \\ 90 defined (CONFIG_REVISION_TAG) || \\ 91 defined (CONFIG_LCD) || \\ 92 defined (CONFIG_VFD)
93 setup_start_tag (bd); /* 设置ATAG_CORE标志 */ … …
100 #ifdef CONFIG_SETUP_MEMORY_TAGS
101 setup_memory_tags (bd); /* 设置内存标记 */ 102 #endif
103 #ifdef CONFIG_CMDLINE_TAG
104 setup_commandline_tag (bd, commandline); /* 设置命令行标记 */ 105 #endif … …
113 setup_end_tag (bd); /* 设置ATAG_NONE标志 */ 114 #endif 115
116 /* we assume that the kernel is in place */ 117 printf (\ … …
126 cleanup_before_linux (); /* 启动内核前对CPU作最后的设置 */ 127
128 theKernel (0, machid, bd->bi_boot_params); /* 调用内核 */ 129 /* does not return */ 130
131 return 1; 132 }
其中的setup_start_tag,setup_memory_tags,setup_end_tag函数在lib_arm/bootm.c中定义如下:
(1)setup_start_tag函数
static void setup_start_tag (bd_t *bd) {
params = (struct tag *) bd->bi_boot_params; /* 内核的参数的开始地址 */
params->hdr.tag = ATAG_CORE; params->hdr.size = tag_size (tag_core);
params->u.core.flags = 0; params->u.core.pagesize = 0; params->u.core.rootdev = 0;
params = tag_next (params); }
标记列表必须以ATAG_CORE开始,setup_start_tag函数在内核的参数的开始地址设置了一个ATAG_CORE标记。 (2)setup_memory_tags函数 static void setup_memory_tags (bd_t *bd) { int i;
/*设置一个内存标记 */
for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) { params->hdr.tag = ATAG_MEM;
params->hdr.size = tag_size (tag_mem32);
params->u.mem.start = bd->bi_dram[i].start; params->u.mem.size = bd->bi_dram[i].size;
params = tag_next (params); } }
setup_memory_tags函数设置了一个ATAG_MEM标记,该标记包含内存起始地址,内存大小这两个参数。 (3)setup_end_tag函数 static void setup_end_tag (bd_t *bd) {
params->hdr.tag = ATAG_NONE; params->hdr.size = 0; }
标记列表必须以标记ATAG_NONE结束,setup_end_tag函数设置了一个ATAG_NONE标记,表示标记列表的结束。
U-Boot设置好标记列表后就要调用内核了。但调用内核前,CPU必须满足下面的条件: (1) CPU寄存器的设置 ? r0=0 ? r1=机器码
? r2=内核参数标记列表在RAM中的起始地址 (2) CPU工作模式 ? 禁止IRQ与FIQ中断 ? CPU为SVC模式
(3) 使数据Cache与指令Cache失效
do_bootm_linux中调用的cleanup_before_linux函数完成了禁止中断和使Cache失效的功能。cleanup_before_linux函数在cpu/arm920t/cpu.中定义: int cleanup_before_linux (void) { /*
* this function is called just before we call linux * it prepares the processor for linux *
* we turn off caches etc ... */
disable_interrupts (); /* 禁止FIQ/IRQ中断 */
/* turn off I/D-cache */
icache_disable(); /* 使指令Cache失效 */ dcache_disable(); /* 使数据Cache失效 */ /* flush I/D-cache */
cache_flush(); /* 刷新Cache */
return 0; }
由于U-Boot启动以来就一直工作在SVC模式,因此CPU的工作模式就无需设置了。 do_bootm_linux中:
64 void (*theKernel)(int zero, int arch, uint params); … …
73 theKernel = (void (*)(int, int, uint))images->ep; … …
128 theKernel (0, machid, bd->bi_boot_params);
第73行代码将内核的入口地址“images->ep”强制类型转换为函数指针。根据ATPCS规则,函数的参数个数不超过4个时,使用r0~r3这4个寄存器来传递参数。因此第128行的函数调用则会将0放入r0,机器码machid放入r1,内核参数地址bd->bi_boot_params放入r2,从而完成了寄存器的设置,最后转到内核的入口地址。
到这里,U-Boot的工作就结束了,系统跳转到Linux内核代码执行。
1.1.4 U-Boot添加命令的方法及U-Boot命令执行过程
下面以添加menu命令(启动菜单)为例讲解U-Boot添加命令的方法。 (1) 建立common/cmd_menu.c
习惯上通用命令源代码放在common目录下,与开发板专有命令源代码则放在board/
在cmd_menu.c中使用如下的代码定义“menu”命令: _BOOT_CMD(
menu, 3, 0, do_menu,
\ \);
其中U_BOOT_CMD命令格式如下:
U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) 各个参数的意义如下:
name:命令名,非字符串,但在U_BOOT_CMD中用“#”符号转化为字符串 maxargs:命令的最大参数个数
rep:是否自动重复(按Enter键是否会重复执行) cmd:该命令对应的响应函数 usage:简短的使用说明(字符串) help:较详细的使用说明(字符串)
在内存中保存命令的help字段会占用一定的内存,通过配置U-Boot可以选择是否保存help字段。若在include/configs/mini2440.h中定义了CONFIG_SYS_LONGHELP宏,

