注:本文是学习朱老师课程整理的笔记,基于uboot-1.3.4和s5pc11x分析。
前面的分析见uboot之start_armboot分析2
- CFG_NO_FLASH
#ifndef CFG_NO_FLASH
/* configure available FLASH banks */
size = flash_init ();
display_flash_config (size);
#endif /* CFG_NO_FLASH */
虽然NandFlash和NorFlash都是Flash,但是一般NandFlash会简称为Nand而不是Flash,一般讲Flash都是指的Norflash。这里2行代码是Norflash相关的。
flash_init执行的是开发板中对应的NorFlash的初始化、display_flash_config打印的也是NorFlash的配置信息(Flash: 8 MB就是这里打印出来的)。但是实际上X210中是没有Norflash的。所以着两行代码是可以去掉的(我也不知道为什么没去掉?猜测原因有可能是去掉着两行代码会导致别的地方工作不正常,需要花时间去移植调试,然后移植的人就懒得弄。实际上不去掉除了显示有8MB Flash实际没用之外也没有别的影响)。
- mem_malloc_init
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);
}
mem_malloc_init函数用来初始化uboot的堆管理器。
uboot中自己维护了一段堆内存,肯定自己就有一套代码来管理这个堆内存。有了这些东西uboot中你也可以malloc、free这套机制来申请内存和释放内存。我们在DDR内存中给堆预留了896KB的内存。
- mmc初始化
#if defined(CONFIG_X210)
/* MMC的初始化 */
#if defined(CONFIG_GENERIC_MMC)
puts ("SD/MMC: ");
mmc_exist = mmc_initialize(gd->bd);
if (mmc_exist != 0)
{
puts ("0 MB\n");
}
#endif
/* NAND的初始化 */
#if defined(CONFIG_CMD_NAND)
puts("NAND: ");
nand_init();
#endif
#endif /* CONFIG_X210 */
三星用一套uboot同时满足了好多个系列型号的开发板,然后在这里把不同开发板自己独有的一些初始化写到了这里。用#if条件编译配合CONFIG_xxx宏来选定特定的开发板。x210开发板有两个版本,一个是SD卡版本,一个是NAND的版本。
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);
#if defined(DEBUG_S3C_HSMMC)
print_mmc_devices(',');
#endif
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;
}
}
/* mmc->capacity是扇区的数目,(1<<9)=512代表一个扇区有512个字节,相乘就
*是扇区的字节数,(1024*1024)代表MB的大小,之所以先除以(1024*1024),
*然后再乘以512,是因为如果先乘以512就有可能会溢出
*/
printf("%ldMB\n", (mmc->capacity/(1024*1024/(1<<9))));
return 0;
}
mmc_initialize看名字就应该是MMC相关的一些基础的初始化,其实就是用来初始化SoC内部的SD/MMC控制器的。函数在uboot/drivers/mmc/mmc.c里。uboot中对硬件的操作(譬如网卡、SD卡···)都是借用的linux内核中的驱动来实现的,uboot根目录底下有个drivers文件夹,这里面放的全都是从linux内核中移植过来的各种驱动源文件。
mmc_initialize是与具体硬件架构无关的一个MMC初始化函数,所有的使用了这套架构的代码都调用这个函数来完成MMC的初始化。
mmc_initialize中再调用board_mmc_init和cpu_mmc_init来完成具体的硬件的MMC控制器初始化工作。
cpu_mmc_init在uboot/cpu/s5pc11x/cpu.c中,这里面又间接的调用drivers/mmc/s3c_mmcxxx.c中的驱动代码来初始化硬件MMC控制器。这里面分层很多,分层的思想一定要有,否则完全就糊涂了。
- env_relocate
void env_relocate (void)
{
DEBUGF ("%s[%d] offset = 0x%lx\n", __FUNCTION__,__LINE__,
gd->reloc_off);
#ifdef ENV_IS_EMBEDDED
/*
* The environment buffer is embedded with the text segment,
* just relocate the environment pointer
*/
env_ptr = (env_t *)((ulong)env_ptr + gd->reloc_off);
DEBUGF ("%s[%d] embedded ENV at %p\n", __FUNCTION__,__LINE__,env_ptr);
#else
/*
* We must allocate a buffer for the environment
*/
env_ptr = (env_t *)malloc (CFG_ENV_SIZE);
DEBUGF ("%s[%d] malloced ENV at %p\n", __FUNCTION__,__LINE__,env_ptr);
#endif
if (gd->env_valid == 0) {
#if defined(CONFIG_GTH) || defined(CFG_ENV_IS_NOWHERE) /* Environment not changable */
puts ("Using default environment\n\n");
#else
puts ("*** Warning - bad CRC, using default environment\n\n");
show_boot_progress (-60); /* 显示进度,供调试用 */
#endif
set_default_env();
}
else {
env_relocate_spec ();
}
gd->env_addr = (ulong)&(env_ptr->data);
}
完成从SD卡中将环境变量读取到DDR中的任务。
环境变量到底从哪里来?SD卡中有一些(8个)独立的扇区作为环境变量存储区域的。但是我们烧录/部署系统时,我们只是烧录了uboot分区、kernel分区和rootfs分区,根本不曾烧录env分区。所以当我们烧录完系统第一次启动时ENV分区是空的,本次启动uboot尝试去SD卡的ENV分区读取环境变量时失败(读取回来后进行CRC校验时失败),我们uboot选择从uboot内部代码中设置的一套默认的环境变量出发来使用(这就是默认环境变量);这套默认的环境变量在本次运行时会被读取到DDR中的环境变量中,然后被写入(也可能是你saveenv时写入,也可能是uboot设计了第一次读取默认环境变量后就写入)SD卡的ENV分区。然后下次再次开机时uboot就会从SD卡的ENV分区读取环境变量到DDR中,这次读取就不会失败了。真正的从SD卡到DDR中重定位ENV的代码是在env_relocate_spec内部的movi_read_env完成的。
- IP地址、MAC地址的确定
/* 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;
}
……
IPaddr_t getenv_IPaddr (char *var)
{
return (string_to_ip(getenv(var)));
}
开发板的IP地址是在gd->bd中维护的,来源于环境变量ipaddr。getenv函数用来获取字符串格式的IP地址,然后用string_to_ip将字符串格式的IP地址转成字符串格式的点分十进制格式。
IPv4地址由4个0-255之间的数字组成,因此一个IP地址在程序中最简单的存储方法就是一个unsigend int。但是人类容易看懂的并不是这种类型,而是点分十进制类型(192.168.1.2)。这两种类型可以互相转换。
默认的环境变量被定义在x210_sd.h中:
/*#define CONFIG_BOOTARGS "root=ramfs devfs=mount console=ttySA0,9600" */
#define CONFIG_ETHADDR 00:40:5c:26:0a:5b
#define CONFIG_NETMASK 255.255.0.0
#define CONFIG_IPADDR 192.168.1.88
#define CONFIG_SERVERIP 192.168.1.102
#define CONFIG_GATEWAYIP 192.168.0.1
如果SD卡中的环境变量被重定位到了DDR中,则默认的环境变量将不起作用。
- devices_init
devices_init看名字就是设备的初始化。这里的设备指的就是开发板上的硬件设备。放在这里初始化的设备都是驱动设备,这个函数本来就是从驱动框架中衍生出来的。uboot中很多设备的驱动是直接移植linux内核的(譬如网卡、SD卡),linux内核中的驱动都有相应的设备初始化函数。
uboot的这个函数其实就是从linux内核中移植过来的,它的作用也是去执行所有的从linux内核中继承来的那些硬件驱动的初始化函数。
- jumptable_init
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;
#if defined(CONFIG_CMD_I2C)
gd->jt[XF_i2c_write] = (void *) i2c_write;
gd->jt[XF_i2c_read] = (void *) i2c_read;
#endif
}
jumptable跳转表,本身是一个函数指针数组,里面记录了很多函数的函数名。看这阵势是要实现一个函数指针到具体函数的映射关系,将来通过跳转表中的函数指针就可以执行具体的函数。这个其实就是在用C语言实现面向对象编程。在linux内核中有很多这种技巧。
后面的分析见uboot之start_armboot分析4