分类: LINUX
2010-11-16 17:15:48
转自:http://blog.chinaunix.net/u3/90973/showart_1925725.html 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文件中(指Uboot的根目录),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 (¶ms); #endif #ifdef CONFIG_REVISION_TAG setup_revision_tag (¶ms); #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和arch/arm/mm/proc-arm920.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] = '\0'; 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。 |