Linux内核---16.启动分析4uboot与内核的参数传递

来源:互联网 发布:矩阵分析与计算 pdf 编辑:程序博客网 时间:2024/05/17 12:53
硬件: TQ2440
软件: uboot1.1.6+ Linux2.6.25
一、 uboot 将参数copy到0x30000100处
1. 在uboot1.1.6/lib_arm/boot_zImage.c中
  1. 151 int boot_zImage(ulong from, size_t size)
  2. 152 {
  3.         #define LINUX_KERNEL_OFFSET 0x8000 
  4. 158     boot_mem_base = 0x30000000;
  5. 161     to = boot_mem_base + LINUX_KERNEL_OFFSET;
  6. 163     ret = copy_kernel_img(to, (char *)from, size);           // 将内核从nand_flash拷到内存中, to=0x30008000
  7. 171     if (*(ulong *)(to + 9*4) != LINUX_ZIMAGE_MAGIC) ;        // 检查zImage标志, 0x30008000+36处是zImage的标志
  8. 180     setup_linux_param(boot_mem_base + LINUX_PARAM_OFFSET);   // 将参数拷到内存0x30000100处
  9. 183     mach_type = MACH_TYPE_S3C2440;                           // MACH_TYPE_S3C2440=168
  10. 188     call_linux(0, mach_type, to);                            // 启动内核
  11. 190     return 0;
  12. 191 }

  13. 106 static void setup_linux_param(ulong param_base)
  14. 107 {
  15. 108      struct param_struct *params = (struct param_struct *)param_base;     // param_base=0x30000100; //将struct param_struct 着重显示一下
  16. 109      char *linux_cmd;
  17. 112      memset(params, 0, sizeof(struct param_struct));
  18. 113
  19. 114      params->u1.s.page_size = LINUX_PAGE_SIZE;
  20. 115      params->u1.s.nr_pages = (DRAM_SIZE >> LINUX_PAGE_SHIFT);
  21. 116
  22. 117      /* set linux command line */
  23. 118      linux_cmd = getenv ("bootargs");
  24. 119      if (linux_cmd == NULL) {
  25. 120         printk("Wrong magic: could not found linux command line\n");
  26. 121      } else {
  27. 122         memcpy(params->commandline, linux_cmd, strlen(linux_cmd) + 1);  //将bootargs拷到0x30000100+offset(commandline)处
  28. 124      }
  29. 125 }

  30.  75 void call_linux(long a0, long a1, long a2)                                //a0=0, a1=MACH_TYPE_S3C2440, a2=0x30008000
  31.  76 {
  32.  77     local_irq_disable();                                                    
  33.  78     cache_clean_invalidate();
  34.  79     tlb_invalidate();                                                    //启动内核前的必要准备工作,关irq, 清cache以及tlb
  35.  80
  36.  81     __asm__(
  37.  82      "mov r0, %0\n"                                                       //L82-L98启动内核: mov pc,r2即跳到0x30008000处执行
  38.  83      "mov r1, %1\n"                                                       //此时寄存器状态: r0=0, r1=MACH_TYPE_S3C2440, r2=0x30008000
  39.  84      "mov r2, %2\n"                                                       //其中有用的寄存器只有r1=MACH_TYPE_S3C2440
  40.  85      "mov ip, #0\n"                                                       //r0必须为0, 内核会把r2当成tagsList,检查后发现不对,就把r2赋成0,对内核来说这两个寄存器都没有多大用途
  41.  86      "mcr p15, 0, ip, c13, c0, 0\n" /* zero PID */          
  42.  87      "mcr p15, 0, ip, c7, c7, 0\n" /* invalidate I,D caches */
  43.  88      "mcr p15, 0, ip, c7, c10, 4\n" /* drain write buffer */
  44.  89      "mcr p15, 0, ip, c8, c7, 0\n" /* invalidate I,D TLBs */
  45.  90      "mrc p15, 0, ip, c1, c0, 0\n" /* get control register */
  46.  91      "bic ip, ip, #0x0001\n" /* disable MMU */
  47.  92      "mcr p15, 0, ip, c1, c0, 0\n" /* write control register */
  48.  93      "mov pc, r2\n"
  49.  94      "nop\n"
  50.  95      "nop\n"
  51.  96      : /* no outpus */
  52.  97      : "r" (a0), "r" (a1), "r" (a2)
  53.  98      : "r0","r1","r2","ip"
  54.  99     );
  55. 100 }
对于kernel来说,uboot传的参数是放在0x30000100处,并没有通过寄存器(r0,r1,r2...)传递参数地址.
二、 kernel 将参数从0x30000100处读取出来
2. zImage读取uboot参数过程

说明: 虽然进入内核之后会先解压, 对于TQ2440来说, 还是解压到0x30008000,而且寄存器没有变化,所以可以忽略解压的过程,可以认为内核是直接在0x30008000运行了.

2.1 进入start_kernel -->setup_arch
  1.  void __init setup_arch(char **cmdline_p)
  2.  789 {
  3.  790     struct tag *tags = (struct tag *)&init_tags;
  4.  791     struct machine_desc *mdesc;
  5.  792     char *from = default_command_line;
  6.  795     mdesc = setup_machine(machine_arch_type);                //见下文2.2: 会调用head-common.S中的lookup_machine_tye,这个跟arch/arm/head.S中的调用是一样的
  7.  796     machine_name = mdesc->name;                              //查找到正确的machine_desc结构体之后,在machine_desc中就有了boot_params的地址(0x30000100)
  8.  801     if (__atags_pointer)
  9.  802         tags = phys_to_virt(__atags_pointer);
  10.  803     else if (mdesc->boot_params)
  11.  804         tags = phys_to_virt(mdesc->boot_params);             //将物理地址(boot_params)转为虚拟地址,己经开启MMU了啊,亲. P->V(0x30000100-> 0xc0000100) 
  12.  805
  13.  810     if (tags->hdr.tag != ATAG_CORE)                         
  14.  811         convert_to_tag_list(tags);                   //见下文2.3: 将tags转为tagslist(可以理解为taglist_init),              
  15.                                                          //同时获取uboot中setup_linux_param传的参数
  16.                                                  //taglist的hdr.tag依次为ATAG_CORE ATAG_RAMDISK ATAG_INITRD ATAG_SERIAL ATAG_REVISION ATAG_CMDLINE ATAG_NONE 
  17.  818     if (tags->hdr.tag == ATAG_CORE) {
     819         if (meminfo.nr_banks != 0)
     820             squash_mem_tags(tags); //no exec
     821         save_atags(tags);
     822         parse_tags(tags);                          //见下文2.4 :将参数copy到default_command_line中,from指向default_command_line,所以现在的from就是uboot的参数      
     823     }
     824 
     825     init_mm.start_code = (unsigned long) &_text;
     826     init_mm.end_code   = (unsigned long) &_etext;
     827     init_mm.end_data   = (unsigned long) &_edata;
     828     init_mm.brk    = (unsigned long) &_end;
     829 
     830     memcpy(boot_command_line, from, COMMAND_LINE_SIZE);       //将参数copy到全局变量boot_cmmand_line中
     831     boot_command_line[COMMAND_LINE_SIZE-1] = '\0';                                     
     832     parse_cmdline(cmdline_p, from);                                 //见下文3.1解析参数
     833     paging_init(&meminfo, mdesc);
     834     request_standard_resources(&meminfo, mdesc);
     
  18.      }

2.2 setup_machine函数
  1. static struct machine_desc * __init setup_machine(unsigned int nr)
  2. {
  3.     struct machine_desc *list;
  4.     list = lookup_machine_type(nr);            //这个lookup_machine_type就是 arch/arm/kernel/head.S中的 __lookup_machine_type,只不过对C进行了一点封装
  5.     return list;
  6. }

  7. 2.2.1 在arch/arm/mach/arch.h 
    1. #define MACHINE_START(_type,_name) \
    2.     static const struct machine_desc __mach_desc_##_type \
    3.      __used \
    4.      __attribute__((__section__(".arch.info.init"))) = { \
    5.         .nr = MACH_TYPE_##_type, \
    6.         .name = _name,
    7.     #define MACHINE_END \
    8.     }
    2.2.2 在arch/arm/mach-s3c440/mach-smdk2440.c
    对结构体machine_desc进行初始化
      MACHINE_START(S3C2440, "SMDK2440")
    1.     /* Maintainer: Ben Dooks <ben@fluff.org> */
    2.     .phys_io    = S3C2410_PA_UART,
    3.     .io_pg_offst    = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
    4.     .boot_params    = S3C2410_SDRAM_PA + 0x100,                                    //boot_param=0x30000100
    5.     .init_irq    = s3c24xx_init_irq,
    6.     .map_io        = smdk2440_map_io,
    7.     .init_machine    = smdk2440_machine_init,
    8.     .timer        = &s3c24xx_timer,
    9. MACHINE_END


2.3 在arch/arm/kernel/compat.c
  1. 215 void __init convert_to_tag_list(struct tag *tags)
  2. 216 {
  3. 217      struct param_struct *params = (struct param_struct *)tags;        //最终再次见到struct param_struct结构体了
  4. 218      build_tag_list(params, &params->u2);
  5. 219 }
在arch/arm/kernel/compat.c中
  1. 95 static void __init build_tag_list(struct param_struct *params, void *taglist)  //这个参数params就是0x3000100的虚拟地址
  2. 96 {
  3. 201     tag = tag_next(tag);
  4. 202     tag->hdr.tag = ATAG_CMDLINE;
  5. 203     tag->hdr.size = (strlen(params->commandline) + 3 +
  6. 204     sizeof(struct tag_header)) >> 2;
  7. 205  strcpy(tag->u.cmdline.cmdline, params->commandline);    //获取uboot的setup_linux_param中的参数
    213 }
2.4 在 arch/arm/kernel/setup.c中
  1. start_kernel-->setup_arch-->parse_tags
  2. 在convert_to_tag_list(tags)中,己经把taglist的hdr.tag依次初始化为:
  3. ATAG_CORE
  4. ATAG_RAMDISK 
  5. ATAG_INITRD 
  6. ATAG_SERIAL 
  7. ATAG_REVISION 
  8. ATAG_CMDLINE 
  9. ATAG_NONE 
  10. 下面就按照list的内容依次调用其初始化函数
  11.  733 static int __init parse_tag(const struct tag *tag)
  12.  734 {
  13.  735     extern struct tagtable __tagtable_begin, __tagtable_end;
  14.  736     struct tagtable *t;
  15.  737 
  16.  738     for (= &__tagtable_begin; t < &__tagtable_end; t++)
  17.  739     if (tag->hdr.tag == t->tag) {
  18.  740      t->parse(tag);
  19.  741      break;
  20.  742     }
  21.  743 
  22.  744      return t < &__tagtable_end;
  23.  745 }

  24.  751 static void __init parse_tags(const struct tag *t)
  25.  752 {
  26.  753     for (; t->hdr.size; t = tag_next(t))                        //会调用head-common.
  27.  754     if (!parse_tag(t))
  28.  755         printk(KERN_WARNING
  29.  756         "Ignoring unrecognised tag 0x%08x\n",
  30.  757         t->hdr.tag);
  31.  758 }
  32.  759
ATAG_CMDLINE的初始化函数是parse_tag_cmdline, L739 t->parse(tag);就调用了这个
  1. 720 static int __init parse_tag_cmdline(const struct tag *tag)
  2.  721 {
  3.  722      strlcpy(default_command_line, tag->u.cmdline.cmdline, COMMAND_LINE_SIZE);  //tag->u.cmdline.cmdline就是uboot的参数,现在copy到default_command_line中
  4.  723      return 0;
  5.  724 }
  6.  725 
  7.  726 __tagtable(ATAG_CMDLINE, parse_tag_cmdline);
  8.  727

三、 kernel 解析参数
3.1 在 arch/arm/kernel/setup.c中
  1. 这个函数只会解析,几个特定的参数,如: "mem=" "initrd=" "ecc="  nowb nocache "cachepolicy="(没有等等,就这几个),其它的参数,它老人家就不管了 
  2.  505 static void __init parse_cmdline(char **cmdline_p, char *from)
  3.  506 {
  4.  507     char c = ' ', *to = command_line;
  5.  508     int len = 0;
  6.  509 
  7.  510     for (;;) {
  8.  511         if (== ' ') {
  9.  512            extern struct early_params __early_begin, __early_end;
  10.  513            struct early_params *p;
  11.  514 
  12.  515            for (= &__early_begin; p < &__early_end; p++) {
  13.  516                int len = strlen(p->arg);
  14.  517 
  15.  518                if (memcmp(from, p->arg, len) == 0) {
  16.  519                    if (to != command_line)
  17.  520                        to -= 1;
  18.  521                 from += len;
  19.  522                 p->fn(&from);
  20.  523 
  21.  524                  while (*from != ' ' && *from != '\0')
  22.  525                      from++;
  23.  526                break;
  24.  527                }
  25.  528            }
  26.  529         }
  27.  530     c = *from++;
  28.  531     if (!c)
  29.  532        break;
  30.  533     if (COMMAND_LINE_SIZE <= ++len)
  31.  534        break;
  32.  535      *to++ = c;
  33.  536     }
  34.  537      *to = '\0';
  35.  538     *cmdline_p = command_line;
  36.  539 }

3.2  在init/main中
start_kernel--> parse_early_param
  1. 476 void __init parse_early_param(void)
  2. 477 {
  3. 478     static __initdata int done = 0;
  4. 479     static __initdata char tmp_cmdline[COMMAND_LINE_SIZE];
  5. 480 
  6. 481      if (done)
  7. 482          return;
  8. 483 
  9. 484      /* All fall through to do_early_param. */
  10. 485     strlcpy(tmp_cmdline, boot_command_line, COMMAND_LINE_SIZE);
  11. 486     parse_args("early options", tmp_cmdline, NULL, 0, do_early_param);
  12. 487     done = 1;
  13. 488 }


3.3 在init/main中
start_kernel --> parse_args("Booting kernel", static_command_line, __start___param,    __stop___param - __start___param, &unknown_bootoption);
  1. 130 int parse_args(const char *name,
    131            char *args,
    132            struct kernel_param *params,
    133            unsigned num,
    134            int (*unknown)(char *param, char *val))
    135 {
    136     char *param, *val;
    137 
    138     DEBUGP("Parsing ARGS: %s\n", args);
    139 
    140     /* Chew leading spaces */
    141     while (*args == ' ')
    142         args++;
    143 
    144     while (*args) {
    145         int ret;
    146         int irq_was_disabled;
    147 
    148         args = next_arg(args, ?m, &val);
    149         irq_was_disabled = irqs_disabled();
    150         ret = parse_one(param, val, params, num, unknown);
    151         if (irq_was_disabled && !irqs_disabled()) {
    152             printk(KERN_WARNING "parse_args(): option '%s' enabled "
    153                     "irq's!\n", param);
    154         }
    155         switch (ret) {
    156         case -ENOENT:
    157             printk(KERN_ERR "%s: Unknown parameter `%s'\n",
    158                    name, param);
    159             return ret;
    160         case -ENOSPC:
    161             printk(KERN_ERR
    162                    "%s: `%s' too large for parameter `%s'\n",
    163                    name, val ?: "", param);
    164             return ret;
    165         case 0:
    166             break;
  2. 167         default:
    168             printk(KERN_ERR
    169                    "%s: `%s' invalid for parameter `%s'\n",
    170                    name, val ?: "", param);
    171             return ret;
    172         }
    173     }
    174 
    175     /* All parsed OK. */
    176     return 0;
    177 }

综上所述:
    还综上个屁啊, 这个参数传递也太简单了. uboot 会把要传给内核的参数copy到0x30000100处; 内核的setup_arch函数会直接到0x30000100把参数copy出来,然后进行解析.
0 0