分析u-boot第二阶段(u-boot-1.1.6/lib_arm/board.c的start_armboot函数):
1、初始化堆空间
gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));
__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));
monitor_flash_len = _bss_start - _armboot_start; /*初始化堆空间*/
2、执行初始化序列
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if ((*init_fnc_ptr)() != 0) {
hang ();
}
}/*init_sequence数组里存放一系列初始化函数,循环调用,有一个出错则停止程序*/
下面是初始化序列
init_fnc_t *init_sequence[] = {
cpu_init, /* basic cpu dependent setup */
board_init, /* basic board dependent setup */
interrupt_init, /* set up exceptions */
env_init, /* initialize environment */
init_baudrate, /* initialze baudrate settings */
serial_init, /* serial communications setup */
console_init_f, /* stage 1 init of console */
display_banner, /* say that we are here */
#if defined(CONFIG_DISPLAY_CPUINFO)
print_cpuinfo, /* display cpu info (and speed) */
#endif
#if defined(CONFIG_DISPLAY_BOARDINFO)
checkboard, /* display board info */
#endif
dram_init, /* configure available RAM banks */
display_dram_config,
NULL,
};
在board_init设置了下面两个参数,用于传给内核
gd->bd->bi_arch_number = MACH_TYPE_SMDK2410;
gd->bd->bi_boot_params = 0x30000100;
在dram_init设置了
gd->bd->bi_dram[0].start = PHYS_SDRAM_1; //0x30000000
gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE; //0x04000000
3、nand初始化
nand_init();
4、初始化环境变量
env_relocate ();
5、跳入主循环
for (;;) {
main_loop ();
}
分析u-boot-1.1.6/common/main.c的main_loop函数的主要内容:
1、从环境变量中获得倒计时
s = getenv ("bootdelay"); /*获得启动倒计时*/
bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY; /*转换为数字*/
2、获得启动命令
s = getenv ("bootcmd"); /*获得启动命令*/
3、启动内核
如果按下空格键,程序会跳出if语句往下跑,进入菜单模式(一般用于调试,便于下载u-boot、内核、文件系统、设置u-boot环境变量等等),否则等待超时根据启动命令来启动内核
if (bootdelay >= 0 && s && !abortboot (bootdelay)) {
int prev = disable_ctrlc(1); /* disable Control C checking */
# ifndef CFG_HUSH_PARSER
/*定时到,启动内核*/
{
printf("Booting Linux ...\n");
run_command (s, 0); /*启动内核*/
}
# else
/*解析字符串*/
parse_string_outer(s, FLAG_PARSE_SEMICOLON |
FLAG_EXIT_FROM_LOOP);
# endif
disable_ctrlc(prev); /* restore Control C checking */
}
从前面分析可以知道,u-boot第二阶段主要是通过下面两行代码获得启动命令,启动内核
s = getenv ("bootcmd");
run_command (s, 0);
关于run_command会在下一节中介绍,下面继续跟踪u-boot对内核的调用
从一个正常使用的u-boot中可以看到,bootcmd中有两条命令
第一条命令是从nand中读取内核
第二条命令bootm,用于加载内核
分析u-boot-1.1.6/common/cmd_bootm.c的do_bootm函数即可以了解到u-boot调用内核的过程
在分析do_bootm之前,我们要知道内核在flash上的格式是uImage,它包含头部信息和真正的内核,头部信息结构类型定义在u-boot-1.1.6/include/image.h中:
typedef struct image_header {
uint32_t ih_magic; /* Image Header Magic Number */
uint32_t ih_hcrc; /* Image Header CRC Checksum */
uint32_t ih_time; /* Image Creation Timestamp */
uint32_t ih_size; /*内核大小*/
uint32_t ih_load; /*加载地址*/
uint32_t ih_ep; /*入口地址*/
uint32_t ih_dcrc; /* Image Data CRC Checksum */
uint8_t ih_os; /* Operating System */
uint8_t ih_arch; /* CPU架构*/
uint8_t ih_type; /*内核类型*/
uint8_t ih_comp; /* Compression Type */
uint8_t ih_name[IH_NMLEN]; /* Image Name */
} image_header_t;
do_bootm主要完成以下工作:
1、读出头部信息
memmove (&header, (char *)addr, sizeof(image_header_t));
2、将内核移动到加载地址(如果内核不位于加载地址)
if(ntohl(hdr->ih_load) == data) {
printf (" XIP %s ... ", name);
} else {
……
memmove ((void *) ntohl(hdr->ih_load), (uchar *)data, len);
}
3、调用do_bootm_linux启动内核
do_bootm_linux (cmdtp, flag, argc, argv, addr, len_ptr, verify);
do_bootm_linux启动内核主要有3步:
1、取出内核运行地址
theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep);
2、为内核准备一些启动参数
#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
启动参数相关的结构类型定义在u-boot-1.1.6/include/asm-arm/setup.h,例如:
tag头部:
struct tag_header {
u32 size;
u32 tag;
};
size:该tag的大小
tag:该tag的起始地址
内存tag主体:
struct tag_mem32 {
u32 size;
u32 start; /* physical start address */
};
size:内存块大小
start:内存块起始物理地址
命令行tag主体:
struct tag_cmdline {
char cmdline[1]; /* this is the minimum size */
};
cmdline:命令行参数
tag结构体,一个tag包含两部分
①:tag头部
②:tag主体,tag主体可能是联合体u中的任意一个
struct tag {
struct tag_header hdr;
union {
struct tag_core core;
struct tag_mem32 mem;
struct tag_videotext videotext;
struct tag_ramdisk ramdisk;
struct tag_initrd initrd;
struct tag_serialnr serialnr;
struct tag_revision revision;
struct tag_videolfb videolfb;
struct tag_cmdline cmdline;
struct tag_acorn acorn;
struct tag_memclk memclk;
} u;
};
每个tag是一个启动参数的单位,tag中含有一个tag头部和一个tag主体,tag主体是一个联合体
分析setup_start_tag、setup_memory_tags 、setup_commandline_tag 、setup_end_tag
①:start_tag是第一个tag,记录了一些内核相关的参数,也用于标志tag参数的开始
static void setup_start_tag (bd_t *bd)
{
/*这个参数定义在u-boot-1.1.6/board/smdk2410/smdk2410.c中:
*gd->bd->bi_boot_params = 0x30000100;
*故启动参数起始地址为0x30000100*/
params = (struct tag *) bd->bi_boot_params;
/*ATAG_CORE在u-boot-1.1.6/include/asm-arm/setup.h中定义为0x54410001*/
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);
}
②:memory_tags记录了dram相关的参数,描述各块dram的起始地址和大小
static void setup_memory_tags (bd_t *bd)
{
int i;
for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
params->hdr.tag = ATAG_MEM; /*0x54410002*/
params->hdr.size = tag_size (tag_mem32);
params->u.mem.start = bd->bi_dram[i].start; /*0x30000000,前面dram_init中设置*/
params->u.mem.size = bd->bi_dram[i].size; /*0x04000000,前面dram_init中设置*/
params = tag_next (params);
}
}
③:commandline_tag记录了命令行参数
static void setup_commandline_tag (bd_t *bd, char *commandline)
{
char *p;
if (!commandline) /*commandline是环境变量中的bootargs*/
return;
/* eat leading white space */
for (p = commandline; *p == ' '; p++);
/* skip non-existent command lines so the kernel will still
* use its default command line.
*/
if (*p == '\0')
return;
params->hdr.tag = ATAG_CMDLINE; /*0x54410009*/
params->hdr.size =
(sizeof (struct tag_header) + strlen (p) + 1 + 4) >> 2;
strcpy (params->u.cmdline.cmdline, p);
params = tag_next (params);
}
④:end_tag是最后一个tag,size=0,用于标志tag参数的结束
static void setup_end_tag (bd_t *bd)
{
params->hdr.tag = ATAG_NONE;
params->hdr.size = 0;
}
3、跳转到内核运行地址
theKernel (0, bd->bi_arch_number, bd->bi_boot_params);
bi_arch_number:机器ID,在u-boot-1.1.6/board/smdk2410/smdk2410.c中定义
bi_boot_params:启动参数的起始地址
u-boot跳转到内核后,所有需要的参数都已经通过bi_boot_params传入内核,u-boot不再起作用
本文深入剖析U-Boot启动过程,从初始化堆空间、执行初始化序列到内核调用,详细解读U-Boot如何配置硬件、加载内核及传递启动参数。重点介绍了启动命令的获取、内核的读取与加载,以及启动参数的构造。
173

被折叠的 条评论
为什么被折叠?



