• 【linux】U-BOOT与linux kernel通信: struct tag


     

    欢迎转载,转载时需保留作者信息。

    邮箱:tangzhongp@163.com

    博客园地址:http://www.cnblogs.com/embedded-tzp

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

     

    u-boot与linux通信格式

      

      

    如上图,开机时执行u-boot, u-boot引导完后,就是交给linux系统了,但是linux需要一些基本信息,如内存大小,启动方式等,这就涉及到u-boot和linux通信。

    而通信格式由linux规定了,以其中atag格式举例,详情查阅Documentation/arm/Setup

      

    格式结构体为struct tag: 定义在 Setup.h (srcarcharmincludeuapiasm):147

    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;

    };

     

    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 (srcincludelinux) :中定义了大部分的 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 (srcarcharmkernel):

    __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 ",

                    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_endsrcarcharmkernelvmlinux.lds

    中定义:

    .init.tagtable : {

      __tagtable_begin = .;

      *(.taglist.init)

      __tagtable_end = .;

     }

    Setup.h (srcarcharmincludeasm)可以看到.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 (srcarcharmkernel) 中:有

    __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 (srcarcharmkernel)    中有:

    __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' ", param);

            }

        }

        /* We accept everything at this stage. */

        return 0;

    }

     

    srcarcharmkernelvmlinux.lds中有:

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

     

    而在Init.h (srcincludelinux)     中,有.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 (srckernel)中有

     __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++;

        }

    }

     

    srcarcharmkernelvmlinux.lds中有:

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

    Init.h (srcincludelinux)中,有.con_initcall.init段定义。

    #define console_initcall(fn)

        static initcall_t __initcall_##fn

        __used __section(.con_initcall.init) = fn

     

    Samsung.c (srcdrivers tyserial)中有:

             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

  • 相关阅读:
    Mysql里的isnull(),ifnull(),nullif
    懒加载数据
    MyEclipse编辑xml文件没有提示
    java-五子棋游戏源码
    Java版打字练习游戏源码
    Wpf实现图片自动轮播自定义控件
    WP8.1开发:自定义控件
    简单的UIButton按钮动画效果ios源码下载
    自定义的一款选项卡ios源码
    Aisen微博应用源码完整版
  • 原文地址:https://www.cnblogs.com/embedded-tzp/p/4449143.html
Copyright © 2020-2023  润新知