参考:
u-boot向内核传递参数解析:http://blog.chinaunix.net/u3/109474/showart_2205327.html
vivi学习(十七):vivi与Linux kernel的参数传递情景分析(下):http://tech.sunplusedu.com/space/post-6910.aspx
【环境】
Linux内核版本:V2.6.20
U-boot版本:V1.1.4
【简介】
本文描述Bootloader在启动时如何将内核启动的参数传递给内核,从内核和loader的角度分别描述其原理。
【描述】
在嵌入式设备中,Linux内核一般无法直接启动,而需要bootloader先初始化硬件环境,完成后加载内核,并将相关参数传递给内核。因此,在参数传递中,内核和bootloader需要约定两件事:1:参数表的地址;2:参数的结构体,以及相关赋值内容。
本文后续将详细描述这两个方面,其中参数表的地址为双方约定,并在编译时已经确定了;参数结构体,双方采用结构体struct tag(后续将有详细介绍)。
而传递的参数内容,当前为止有以下几部分:
#define ATAG_MEM 0x54410002 //内存参数,一定要传递
#define ATAG_VIDEOTEXT 0x54410003
#define ATAG_RAMDISK 0x54410004
#define ATAG_INITRD 0x54410005
#define ATAG_INITRD2 0x54420005
#define ATAG_SERIAL 0x54410006
#define ATAG_REVISION 0x54410007
#define ATAG_VIDEOLFB 0x54410008
#define ATAG_CMDLINE 0x54410009 //命令行参数,用得比较多,如在uboot中设置的bootargs
#define ATAG_ACORN 0x41000101
#define ATAG_MEMCLK 0x41000402
【Linux中内核启动参数地址的设定以及原理】
<启动参数地址的设置>
以smdk2410平台为例,在arch/arm/mach-s3c2410/mach-smdk2410.c有如下设置:
MACHINE_START(SMDK2410, "SMDK2410") /* @TODO: request a new identifier and switch
* to SMDK2410 */
/* Maintainer: Jonas Dietsche */
.phys_ram = S3C2410_SDRAM_PA,
.phys_io = S3C2410_PA_UART,
.io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
.boot_params = S3C2410_SDRAM_PA + 0x100,
.map_io = smdk2410_map_io,
.init_irq = smdk2410_init_irq,
.timer = &s3c24xx_timer,
MACHINE_END
其中的boot_params设置了启动参数。
<启动参数地址在启动时的初始化>
在arch/arm/kernel/setup.c中:
void __init setup_arch(char **cmdline_p)
{
struct tag *tags = (struct tag *)&init_tags;【4】
struct machine_desc *mdesc;
char *from = default_command_line;
......
mdesc = setup_machine(machine_arch_type);【1】
......
i
if (mdesc->boot_params) 【2】
tags = phys_to_virt(mdesc->boot_params);
/*
* If we have the old style parameters, convert them to
* a tag list.
*/
if (tags->hdr.tag != ATAG_CORE)
convert_to_tag_list(tags);
if (tags->hdr.tag != ATAG_CORE)
tags = (struct tag *)&init_tags;
if (mdesc->fixup)
mdesc->fixup(mdesc, tags, &from, &meminfo);
if (tags->hdr.tag == ATAG_CORE) {
if (meminfo.nr_banks != 0)
squash_mem_tags(tags);
parse_tags(tags);【3】
}
......
memcpy(saved_command_line, from, COMMAND_LINE_SIZE);
saved_command_line[COMMAND_LINE_SIZE-1] = '/0';
parse_cmdline(cmdline_p, from);
......
}
【1】
[setup_machine() 函数]
该函数实际上就是在__arch_info_begin与__arch_info_end中找匹配的machine_desc,如下所示:
static struct machine_desc * __init setup_machine(unsigned int nr)
{
extern struct machine_desc __arch_info_begin, __arch_info_end;
struct machine_desc *list;
/*
* locate architecture in the list of supported architectures.
*/
for (list = &__arch_info_begin; list < &__arch_info_end; list++)
if (list->nr == nr)
break;
/*
* If the architecture type is not recognised, then we
* can co nothing...
*/
if (list >= &__arch_info_end) {
printk("Architecture configuration botched (nr %d), unable "
"to continue./n", nr);
while (1);
}
printk("Machine: %s/n", list->name);
return list;
}
对于smdk2410平台,有如下定义:
//File: arch/arm/mach-smdk2410.c
MACHINE_START(SMDK2410, "SMDK2410") /* @TODO: request a new identifier and switch
* to SMDK2410 */
/* Maintainer: Jonas Dietsche */
.phys_io = S3C2410_PA_UART,
.io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
.boot_params = S3C2410_SDRAM_PA + 0x100,
.map_io = smdk2410_map_io,
.init_irq = s3c24xx_init_irq,
.init_machine = smdk_machine_init,
.timer = &s3c24xx_timer,
MACHINE_END
针对宏MACHINE_START与MACHINE_END的定义如下:
#define MACHINE_START(_type,_name) /
const struct machine_desc __mach_desc_##_type /
__attribute__((__section__(".arch.info"))) = { /
nr: MACH_TYPE_##_type, /
name: _name,
#define MAINTAINER(n)
#define BOOT_MEM(_pram,_pio,_vio) /
phys_ram: _pram, /
phys_io: _pio, /
io_pg_offst: ((_vio)>>18)&0xfffc,
#define BOOT_PARAMS(_params) /
param_offset: _params,
#define VIDEO(_start,_end) /
video_start: _start, /
video_end: _end,
#define DISABLE_PARPORT(_n) /
reserve_lp##_n: 1,
#define BROKEN_HLT /* unused */
#define SOFT_REBOOT /
soft_reboot: 1,
#define FIXUP(_func) /
fixup: _func,
#define MAPIO(_func) /
map_io: _func,
#define INITIRQ(_func) /
init_irq: _func,
#define MACHINE_END /
};
在编译链接脚本中有如下定义:
File: arch/arm/vmlinux-armv.lds.in
.init : { /* Init code and data */
_stext = .;
__init_begin = .;
*(.text.init)
__proc_info_begin = .;
*(.proc.info)
__proc_info_end = .;
__arch_info_begin = .;
*(.arch.info)
__arch_info_end = .;
__tagtable_begin = .;
*(.taglist)
__tagtable_end = .;
因此,实际上在__arch_info_begin与__arch_info_end之间存储了一个关于smdk2410配置的machine_desc类型的结构体。
在函数setup_machine()中,实际上通过扫描所有在该区域的machine_desc类型的结构体,看是否能找到满足传入参数类型的机器类型。
【2】
mdesc->boot_params定义的由loader传递过来的参数表的首地址,其为物理地址。
本来struct tag *tags = (struct tag *)&init_tags为默认的tag参数表地址,但如果mdesc->boot_params有定义,则表示使用由loader传递过来的参数表地址,tag重新赋值:tags = phys_to_virt(mdesc->boot_params);
【3】
parse_tags()函数是从首地址开始,扫描所有有效地tag,并执行相应的操作。
static void __init parse_tags(struct tag *t)
{
for (; t->hdr.tag != ATAG_NONE; t = tag_next(t))
if (!parse_tag(t))
printk(KERN_WARNING
"Ignoring unrecognised tag 0x%08x/n",
t->hdr.tag);
}
//看来所有的tag都以链表的形式串联了起来,最后一个tag的类型要为ATAG_NONE;
//对找到的每个tag,调用函数parse_tag()进行解析:
static int __init parse_tag(const struct tag *tag)
{
extern struct tagtable __tagtable_begin, __tagtable_end;
struct tagtable *t;
for (t = &__tagtable_begin; t < &__tagtable_end; t++)【5】
if (tag->hdr.tag == t->tag) {
t->parse(tag);
break;
}
return t < &__tagtable_end;
}
//该函数查找存储在__tagtable_begin中的所有类型的tag表,当找到有效地tag后,调用针对该tag的处理函数;
【4】
结构体tag的定义:
对于arm构架,其定义为:
File: include/asm-arm
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;
};
其中:
struct tag_header {
u32 size;
u32 tag;
};
【5】
关于tagtable
在arch/arm/kernel/setup.c中,有很多诸如以下的定义:
__tagtable(ATAG_REVISION, parse_tag_revision);
__tagtable(ATAG_CMDLINE, parse_tag_cmdline);
__tagtable(ATAG_SERIAL, parse_tag_serialnr);
......
宏__tagtable的定义如下:
#define __tagtable(tag, fn) /
static struct tagtable __tagtable_##fn __tag = { tag, fn }
#define __tag __attribute_used__ __attribute__((__section__(".taglist.init")))
因此,以上的__tagtable()的调用,相当于向.taglist.init段中插入各个tag的描述。
在arch/arm/kernel/vmlinux.lds.S#L38中,有如下:
__tagtable_begin = .;
*(.taglist.init)
__tagtable_end = .;
因此,在如上【5】中,相当于在__tagtable_begin中扫描各个有效地tagtable项。
附:在/include/asm-arm/setup.h中,定义了所有的tag类型如下:
struct tag_header {
__u32 size;
__u32 tag;
};
#define ATAG_NONE 0x00000000
#define ATAG_CORE 0x54410001
#define ATAG_MEM 0x54410002
#define ATAG_VIDEOTEXT 0x54410003
#define ATAG_RAMDISK 0x54410004
#define ATAG_INITRD 0x54410005
#define ATAG_INITRD2 0x54420005
#define ATAG_SERIAL 0x54410006
#define ATAG_REVISION 0x54410007
#define ATAG_VIDEOLFB 0x54410008
#define ATAG_CMDLINE 0x54410009
#define ATAG_ACORN 0x41000101
#define ATAG_MEMCLK 0x41000402
【Uboot中内核启动参数地址的设定以及原理】
<参数地址的确定>
以smdk2410为例,在smdk2410.c中的函数board_init()函数中,有:
int board_init (void)
{
......
/* adress of boot parameters */
gd->bd->bi_boot_params = 0x30000100;
......
}
此处即为传递参数的地址,改值需要与如上所述的MACHINE_START中的boot_params设置一致。
<启动参数传递的原理>
以arm平台为例,在armlinux.c中的函数,有:
void do_bootm_linux (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[],
ulong addr, ulong *len_ptr, int verify)
{
......
#ifdef CONFIG_CMDLINE_TAG
char *commandline = getenv ("bootargs");
#endif
......
#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
/* we assume that the kernel is in place */
printf ("/nStarting kernel .../n/n");
......
}
配置定义一般定于:
u-boot/u-boot-1.1.4/include/configs中各个board的文件中
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/Eric_tao/archive/2010/07/02/5708903.aspx