Linux內核啓動參數的傳遞

參考:

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 (&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

    /* we assume that the kernel is in place */

    printf ("/nStarting kernel .../n/n");

    

    ......

  
  
}  
  
配置定義一般定於:

  
u-boot/u-boot-1.1.4/include/configs中各個board的文件中  
  
  
  
  
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章