Linux移植之tag参数列表解析过程分析

2024-08-26  

在Linux移植之内核启动过程start_kernel函数简析中已经指出了start_kernel函数的调用层次,这篇主要是对具体的tag参数列表进行解析。


1、内存参数ATAG_MEM参数解析


2、命令行参数ATAG_CMDLINE解析,以传入的命令参数bootargs=noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0为列:


  1)、noinitrd参数解析过程,当你没有使用ramdisk启动系统的时候,你需要使用noinitrd这个参数,但是如果使用了的话,就需要指定initrd=r_addr,size, r_addr表示initrd在内存中的位置,size表示initrd的大小。


  2)、root=/dev/mtdblock3参数解析过程


  3)、init=/linuxrc参数解析过程


  4)、 console=ttySAC0参数解析过程


start_kernel

    setup_arch                  //解析UBOOT传入的启动参数

    setup_command_line //解析UBOOT传入的启动参数

    do_early_param         //解析early参数,uboot中没传这个参数

    unknown_bootoption//解析到了命令行参数,saved_root_name在这块初始化

    console_init();//控制台初始化

    rest_init

        kernel_thread

            kernel_init

                prepare_namespace   //解析命令行参数解析成功挂接在哪个分区

                    mount_root//挂接根文件系统

                init_post

                    //执行应用程序


1、内存参数ATAG_MEM参数解析


看到archarmkernelSetup.c文件,在setup_arch函数里看到如下几行,首先根据内核启动时第一阶段得到的machine_arch_type,取得mdesc结构体,这个结构体在Linux移植之内核启动过程引导阶段分析已经介绍过,这里主要关心的是boot_params参数,里面存放的是tag参数列表的存放地址,然后将取得的物理地址转换为虚拟地址供后面使用tag。


776    console_init();//控制台初始化

777    archarmkernelSetup.c

778

779    setup_processor();//设置处理器相关的一些设置

780    mdesc = setup_machine(machine_arch_type);//获得开发板的machine_desc结构

781    machine_name = mdesc->name;//取得开发板的名称

782

783    if (mdesc->soft_reboot)

784        reboot_setup('s');

785

786    if (mdesc->boot_params)//确定uboot传入的启动参数的地址

787        tags = phys_to_virt(mdesc->boot_params);//将启动参数的物理地址转换为虚拟地址


setup_arch函数继续往下看


109    static struct meminfo meminfo __initdata = { 0, };


798    if (tags->hdr.tag == ATAG_CORE) {//ATAG_CORE为tag标记列表的开始

799        if (meminfo.nr_banks != 0)//如果已经在内核中定义了meminfo结构

780            squash_mem_tags(tags);//则忽略内存tag

781        parse_tags(tags);//解释每个tag

782    }


其中meminfo就是处理完ATAG_MEN参数后,将里面的内容放去meninfo中,它的结构定义在includeasm-armSetup.h 中


207    struct meminfo {

208        int nr_banks;

209        struct membank bank[NR_BANKS];

210    };

接着继续看parse_tags函数,它也位于archarmkernelSetup.c中



733    static void __init parse_tags(const struct tag *t)

734    {

735        for (; t->hdr.size; t = tag_next(t))//循环取出tag列表,然后处理

736            if (!parse_tag(t))                //处理取出的tag列表

737                printk(KERN_WARNING

738                    'Ignoring unrecognised tag 0x%08xn',

739                    t->hdr.tag);

740    }


接着分析parse_tag函数,它同样位于archarmkernelSetup.c中


715    static int __init parse_tag(const struct tag *tag)

716    {

717        extern struct tagtable __tagtable_begin, __tagtable_end;

718        struct tagtable *t;

719

720        for (t = &__tagtable_begin; t < &__tagtable_end; t++)//从.taglist.init段找出符合的处理tag列表的结构

721            if (tag->hdr.tag == t->tag) {//找到符合的tag

722                t->parse(tag);//调用相应的处理tag的函数处理

723                break;

724            }

725    

726        return t < &__tagtable_end;//t<&__tagtable_end说明找到了tag

727    }


parse_tag会从.taglist.init段找出符合的tag,然后调用相应的处理函数处理。tagtable 的结构如下,它位于includeasm-armSetup.h 中


171    struct tagtable {

172        __u32 tag;//处理的tag值

173        int (*parse)(const struct tag *);//处理函数

174    };

我们需要的是处理ATAG_MEN参数的函数,搜搜ATAG_MEN,在archarmkernelSetup.c中找到了parse_tag_mem32处理ATAG_MEN参数的函数。它的功能就是取出内存的开始地址与大小信息后存放在meminfo结构中


614    static int __init parse_tag_mem32(const struct tag *tag)

615    {

616        if (meminfo.nr_banks >= NR_BANKS) {

617            printk(KERN_WARNING

618                   'Ignoring memory bank 0x%08x size %dKBn',

619                tag->u.mem.start, tag->u.mem.size / 1024);

620            return -EINVAL;

621        }

622        arm_add_memory(tag->u.mem.start, tag->u.mem.size);//取出内存的开始地址与大小信息后存放在meminfo结构中

623        return 0;

624    }

625

626    __tagtable(ATAG_MEM, parse_tag_mem32);//解析ATAG_MEM列表,函数为parse_tag_mem32


再看到__tagtable,同样位于includeasm-armSetup.h中。主要就是将tagtable 这个结构体放在.taglist.init段


188    #define __tag __used __attribute__((__section__('.taglist.init')))

189    #define __tagtable(tag, fn)

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

到这里就分析完了tag列表中ATAG_MEM参数的处理,接下去分析ATAG_CMDLINE参数的处理。


2、命令行参数ATAG_CMDLINE解析


找到与ATAG_CMDLINE参数的过程与前面ATAG_MEM参数一样的流程就不分析了,直接找到处理ATAG_CMDLINE参数的函数,它位于archarmkernelSetup.c中。它只是简单的将tag->u.cmdline.cmdline的内容复制到default_command_line中。


702    static int __init parse_tag_cmdline(const struct tag *tag)

703    {

704        strlcpy(default_command_line, tag->u.cmdline.cmdline, COMMAND_LINE_SIZE);//简单的将tag的内容复制到字符串default_command_line中

705        return 0;

706    }

707

708    __tagtable(ATAG_CMDLINE, parse_tag_cmdline);


接着看到default_command_line,它定义在archarmkernelSetup.c中,它的大小为1024字节


114    static char default_command_line[COMMAND_LINE_SIZE] __initdata = CONFIG_CMDLINE;

它初始化为CONFIG_CMDLINE,位于includelinuxAutoconf.h中


374    #define CONFIG_CMDLINE 'root=/dev/hda1 ro init=/bin/bash console=ttySAC0'

所以拷贝之后


default_command_line[] = 'noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0'

继续往下看default_command_line,在archarmkernelSetup.c下的setup_arch函数中:其中parse_cmdline是对位于.early_param.init段的内容进行前期的初始化。相应的命令有:cachepolicy=、nocache、nowb、ecc=、initrd=、mem=等等,我们的参数没有涉及到这类命令,所以不去细细的分析这个函数了。


809    memcpy(boot_command_line, from, COMMAND_LINE_SIZE);//form指向default_command_line,将default_command_line中的内容拷贝到boot_command_line中

810    boot_command_line[COMMAND_LINE_SIZE-1] = '';//以''结束字符串

811    parse_cmdline(cmdline_p, from);//对位于.early_param.init段命令进行一些先期的处理

812    paging_init(&meminfo, mdesc);//重新初始化页表

813    request_standard_resources(&meminfo, mdesc);//资源的初始化

接着看到paging_init这个函数,这个函数调用了meminfo这个从ATAG_MEM取得的参数以及mdesc我们按照以下调用层次分析


paging_init

    devicemaps_init //设备maps初始化

        mdesc->map_io //调用map_io函数初始化

在archarmmach-s3c2440Mach-smdk2440.c中找到mdesc这个结构


339    MACHINE_START(S3C2440, 'SMDK2440')

340        /* Maintainer: Ben Dooks */

341        .phys_io    = S3C2410_PA_UART,

342        .io_pg_offst    = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,

343        .boot_params    = S3C2410_SDRAM_PA + 0x100,

344

345        .init_irq    = s3c24xx_init_irq,

346        .map_io        = smdk2440_map_io,

347        .init_machine    = smdk2440_machine_init,

348        .timer        = &s3c24xx_timer,

349    MACHINE_END


其中smdk2440_map_io就等要调用的函数,它同样位于archarmmach-s3c2440Mach-smdk2440.c下,可以看到这里修改过晶振的值。


324    static void __init smdk2440_map_io(void)

325    {

326        s3c24xx_init_io(smdk2440_iodesc, ARRAY_SIZE(smdk2440_iodesc));

327        s3c24xx_init_clocks(12000000);//根据开发板合适的晶振配置

328        s3c24xx_init_uarts(smdk2440_uartcfgs, ARRAY_SIZE(smdk2440_uartcfgs));

329    }


继续分析UBOOT传入的需要解析的参数:


1)、noinitrd参数解析过程


当没有使用ramdisk启动系统的时候,你需要使用noinitrd这个参数,但是如果使用了的话,就需要指定initrd=r_addr,size, r_addr表示initrd在内存中的位置,size表示initrd的大小。看到代码里面,位于initDo_mounts_initrd.c下,可以看到处理函数只是简单的将mount_initrd 置为0,说明不支持ramdisk启动。


19    static int __init no_initrd(char *str)

20    {

21        mount_initrd = 0;

22        return 1;

23    }

24

25    __setup('noinitrd', no_initrd);


接着分析一下__setup的定义,看到includelinuxInit.h里面有它的定义。


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

161        static char __setup_str_##unique_id[] __initdata = str;   

162        static struct obs_kernel_param __setup_##unique_id   

163            __attribute_used__               

164            __attribute__((__section__('.init.setup')))   

165            __attribute__((aligned((sizeof(long)))))   

166            = { __setup_str_##unique_id, fn, early }

167

168    #define __setup_null_param(str, unique_id)           

169        __setup_param(str, unique_id, NULL, 0)

170

171    #define __setup(str, fn)                   

文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。