u-boot-1.1.6分析(五)启动流程之第二阶段

本文深入剖析U-Boot启动过程,从初始化堆空间、执行初始化序列到内核调用,详细解读U-Boot如何配置硬件、加载内核及传递启动参数。重点介绍了启动命令的获取、内核的读取与加载,以及启动参数的构造。

分析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 (&params);
#endif
#ifdef CONFIG_REVISION_TAG
	setup_revision_tag (&params);
#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不再起作用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值