u-boot向Linux内核传递参数tag原理分析

    U-boot最主要的功能是引导OS,目前对Linux支持的相对比较好,“引导”的意义不仅仅是拷贝内核,运行内核,还要给内核kernel传递板子的相关参数,打个比方,u-boot相当于是一名专业功底深厚的 “接待员”,他会先初始化好一些外围设备,比如说串口,SDRAM、Nand Flash、MMC等,初始化这些 外围设备也是为了最终迎接Kernel的到来,比如初始化串口,自然是为了调试打印,初始化SDRAM是为了运行u-boot 本身,是u-boot的 运行的根本,初始化Nand Flash和MMC主要是为了搬迁u-boot和kernel,试想,kernel都是烧写在Nand Flash的,自然需要对应的驱动来读写Nand flash。

    u-boot在引导kernel的同时,还需要给kernel传递一些参数,这就相当于交代一些关键信息,比如这块板子的内存起始地址、内存大小、串口信息、root的类型等等,kernel是个“大拿”,不要想着直接去修改kernel 代码来实现这些功能,这不现实,也不科学,所以最好是按照kernel的接口协议,传递板子信息参数给kernel。

    u-boot的最后一句代码如下:

theKernel (0, machid, bd->bi_boot_params);

这条代码就是引导内核kernel的终极代码,向内核传递了3个参数,分别是0,machid、bd->bi_boot_params,去掉那个“0”其实就2个参数,写到这里你可以能怀疑,难道就这两个参数吗?答案是也 对也不对,对的是,确实只有这两个参数,不对的是,这个bd->bi_boot_params是个指针,也就是是个地址,它指向了所有 传递给内核的 参数,指针这个 东西就没谱了, 后面可以延伸n多个地址,也就是n个参数。

      kernel规定,如果想要传递参数给kernel,就需要将参数保存在struct tag类型的数据结构里,可以有n 多个,tag的数据 结构定义如下:

///include/asm-arm/setup.h
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;

		/*
		 * Acorn specific
		 */
		struct tag_acorn	acorn;

		/*
		 * DC21285 specific
		 */
		struct tag_memclk	memclk;
	} u;
};

//其中tag_header的定义如下:
struct tag_header {
	u32 size;
	u32 tag;
};

   从上面的定义可知,tag_header作为“头”,表示了这个tag的类型和大小,而后面的“共同体”代表了 几种不同的定义,共同体的作用是为了找“ 共同”,然后取最大空间公约数,上面的所有tag类型,并不是要全部赋值传递给kernel,而是根据移值板子的配置(mx28_evk.h),比如最常用,也是最基本的几种tag:tag_core、tag_mem32、tag_cmdline,假设 我们就只传递这几种tag,那么我们就需要在thekernel函数之前对这几种tag 进行初始化,去掉那个复杂的共同体表示方式,这三种tag的内容就如下:

//tag_core
struct tag {
	struct tag_header hdr;
	struct tag_core		core;
};
//tag_mem32
struct tag {
	struct tag_header hdr;
	struct tag_core		core;
};
//tag_cmdline
struct tag {
	struct tag_header hdr;
	struct tag_cmdline	cmdline;
};

 我们只需要把这3个tag顺序依次的存放到前面说的bd->bi_boot_params开始地址处即可。而且linux规定,tag列表的第一项必须是tag_core,最后一项必须是ATAG_NENE,自然我们也要遵从这个“协议规定”,这没什么好说的,不遵守,linux就不认。我们来看下这3个tag,u-boot是如何初始化的。

    前面讲到thekernel是最后一条代码,那么初始化tag必然也是要在这条代码之前的,我们在/lib_arm/bootm.c文件中可以找到引导kernel函数:do_bootm_linux,去掉条件编译如下所示:

int do_bootm_linux(int flag, int argc, char *argv[], bootm_headers_t *images)
{
	bd_t	*bd = gd->bd;
	char	*s;
	int	machid = bd->bi_arch_number;
	void	(*theKernel)(int zero, int arch, uint params);

    //环境变量-命令行参数bootargs获取
	char *commandline = getenv ("bootargs");
	if ((flag != 0) && (flag != BOOTM_STATE_OS_GO))
		return 1;

	theKernel = (void (*)(int, int, uint))images->ep;

	s = getenv ("machid");
	if (s) {
		machid = simple_strtoul (s, NULL, 16);
		printf ("Using machid 0x%x from environment\n", machid);
	}

	show_boot_progress (15);

	debug ("## Transferring control to Linux (at address %08lx) ...\n",
	       (ulong) theKernel);

    //安装tag_core
	setup_start_tag (bd);
    //安装tag_mem32
	setup_memory_tags (bd);
    //安装tag_cmdline
	setup_commandline_tag (bd, commandline);
    //tag操作结束
	setup_end_tag (bd);

	/* we assume that the kernel is in place */
	printf ("\nStarting kernel ...\n\n");

	cleanup_before_linux ();

	theKernel (0, machid, bd->bi_boot_params);
	/* does not return */

	return 1;
}

    从上面的代码可以知道,总共操作了4种tag,分别为tag_core、tag_mem32、tag_cmdline、tag end(就是结束),

   我们来分别看这个4中tag的操作函数:

static void setup_start_tag (bd_t *bd)
{
	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 = tag_next (params);
}


#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);

		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 */


static void setup_commandline_tag (bd_t *bd, char *commandline)
{
	char *p;

	if (!commandline)
		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;
	params->hdr.size =
		(sizeof (struct tag_header) + strlen (p) + 1 + 4) >> 2;

	strcpy (params->u.cmdline.cmdline, p);

	params = tag_next (params);
}

static void setup_end_tag (bd_t *bd)
{
	params->hdr.tag = ATAG_NONE;
	params->hdr.size = 0;
}

   从上面的 代码及顺序就可以很好的验证之前的分析,先 从ATAG_CORE开始,以ATAG_NONE结束,简单分析可知:

(1)ATAG_CORE:将bd->bi_boot_params赋值到tag_core,以便告诉内核从哪里开始找参数。

(2)ATAG_MEM:主要是告诉内核sdram的大小和开始地址。

(3)ATAG_CMDLINE:命令行参数,bootargs的赋值,这也是最重要的参数,kernel根据这个命令行参数来进行配置,这一点会再专门写一篇文章来分析。

(4)ATAG_NONE:结束,所以tag类型为ATAG_NONE,大小为0.

你可能感兴趣的:(U-boot)