在上一篇中我们介绍了uboot启动过程中最先执行的代码start.S。
经过start.S的,uboot的代码已经从NAND FLASH中拷贝到RAM0x33f80000处
然后
ldr pc, _start_armboot
_start_armboot: .word start_armboot
接着,分析start_armboot。
如果你曾经接触过uboot的话,你就应该知道uboot的所有源文件基本都会参与
编译,但是基本上都是通过宏控制实际参与编译的内容。在本篇中,我会把没有定义相关宏的内容
自动忽略。
/* Pointer is writable since we allocated a register for it */
gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));
/* compiler optimization barrier needed for GCC >= 3.4 */
__asm__ __volatile__("": : :"memory");
memset ((void*)gd, 0, sizeof (gd_t));
gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
memset (gd->bd, 0, sizeof (bd_t));
其中gd的定义为#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm (“r8”)
可以看出gd是一个放在寄存器r8中的一个指针,指向gd_t结构。
__asm__ __volatile__("": : :"memory");
这里是一个特殊的用法:
1) asm用于指示编译器在此插入汇编语句。
2) volatile用于告诉编译器,严禁将此处的汇编语句与其它的语句重组合优化。即:原原本本按原来的样子处理这这里的汇编。
3) memory强制gcc编译器假设RAM所有内存单元均被汇编指令修改,这样cpu中的registers和cache中已缓存的内存单元中的数据将作废。cpu将不得不在需要的时候重新读取内存中的数据。这就阻止了cpu又将registers,cache中的数据用于去优化指令,而避免去访问内存。
4) “”:::表示这是个空指令。
关于更详细的阐述可参考https://www.cnblogs.com/pengdonglin137/p/3573972.html
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if ((*init_fnc_ptr)() != 0) {
hang ();
}
}
init_fnc_t *init_sequence[] = {
cpu_init,
/*对S3C2440的端口A-H进行初始化,设置bd参数bi_arch_number,bi_boot_params*/
board_init,
/*对定时器4进行初始化,用于计时之用,10ms超时*/
interrupt_init, /* set up exceptions */
/*对gd->env_addr进行设置,指向default_environment*/
env_init, /* initialize environment */
/*设置gd->bd->bi_baudrate*/
init_baudrate, /* initialze baudrate settings */
/*串口初始化函数,具体操作可参看前面前奏之uart章节*/
serial_init, /* serial communications setup */
console_init_f, /* stage 1 init of console */
display_banner, /* say that we are here */
/*初始化gd中关于RAM的参数配置*/
dram_init, /* configure available RAM banks */
display_dram_config,
NULL,
};
对NAND进行初始化,打印NAND大小
#if (CONFIG_COMMANDS & CFG_CMD_NAND)
puts ("NAND: ");
nand_init(); /* go init the NAND */
#endif
/* initialize environment */
env_relocate ();
/*读取环境变量,获取ip地址*/
/* IP Address */
gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");
/*获取配置网卡的MAC地址*/
/* MAC Address */
{
int i;
ulong reg;
char *s, *e;
char tmp[64];
i = getenv_r ("ethaddr", tmp, sizeof (tmp));
s = (i > 0) ? tmp : NULL;
for (reg = 0; reg < 6; ++reg) {
gd->bd->bi_enetaddr[reg] = s ? simple_strtoul (s, &e, 16) : 0;
if (s)
s = (*e) ? e + 1 : e;
}
}
/*创建dev_list 并添加了serial设备*/
devices_init (); /* get the devices list going. */
/*初始化console,将serial作为标准输入,标准输出以及错误输出*/
console_init_r (); /* fully init console as a device */
/*设置CPSR寄存器,开启中断*/
enable_interrupts ();
/* Initialize from environment */
if ((s = getenv ("loadaddr")) != NULL) {
load_addr = simple_strtoul (s, NULL, 16);
printf("load_addr:%p\n\r",load_addr);
}
#if (CONFIG_COMMANDS & CFG_CMD_NET)
/*从环境变量中初始化Bootfile变量*/
if ((s = getenv ("bootfile")) != NULL) {
copy_filename (BootFile, s, sizeof (BootFile));
printf("BootFile:%s\n\r",BootFile);
}
#endif /* CFG_CMD_NET */
#if (CONFIG_COMMANDS & CFG_CMD_NET)
eth_initialize(gd->bd);
#endif
以上基本就是start_armboot的全部初始化内容了。我们知道uboot的主要功能为加载系统到RAM中,并跳转过去执行。其实具体来说就是我们下一篇要讲的main_loop ()主体部分。