【linux】U-BOOT與linux kernel通訊: struct tag

 

歡迎轉載,轉載時需保留做者信息。linux

郵箱:tangzhongp@163.comapi

博客園地址:http://www.cnblogs.com/embedded-tzp數組

Csdn博客地址:http://blog.csdn.net/xiayulewaide

 

 

u-boot與linux通訊格式

  

  

如上圖,開機時執行u-boot, u-boot引導完後,就是交給linux系統了,可是linux須要一些基本信息,如內存大小,啓動方式等,這就涉及到u-boot和linux通訊。 函數

而通訊格式由linux規定了,以其中atag格式舉例,詳情查閱Documentation/arm/Setup this

  

格式結構體爲struct tag: 定義在 Setup.h (src\arch\arm\include\uapi\asm):147 spa

struct tag { .net

struct tag_header hdr; 命令行

union { 3d

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;

};

 

clip_image001

u-boot會按照上述格式,在內存中劃分一塊atag參數區域,對該區域進行賦值。此處不對u-boot作討論,後面在專門寫文章。當賦值完成後,將cpu初始化成 MMU = off, D-cache = off, I-cache = dont care, r0 = 0, r1 = machine nr, r2 = atags or dtb pointer,跳轉到linux代碼起始處。

 

Linux atag參數解析流程

Linux代碼執行後,在第一階段(start_kernel函數以前),會驗證該區域正確性(bl __vet_atags)

進入第二階段後(start_kernel函數以後):會開始真正解析atag參數,並賦值給相應的變量。

 

Init.h (src\include\linux) :中定義了大部分的 section,比較重要的有

#define __init __section(.init.text) __cold notrace

#define __initdata __section(.init.data)

#define __initconst __constsection(.init.rodata)

#define __exitdata __section(.exit.data)

#define __exit_call __used __section(.exitcall.exit)

 

流程:start_kernel setup_arch→setup_machine_tags→parse_tagsparse_tag

 

setup_arch中調用:

mdesc = setup_machine_tags(__atags_pointer, __machine_arch_type);

 

__atags_pointer定義爲: unsigned int __atags_pointer __initdata;

__atags_pointer初始化: head-common.S (src\arch\arm\kernel):

__mmap_switched:

      adr r3, __mmap_switched_data

      ldmia   r3!, {r4, r5, r6, r7}

      ……………………

ARM(   ldmia   r3, {r4, r5, r6, r7, sp})

…………………….

str  r2, [r6]                 @ Save atags pointer

……………………

      .type    __mmap_switched_data, %object

__mmap_switched_data:

      .long    __data_loc                 @ r4

      .long    _sdata                  @ r5

      .long    __bss_start                @ r6

      .long    _end                    @ r7

      .long    processor_id              @ r4

      .long    __machine_arch_type           @ r5

      .long    __atags_pointer              @ r6

 

         在函數 setup_machine_tags中有:

    if (__atags_pointer)

        tags = phys_to_virt(__atags_pointer);

    else if (mdesc->atag_offset)

        tags = (void *)(PAGE_OFFSET + mdesc->atag_offset);

獲得tags = phys_to_virt(__atags_pointer); 見下圖:

clip_image003

 

parse_tags(tags);

 

static void __init parse_tags(const struct tag *t)

{

    for (; t->hdr.size; t = tag_next(t))

        if (!parse_tag(t))

            printk(KERN_WARNING

                "Ignoring unrecognised tag 0x%08x\n",

                t->hdr.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++)

        if (tag->hdr.tag == t->tag) {

            t->parse(tag);

            break;

        }

 

    return t < &__tagtable_end;

}

 

上述__tagtable_begin __tagtable_endsrc\arch\arm\kernel\vmlinux.lds

中定義:

.init.tagtable : {

  __tagtable_begin = .;

  *(.taglist.init)

  __tagtable_end = .;

 }

Setup.h (src\arch\arm\include\asm)能夠看到.taglist.init段定義:

#define __tag __used __attribute__((__section__(".taglist.init")))

#define __tagtable(tag, fn) \

static const struct tagtable __tagtable_##fn __tag = { tag, fn }

atags_parse.c (src\arch\arm\kernel) 中:有

__tagtable(ATAG_CORE, parse_tag_core);

__tagtable(ATAG_SERIAL, parse_tag_serialnr);等。

 

綜上:parse_tags是依次處理每一個struct tag, 對每一個struct tag,掃描__tagtable_begin__tagtable_end之間的struct tagtable,一旦對象的tag相等,則調用struct tagtableparse, 具體爲函數parse_tag_core, parse_tag_serialnr等函數。

 

u-boot命令行bootargs處理

http://blog.csdn.net/xiayulewa/article/details/45191679中有說過, u-boot能夠經過

setenv bootargs root=/dev/ram0 initrd=0x32000000,0x200000 rootfstype=ext2 console=ttySAC0,57600 init=/linuxrc ip=192.168.1.3

linux傳遞參數,詳細的過程不說,通過上述討論,知道u-bootlinux是以atag方式通訊的,實際閱讀u­-boot源代碼的確如此,上述紅色部分會以ATAG_CMDLINE的方式傳遞。

         Atags_parse.c (src\arch\arm\kernel)    中有:

__tagtable(ATAG_CMDLINE, parse_tag_cmdline);

parse_tag_cmdline設置了default_command_line數組。

而在setup_machine_tags函數中,        

           char *from = default_command_line;

.............

/* parse_early_param needs a boot_command_line */

strlcpy(boot_command_line, from, COMMAND_LINE_SIZE);

又將default_command_line拷貝到boot_command_line數組

 

 

boot_command_line數組處理流程爲:

start_kernel→parse_early_param(boot_command_line已經被setup_machine_tags函數賦值)

→parse_early_options→parse_args→parse_one→handle_unknown(param, val, doing)do_early_param函數

 

/* Check for early params. */

static int __init do_early_param(char *param, char *val, const char *unused)

{

    const struct obs_kernel_param *p;

 

    for (p = __setup_start; p < __setup_end; p++) {

        if ((p->early && parameq(param, p->str)) ||

            (strcmp(param, "console") == 0 &&

             strcmp(p->str, "earlycon") == 0)

        ) {

            if (p->setup_func(val) != 0)

                pr_warn("Malformed early option '%s'\n", param);

        }

    }

    /* We accept everything at this stage. */

    return 0;

}

 

src\arch\arm\kernel\vmlinux.lds中有:

__setup_start = .; *(.init.setup) __setup_end = .;

 

而在Init.h (src\include\linux)     中,有.init.setup段的定義:

#define __setup_param(str, unique_id, fn, early)                     \

         static const char __setup_str_##unique_id[] __initconst       \

                   __aligned(1) = str; \

         static struct obs_kernel_param __setup_##unique_id  \

                   __used __section(.init.setup)                        \

                   __attribute__((aligned((sizeof(long)))))   \

                   = { __setup_str_##unique_id, fn, early }

 

以串口處理爲例:Printk.c (src\kernel)中有

 __setup("console=", console_setup);

 

 

console=xxx串口名字爲什麼必須爲ttySAC

         前面已經討論到__setup("console=", console_setup);了,當內核參數中有console=xxx時,便會執行

console_setup__add_preferred_console

__add_preferred_console函數中有:

    for (i = 0; i < MAX_CMDLINECONSOLES && console_cmdline[i].name[0]; i++)

        if (strcmp(console_cmdline[i].name, name) == 0 &&

              console_cmdline[i].index == idx) {

                if (!brl_options)

                    selected_console = i;

                return 0;

    }

    .........

    c = &console_cmdline[i];

    strlcpy(c->name, name, sizeof(c->name));

    c->options = options;

 

此時console_cmdline[i].name還未初始化,爲空字符串,循環結束時i = 0。最後將console=xxx中的xxx賦值給console_cmdline[0].name

 

接下來,start_kernel→console_init

 

 

void __init console_init(void)

{

    initcall_t *call;

 

    /* Setup the default TTY line discipline. */

    tty_ldisc_begin();

 

    /*

     * set up the console device so that later boot sequences can

     * inform about problems etc..

     */

    call = __con_initcall_start;

    while (call < __con_initcall_end) {

        (*call)();

        call++;

    }

}

 

src\arch\arm\kernel\vmlinux.lds中有:

         __con_initcall_start = .; *(.con_initcall.init) __con_initcall_end = .;

Init.h (src\include\linux)中,有.con_initcall.init段定義。

#define console_initcall(fn) \

    static initcall_t __initcall_##fn \

    __used __section(.con_initcall.init) = fn

 

Samsung.c (src\drivers\tty\serial)中有:

         console_initcall(s3c24xx_serial_console_init);

 

    故綜上:有start_kernel→console_inits3c24xx_serial_console_initregister_console(&s3c24xx_serial_console);

其中有代碼:   

/*

     *  See if this console matches one we selected on

     *  the command line.

     */

    for (i = 0; i < MAX_CMDLINECONSOLES && console_cmdline[i].name[0];

            i++) {

        if (strcmp(console_cmdline[i].name, newcon->name) != 0)

            continue;

……………..

因而可知,只有s3c24xx_serial_console.nameconsole_cmdline[i].name的名字同樣,console才能被正常初始化。

static struct console s3c24xx_serial_console = {

    .name      = S3C24XX_SERIAL_NAME,

    ……….

#define S3C24XX_SERIAL_NAME        "ttySAC"

 

綜上:console=xxx串口名字必須爲ttySAC,使用方式爲下面紅色部分標註的:

setenv bootargs root=/dev/ram0 initrd=0x32000000,0x200000 rootfstype=ext2 console=ttySAC0,57600 init=/linuxrc ip=192.168.1.3

相關文章
相關標籤/搜索