uboot启动第二阶段–start_armboot函数分析(1)
在start_armboot 函数中的第491行和492行分别进行了flash的初始化和信息的打印。这儿的flash指的是NorFlash,而不是NandFlash,但我们用的开发板并没有NorFlash,所以这儿的初始化并没有意义,至于为什么没有硬件初始化也不会失败是因为这儿的初始化是纯软件的,与硬件无关。代码如下:
size = flash_init ();
display_flash_config (size);
在第495行与508行分别判断了这两个宏是否定义,来决定要不要用uboot自带的LCD初始化代码,我们移植的这个uboot并没有用它,而是在后面自己重新实现了uboot的LCD初始化。
在526行处,start_armboot 函数调用了mem_malloc_init 函数,该函数初始化了uboot自己维护的一段堆内存,关于uboot的堆,详见此链接,该函数内容如下:
static ulong mem_malloc_start = 0;
static ulong mem_malloc_end = 0;
static ulong mem_malloc_brk = 0;
static void mem_malloc_init (ulong dest_addr)
{
mem_malloc_start = dest_addr;
mem_malloc_end = dest_addr + CFG_MALLOC_LEN;
mem_malloc_brk = mem_malloc_start;
memset ((void *) mem_malloc_start, 0,
mem_malloc_end - mem_malloc_start);
}
该函数设置了堆的开始地址,结束地址,当前地址,然后将堆内存进行了清0,入口参数也在上面的链接中有所说明CFG_UBOOT_BASE + CFG_UBOOT_SIZE - CFG_MALLOC_LEN - CFG_STACK_SIZE。
在start_armboot 函数第603行初始化了mmc/sd设备,并且输出了"SD/MMC: 3776MB"类似的内容,初始化函数如下:
int mmc_initialize(bd_t *bis)
{
struct mmc *mmc;
int err;
INIT_LIST_HEAD(&mmc_devices);
cur_dev_num = 0;
if (board_mmc_init(bis) < 0)
cpu_mmc_init(bis);
mmc = find_mmc_device(0);
if (mmc) {
err = mmc_init(mmc);
if (err)
err = mmc_init(mmc);
if (err) {
printf("Card init fail!\n");
return err;
}
}
printf("%ldMB\n", (mmc->capacity/(1024*1024/(1<<9))));
return 0;
}
这个函数首先初始化MMC设备链表节点,board_mmc_init 函数与cpu_mmc_init 函数都是mmc初始化函数,差别是一个控制器是板级的,另一个是芯片内部的,S5PV210芯片内部有控制器,所以选择第二个,第一个直接返回-1,在cpu_mmc_init 函数中,初始化了mmc/sd的管脚,时钟,控制器寄存器。然后又初始化了mmc/sd设备(区别于先前,这是初始化sd/mmc本身,之前是S5PV210的控制器),最后打印了MMC(我们开发板有一块4GB的iNand)卡的容量。另外,在第一阶段之前能将第一阶段代码读入SRAM中说明iNand与SD卡已经在iROM中初始化过了,在这重新初始化可能是之前未初始化完全。
该函数对环境变量进行了重定位,函数如下:
void env_relocate (void)
{
/*
* We must allocate a buffer for the environment
*/
env_ptr = (env_t *)malloc (CFG_ENV_SIZE);
if (gd->env_valid == 0) {
puts ("*** Warning - bad CRC, using default environment\n\n");
show_boot_progress (-60);
set_default_env();
}
else {
env_relocate_spec ();
}
gd->env_addr = (ulong)&(env_ptr->data);
}
这个函数为环境变量建立的机制是若没有有效的环境变量,就使用默认的环境变量,否则将会使用扇区内的环境变量。由于在之前的env_init中已经将gd->env_valid置为1,所以默认使用扇区内的环境变量,即真正重定位环境变量的代码是env_relocate_spec 函数中的movi_read_env 函数。
/* IP Address */
gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");
/* 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;
}
}
环境变量值得获取主要是为全局变量值的赋值,另外由于在计算机中使用的是unsigned int,所以eth地址要进行转化。
devices_init(); /* get the devices list going. */
jumptable_init();
console_init_r(); /* fully init console as a device */
/* enable exceptions */
enable_interrupts();
/* Initialize from environment */
if ((s = getenv("loadaddr")) != NULL) {
load_addr = simple_strtoul(s, NULL, 16);
}
if ((s = getenv("bootfile")) != NULL) {
copy_filename(BootFile, s, sizeof (BootFile));
}
board_late_init();
eth_initialize(gd->bd);
这个函数原本是linux内核中初始化设备的一个函数,现在移植到uboot中来,但其实里面什么也没做。
这个函数为全局变量gd->jt赋值,通过这个变量将一些常用的函数地址赋给地址为gd->jt的数组,内容如下:
void jumptable_init (void)
{
int i;
gd->jt = (void **) malloc (XF_MAX * sizeof (void *));
for (i = 0; i < XF_MAX; i++)
gd->jt[i] = (void *) dummy;
gd->jt[XF_get_version] = (void *) get_version;
gd->jt[XF_malloc] = (void *) malloc;
gd->jt[XF_free] = (void *) free;
gd->jt[XF_getenv] = (void *) getenv;
gd->jt[XF_setenv] = (void *) setenv;
gd->jt[XF_get_timer] = (void *) get_timer;
gd->jt[XF_simple_strtoul] = (void *) simple_strtoul;
gd->jt[XF_udelay] = (void *) udelay;
gd->jt[XF_simple_strtol] = (void *) simple_strtol;
gd->jt[XF_strcmp] = (void *) strcmp;
}
这个函数是控制台初始化函数的第二阶段,第一阶段没有什么干实际的工作,只是为一个全局变量赋值,第二阶段实际上初始化了输入和输出,显示错误的设备,这个架构实际上使用的linux得框架,并且最后将这些设备打印出来,如下:
In: serial
Out: serial
Err: serial
其实这个控制台最后还是用的串口直接输出的信息,和不用控制台没什么区别。
这个函数里面什么也没有,推测应该是之前想放中断初始化的代码,不过uboot里面没有用到中断系统,所以是一个空函数。
这两个变量是环境变量,start_armboot 函数通过这两个变量初始化了load_addr与BootFile。
这个函数是开发板后期一些需要初始化的函数,对于X210开发板,这个函数里面是空的。
这个函数是网卡的初始化,但我们开发板的网卡在board_init 里已经初始化过了,所以这个函数是空的。
这个函数中调用了mpadfb_init 函数,mpadfb_init 函数是X210的uboot中驱动代码,用来初始化LCD,并显示启动时LCD上显示的logo。
extern void update_all(void);
if(check_menu_update_from_sd()==0)//update mode
{
puts("[LEFT DOWN] update mode\n");
run_command("fdisk -c 0",0);
update_all();
}
else
puts("[LEFT UP] boot mode\n");
check_menu_update_from_sd 函数中检测left按键是否按下,在uboot启动过程中,left按键按下表示更新模式,弹起表示启动模式。uboot提供了一种更新机制,可以方便的进行烧录系统。
for (;;) {
main_loop();
}
死循环中执行了一个main_loop函数,里面包含了我们uboot命令体系,解析执行命令以及开机倒数,fastboot等等,总之在uboot启动后的shell就是执行这个函数。