uboot是如何传递内存等参数给linux内核的
[复制链接]
1.1.3 U-Boot启动Linux过程 U-Boot使用标记列表(tagged list)的方式向Linux传递参数。标记的数据结构式是tag,在U-Boot源代码目录include/asm-arm/setup.h中定义如下: struct tag_header { u32 size; /* 表示tag数据结构的联合u实质存放的数据的大小*/ u32 tag; /* 表示标记的类型 */ }; struct tag { struct tag_header hdr; union { struct tag_core core; struct tag_mem32 mem; struct tag_videotext videotext; struct tag_ramdisk ramdisk; struct tag_initrd initrd; struct tag_serialnr serialnr; struct tag_revision revision; struct tag_videolfb videolfb; struct tag_cmdline cmdline; /* * Acorn specific */ struct tag_acorn acorn; /* * DC21285 specific */ struct tag_memclk memclk; } u; }; U-Boot使用命令bootm来启动已经加载到内存中的内核。而bootm命令实际上调用的是do_bootm函数。对于Linux内核,do_bootm函数会调用do_bootm_linux函数来设置标记列表和启动内核。do_bootm_linux函数在lib_arm/bootm.c 中定义如下: 59 int do_bootm_linux(int flag, int argc, char *argv[], bootm_headers_t *images) 60 { 61 bd_t *bd = gd->bd; 62 char *s; 63 int machid = bd->bi_arch_number; 64 void (*theKernel)(int zero, int arch, uint params); 65 66 #ifdef CONFIG_CMDLINE_TAG 67 char *commandline = getenv ("bootargs"); /* U-Boot环境变量bootargs */ 68 #endif … … 73 theKernel = (void (*)(int, int, uint))images->ep; /* 获取内核入口地址 */ … … 86 #if defined (CONFIG_SETUP_MEMORY_TAGS) || \ 87 defined (CONFIG_CMDLINE_TAG) || \ 88 defined (CONFIG_INITRD_TAG) || \ 89 defined (CONFIG_SERIAL_TAG) || \ 90 defined (CONFIG_REVISION_TAG) || \ 91 defined (CONFIG_LCD) || \ 92 defined (CONFIG_VFD) 93 setup_start_tag (bd); /* 设置ATAG_CORE标志 */ … … 100 #ifdef CONFIG_SETUP_MEMORY_TAGS 101 setup_memory_tags (bd); /* 设置内存标记 */ 102 #endif 103 #ifdef CONFIG_CMDLINE_TAG 104 setup_commandline_tag (bd, commandline); /* 设置命令行标记 */ 105 #endif … … 113 setup_end_tag (bd); /* 设置ATAG_NONE标志*/ 114 #endif 115 116 /* we assume that the kernel is in place */ 117 printf ("\nStarting kernel ...\n\n"); … … 126 cleanup_before_linux (); /* 启动内核前对CPU作最后的设置 */ 127 128 theKernel (0, machid, bd->bi_boot_params); /* 调用内核 */ 129 /* does not return */ 130 131 return 1; 132 } 其中的setup_start_tag,setup_memory_tags,setup_end_tag函数在lib_arm/bootm.c中定义如下: (1)setup_start_tag函数 static void setup_start_tag (bd_t *bd) { params = (struct tag *) bd->bi_boot_params; /* 内核的参数的开始地址 */ 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); } 标记列表必须以ATAG_CORE开始,setup_start_tag函数在内核的参数的开始地址设置了一个ATAG_CORE标记。 (2)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.start; params->u.mem.size = bd->bi_dram.size; params = tag_next (params); } } setup_memory_tags函数设置了一个ATAG_MEM标记,该标记包含内存起始地址,内存大小这两个参数。 (3)setup_end_tag函数 static void setup_end_tag (bd_t *bd) { params->hdr.tag = ATAG_NONE; params->hdr.size = 0; } 标记列表必须以标记ATAG_NONE结束,setup_end_tag函数设置了一个ATAG_NONE标记,表示标记列表的结束。 U-Boot设置好标记列表后就要调用内核了。但调用内核前,CPU必须满足下面的条件: (1) CPU寄存器的设置 ? r0=0 ? r1=机器码 ? r2=内核参数标记列表在RAM中的起始地址 (2) CPU工作模式 ? 禁止IRQ与FIQ中断 ? CPU为SVC模式 (3) 使数据Cache与指令Cache失效 do_bootm_linux中调用的cleanup_before_linux函数完成了禁止中断和使Cache失效的功能。cleanup_before_linux函数在cpu/arm920t/cpu.中定义: int cleanup_before_linux (void) { /* * this is called just before we call linux * it prepares the processor for linux * * we turn off caches etc ... */ disable_interrupts (); /* 禁止FIQ/IRQ中断 */ /* turn off I/D-cache */ icache_disable(); /* 使指令Cache失效 */ dcache_disable(); /* 使数据Cache失效 */ /* flush I/D-cache */ cache_flush(); /* 刷新Cache */ return 0; } 由于U-Boot启动以来就一直工作在SVC模式,因此CPU的工作模式就无需设置了。 do_bootm_linux中: 64 void (*theKernel)(int zero, int arch, uint params); … … 73 theKernel = (void (*)(int, int, uint))images->ep; … … 128 theKernel (0, machid, bd->bi_boot_params); 第73行代码将内核的入口地址“images->ep”强制类型转换为函数指针。根据ATPCS规则,函数的参数个数不超过4个时,使用r0~r3这4个寄存器来传递参数。因此第128行的函数调用则会将0放入r0,机器码machid放入r1,内核参数地址bd->bi_boot_params放入r2,从而完成了寄存器的设置,最后转到内核的入口地址。 到这里,U-Boot的工作就结束了,系统跳转到Linux内核代码执行。
|