uboot 在main.c下图432行的位置, main_loop() 函数,读取了环境变量"bootcmd" ,也就是启动命令。并 run_command(),即执行bootcmd。
这个环境变量可以在uboot 中使用print命令查看:
启动命令如下:
nand read.jffs2 0x30007FC0 kernel; bootm 0x30007FC0
启动命令的含义:
nand read.jffs2 0x30007FC0 kernel
,
从nand flash的 kernel 分区读取内核到地址0x30007FC0。
kernel 分区的定义在 include/configs/100ask24x0.h 中:
上面分区的名字不重要,重要的是起始地址和大小。在uboot 中可以使用mtd 命令查看分区:
可见kernel 分区的起始地址是0x00060000,大小是0x00200000。
因此,以下两条命令有一样的效果:
nand read.jffs2 0x30007FC0 kernel
nand read.jffs2 0x30007FC0 0x00060000 0x00200000
bootm 0x30007FC0
bootm 命令的功能可以分为两部分:
flash 上存的内核是 uImage,其结构是:头部+真正的内核。
头部包括:
上面重要的是加载地址 ih_load 和 入口地址 ih_ep 这两个值。bootm这个命令会将内核移动到 ih_load处,然后跳到 ih_ep 处执行。
入口地址 ih_ep 是0x30008000,头部是64字节,所以在加载的时候要将内核(包括头部)放到地址0x30007FC0 (0x30008000-64)处,这样可以避免bootm 再次移动内核,节省时间。
0x30008000 的由来可以参考这里:
简言之就是datasheet 中定义的内存映射基地址是0x3000 0000,且有32k(0x8000)用来存放内核页表。代码中定义如下:
上图代码中可以看出,SDRAM 的起始地址是0x30000000,大小是64M。
do_boot_linux() 最终通过调用
启动参数首先被存储到RAM 的某个约定好的位置,内核启动后再从这个约定好的位置读取启动参数。
在 do_bootm() -> do_boot_linux
中有如下代码
#if defined (CONFIG_SETUP_MEMORY_TAGS) || \
defined (CONFIG_CMDLINE_TAG) || \
defined (CONFIG_INITRD_TAG) || \
defined (CONFIG_SERIAL_TAG) || \
defined (CONFIG_REVISION_TAG) || \
defined (CONFIG_LCD) || \
defined (CONFIG_VFD)
setup_start_tag (bd);
#ifdef CONFIG_SERIAL_TAG
setup_serial_tag (¶ms);
#endif
#ifdef CONFIG_REVISION_TAG
setup_revision_tag (¶ms);
#endif
#ifdef CONFIG_SETUP_MEMORY_TAGS
setup_memory_tags (bd);
#endif
#ifdef CONFIG_CMDLINE_TAG
setup_commandline_tag (bd, commandline);
#endif
#ifdef CONFIG_INITRD_TAG
if (initrd_start && initrd_end)
setup_initrd_tag (bd, initrd_start, initrd_end);
#endif
#if defined (CONFIG_VFD) || defined (CONFIG_LCD)
setup_videolfb_tag ((gd_t *) gd);
#endif
setup_end_tag (bd);
#endif
代码中setup_XXXX_tag()_bd
就是设置各种启动参数的。
static void setup_start_tag (bd_t *bd)
{
// params 是一个不断移动的指针,指向当前启动参数将要保存到的地址
// 100ask24x0.c : gd->bd->bi_boot_params = 0x30000100;
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 指针指向下一块内存区域
params = tag_next (params);
// #define tag_next(t) ((struct tag *)((u32 *)(t) + (t)->hdr.size))
}
#ifdef CONFIG_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);
// 此处设置的RAM 的基地址,以及大小,代码如下
//100ask24x0.c : gd->bd->bi_dram[0].start = PHYS_SDRAM_1;
//100ask24x0.c : gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE;
// 100ask24x0.h : #define PHYS_SDRAM_1 0x30000000 /* SDRAM Bank #1 */
// 100ask24x0.h : #define PHYS_SDRAM_1_SIZE 0x04000000 /* 64 MB */
params->u.mem.start = bd->bi_dram[i].start;
params->u.mem.size = bd->bi_dram[i].size;
params = tag_next (params);
}
}
#endif /* CONFIG_SETUP_MEMORY_TAGS */
启动参数配置完成后,内存上的数据如下图所示,从0x30000100 位置开始保存了所有的启动参数:
armlinux.c -> do_bootm_linux():
...
// 给函数指针theKernel 赋值
theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep);
...
// 启动内核,第二个参数是机器id,第三个参数就是启动参数所在的地址
theKernel (0, bd->bi_arch_number, bd->bi_boot_params);
...