Chinaunix首页 | 论坛 | 博客
  • 博客访问: 117461
  • 博文数量: 31
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 296
  • 用 户 组: 普通用户
  • 注册时间: 2015-01-10 21:57
文章分类

全部博文(31)

文章存档

2016年(4)

2015年(27)

我的朋友

分类: LINUX

2015-03-19 14:08:02

这里我们先搞清楚一个问题,在kernel配置选项Boot options中有一个Default kernel command string参数项,而在u-boot参数中也有一个bootargs参数项,他们都是供内核启动用的,那他们又有什么区别呢,内核启动时到底是用哪一个呢?两种参数项分别如下图所示(kernel中的参数指定是从开发板Flash分区上挂载文件系统,u-boot中的参数指定的是从

NFS挂载文件系统)

 


  实际上,内核中的参数项是内核默认提供的,在内核配置时去指定,而u-boot提供的

则在u-boot启动时传递到内核中取代内核提供的参数。所以当u-boot没有提供bootargs参数时,内核启动就是用内核配置时指定的参数,当u-boot提供了bootargs参数时就使用u-boot的参数。

前面在U-Boot启动过程中讲到,在do_boot_linux函数中会会调用“theKernel (0, bd->bi_arch_number, bd->bi_boot_params)”启动内核,其theKernel指向内核存放内存地址,这个用来实现程序跳转到内核存在的内存地址处,开始内核的执行。bd->bi_arch_number就是前面board_init函数设置的机器类型ID,在内核引导阶段的__lookup_processor_type函数已经用到。而bd->bi_boot_params就是标记列表的开始地址,预先存在该地址的存放一个tag列表, tag列表将在setup_arch函数中进行初步的处理。

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_desc结构*/

machine_name = mdesc->name;

if (mdesc->soft_reboot)

reboot_setup("s");

if (__atags_pointer)                  

    tags = phys_to_virt(__atags_pointer);

else if (mdesc->boot_params)  /*定义了Bootloader的传入参数的地址*/

    tags = phys_to_virt(mdesc->boot_params);/*这个地址就是tag列表地址*/phys_to_virt时将已经映射的物理内存的地址转换为虚拟地址

/*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) /*如果已在内核中定义了meminfo结构*/

squash_mem_tags(tags); ;/*忽略内存tag*/

save_atags(tags);

parse_tags(tags);} /*解释每个tag*/}

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;

/* parse_early_param needs a boot_command_line */

strlcpy(boot_command_line, from, COMMAND_LINE_SIZE);

/* populate cmd_line too for later use, preserving boot_command_line */

strlcpy(cmd_line, boot_command_line, COMMAND_LINE_SIZE);

*cmdline_p = cmd_line;

parse_early_param();

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

request_standard_resources(&meminfo, mdesc);

#ifdef CONFIG_SMP

smp_init_cpus();

#endif

cpu_init();

tcm_init();

init_arch_irq = mdesc->init_irq;

system_timer = mdesc->timer;

init_machine = mdesc->init_machine;

……

early_trap_init();

}

setup_processor()被用来进行处理器的相关的一些设置,它会调用引导阶段的__lookup_processor_type函数从处理器内核描述符表中找到匹配的描述符(proc_init_list结构),并初始化一些处理器变量。setup_machine用机器编号ID(在解压函数decompress_kernel 中被赋值)作为参数返回机器描述符,它会调用引导阶段的__lookup_machine_type函数来来获得开发板的机器描述符(machine_desc结构)。从机器描述符中获得内核参数的物理地址,赋值给tags 变量。然后调用parse_tags()函数分析内核参数链表,把各个参数值传递给全局变量。这样内核就收到了u-boot传递的参数。

arch/arm/mach-s3c6410/ mach- smdk6410.c中有如下定义,启动参数地址为(S3C_SDRAM_PA + 0x100),即为0x50000100

MACHINE_START(SMDK6410, "SMDK6410")

……

.boot_params    = S3C_SDRAM_PA + 0x100,

由上面的代码可以看出,由machine_desc结构我们可以确定Bootloader的传入参数的地址,由于开启了mmu,在使用时,我们需要调用phys_to_virt()函数将其转化为虚拟地址。

parse_tags()函数用来处理每个标记。文件arch/arm/kernel/setup.c对每种标记都定义了相关的处理函数。比如对内存标记、命令行标记,使用如下代码为它们指定了处理函数为parse_tag_mem32()parse_tag_cmdline()

__tagtable(ATAG_MEM, parse_tag_mem32);

__tagtable(ATAG_CMDLINE, parse_tag_cmdline);

其中parse_tag_mem32()函数根据内存标记定义内存的起始地址、长度,在全局变量meminfo中增加内存的描述信息。以后内核就可以通过meminfo结构了解开发板的内存信息。parse_tag_cmdline()只是简单的将命令行标记的内容复制到字符串default_command_line中保存下来,后面会进一步处理。

parse_cmdline( )扫描命令行参数,对其中一些参数进行先期的处理,比如对参数meminitrd,使用如下代码为它们指定了处理函数为early_mem()early_ initrd()

__early_param("mem=", early_mem);

__early_param("initrd=", early_initrd);

"mem="用来强制限制linux系统所能使用的内存总量。比如"mem=60M"使得系统只能使用60M的内存,即使是tag中指明了64M内存。本文中没有设置这样类命令行参数。

命令行的处理没有就此结束,在setup_arch函数函数之外还进行了一系列的后继处理,比如在start_kernel函数中调用了如下代码:

setup_arch(&command_line);

    setup_command_line(command_line);

……

parse_early_param();

parse_args("Booting kernel", static_command_line, __start___param,

   __stop___param - __start___param,&unknown_bootoption);   //解析由BOOT传递的启动参数

   比如对于命令“console=ttySAC0”,它的处理过程就是通过parse_args函数调用传入的unknown_bootoption函数,最后调用由下面代码指定的console_setup处理函数:

   __setup(“console=”, console_setup)

“console=”用来指定要使用的控制台名称、序号、参数。比如“console= ttySAC0115200”,表示要使用的控制台名称为ttySAC序号为0即第一个串口),波特率为115200。经过console_setup函数处处理之后,会在全局结构console_cmdline中保存这些信息,然后在后面console_init函数初始化控制台时会根据这些信息选择要使用的控制台。

阅读(2105) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~