zoukankan      html  css  js  c++  java
  • 引用 U-boot给kernel传参数和kernel读取参数—struct tag

    引用

    清风徐徐  U-boot给kernel传参数和kernel读取参数—struct tag 

     

            U-boot会给Linux Kernel传递很多参数,如:串口,RAM,videofb等。而Linux kernel也会读取和处理这些参数。两者之间通过struct tag来传递参数。U-boot把要传递给kernel的东西保存在struct tag数据结构中,启动kernel时,把这个结构体的物理地址传给kernel;Linux kernel通过这个地址,用parse_tags分析出传递过来的参数。

    本文主要以U-boot传递RAM和Linux kernel读取RAM参数为例进行说明。

    1、u-boot给kernel传RAM参数

           ./common/cmd_bootm.c文件中,bootm命令对应的do_bootm函数,当分析uImage中信息发现OS是Linux时,调用./lib_arm/bootm.c文件中的do_bootm_linux函数来启动Linux kernel。

           在do_bootm_linux函数中:

    void do_bootm_linux (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[],

                       ulong addr, ulong *len_ptr, int verify)

    {

    ......

    #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);      //初始化tag结构体开始

    #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);      //设置RAM参数

    #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);              //初始化tag结构体结束

    #endif

    ......

    ......

           theKernel (0, machid, bd->bi_boot_params);

    //传给Kernel的参数= (struct tag *)型的bd->bi_boot_params

    //bd->bi_boot_params在board_init函数中初始化如对于at91rm9200,初始化在at91rm9200dk.c的board_init中进行:bd->bi_boot_params=PHYS_SDRAM + 0x100;

    //这个地址也是所有taglist的首地址,见下面的setup_start_tag函数

    }

     

           对于setup_start_tag和setup_memory_tags函数说明如下。

           函数setup_start_tag也在此文件中定义,如下:

    static void setup_start_tag (bd_t *bd)

    {

           params = (struct tag *) bd->bi_boot_params;

    //初始化(struct tag *)型的全局变量params为bd->bi_boot_params的地址,之后的setup tags相关函数如下面的setup_memory_tags就把其它tag的数据放在此地址的偏移地址上。

     

           params->hdr.tag = ATAG_CORE;

           params->hdr.size = tag_size (tag_core);

           params->u.core.flags = 0;

           params->u.core.pagesize = 0;

           params->u.core.rootdev = 0;

           params = tag_next (params);

    }

          

    RAM相关参数在bootm.c中的函数setup_memory_tags中初始化:

    static void setup_memory_tags (bd_t *bd)

    {

           int i;

           for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {

                  params->hdr.tag = ATAG_MEM;

                  params->hdr.size = tag_size (tag_mem32);

                  params->u.mem.start = bd->bi_dram[i].start;

                  params->u.mem.size = bd->bi_dram[i].size;

                  params = tag_next (params);

           }                   //初始化内存相关tag

    }

     

    2、Kernel读取U-boot传递的相关参数

    对于Linux Kernel,ARM平台启动时,先执行arch/arm/kernel/head.S,此文件会调用arch/arm/kernel/head-common.S中的函数,并最后调用start_kernel:

    ......

    b     start_kernel

    ......

     

    init/main.c中的start_kernel函数中会调用setup_arch函数来处理各种平台相关的动作,包括了u-boot传递过来参数的分析和保存:

    start_kernel()

    {

    ......

           setup_arch(&command_line);

    ......

    }

          

           其中,setup_arch函数在arch/arm/kernel/setup.c文件中实现,如下:

    void __init setup_arch(char **cmdline_p)

    {

           struct tag *tags = (struct tag *)&init_tags;

           struct machine_desc *mdesc;

           char *from = default_command_line;

           setup_processor();

           mdesc = setup_machine(machine_arch_type);

           machine_name = mdesc->name;

           if (mdesc->soft_reboot)

                  reboot_setup("s");

           if (__atags_pointer)             

    //指向各种tag起始位置的指针,定义如下:

    //unsigned int __atags_pointer  __initdata;

    //此指针指向__initdata段,各种tag的信息保存在这个段中。

                  tags = phys_to_virt(__atags_pointer);

           else if (mdesc->boot_params)

                  tags = phys_to_virt(mdesc->boot_params);

           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);

                  save_atags(tags);

                  parse_tags(tags); 

    //处理各种tags,其中包括了RAM参数的处理。

    //这个函数处理如下tags:

    __tagtable(ATAG_MEM, parse_tag_mem32);

    __tagtable(ATAG_VIDEOTEXT, parse_tag_videotext);

    __tagtable(ATAG_RAMDISK, parse_tag_ramdisk);

    __tagtable(ATAG_SERIAL, parse_tag_serialnr);

    __tagtable(ATAG_REVISION, parse_tag_revision);

    __tagtable(ATAG_CMDLINE, parse_tag_cmdline);

           }

           init_mm.start_code = (unsigned long) &_text;

           init_mm.end_code   = (unsigned long) &_etext;

           init_mm.end_data   = (unsigned long) &_edata;

           init_mm.brk       = (unsigned long) &_end;

           memcpy(boot_command_line, from, COMMAND_LINE_SIZE);

           boot_command_line[COMMAND_LINE_SIZE-1] = '';

           parse_cmdline(cmdline_p, from);  //处理编译内核时指定的cmdline或u-boot传递的cmdline

           paging_init(&meminfo, mdesc);

           request_standard_resources(&meminfo, mdesc);

    #ifdef CONFIG_SMP

           smp_init_cpus();

    #endif

           cpu_init();

           init_arch_irq = mdesc->init_irq;

           system_timer = mdesc->timer;

           init_machine = mdesc->init_machine;

    #ifdef CONFIG_VT

    #if defined(CONFIG_VGA_CONSOLE)

           conswitchp = &vga_con;

    #elif defined(CONFIG_DUMMY_CONSOLE)

           conswitchp = &dummy_con;

    #endif

    #endif

           early_trap_init();

    }

     

    对于处理RAM的tag,调用了parse_tag_mem32函数:

    static int __init parse_tag_mem32(const struct tag *tag)

    {

    ......

           arm_add_memory(tag->u.mem.start, tag->u.mem.size);

    ......

    }

    __tagtable(ATAG_MEM, parse_tag_mem32);

           上述的arm_add_memory函数定义如下:

    static void __init arm_add_memory(unsigned long start, unsigned long size)

    {

           struct membank *bank;

           size -= start & ~PAGE_MASK;

     

           bank = &meminfo.bank[meminfo.nr_banks++];

           bank->start = PAGE_ALIGN(start);

           bank->size  = size & PAGE_MASK;

           bank->node  = PHYS_TO_NID(start);

    }

           如上可见,parse_tag_mem32函数调用arm_add_memory函数把RAM的start和size等参数保存到了meminfo结构的meminfo结构体中。最后,在setup_arch中执行下面语句:

           paging_init(&meminfo, mdesc);

           对有MMU的平台上调用arch/arm/mm/nommu.c中的paging_init,否则调用arch/arm/mm/mmu.c中的paging_init函数。这里暂不分析mmu.c中的paging_init函数。

     

     

    3、关于U-boot中的bd和gd

    U-boot中有一个用来保存很多有用信息的全局结构体--gd_t(global data缩写),其中包括了bd变量,可以说gd_t结构体包括了u-boot中所有重要全局变量。最后传递给内核的参数,都是从gd和bd中来的,如上述的setup_memory_tags函数作用就是用bd中的值来初始化RAM相应的tag。

    对于ARM平台这个结构体的定义大致如下:

    include/asm-arm/global_data.h

    typedef    struct      global_data {

           bd_t        *bd;

           unsigned long  flags;

           unsigned long  baudrate;

           unsigned long  have_console; /* serial_init() was called */

           unsigned long  reloc_off;       /* Relocation Offset */

           unsigned long  env_addr;       /* Address  of Environment struct */

           unsigned long  env_valid;       /* Checksum of Environment valid? */

           unsigned long  fb_base;  /* base address of frame buffer */

           void        **jt;        /* jump table */

    } gd_t;

     

    在U-boot中使用gd结构之前要用先用宏DECLARE_GLOBAL_DATA_PTR来声明。这个宏的定义如下:

    include/asm-arm/global_data.h

    #define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r8")

    从这个宏的定义可以看出,gd是一个保存在ARM的r8寄存器中的gd_t结构体的指针。

     

    说明:本文的版本为U-boot-1.3.4、Linux-2.6.28,平台是ARM。

  • 相关阅读:
    SEO高手和SEO屌丝的八个区
    【织梦免费模板】防火涂料网站模版
    利用火车头采集A67手机电影教程一
    SEO高级技巧
    .NET代码设计简单规范
    JAVA 和.NET在安全功能的比较
    poi实现excel数据的导入和导出
    eclipse复制bpmn文件到idea下乱码问题处理
    分账接收方与原请求方不一致,微信分账总结
    Java List<T> 去重
  • 原文地址:https://www.cnblogs.com/wangjingyuwhy/p/3417336.html
Copyright © 2011-2022 走看看