您的位置:首页 > 运维架构 > Linux

Linux内核启动参数的传递

2010-08-16 16:47 615 查看
参考:

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐