uboot在启动内核时,会向内核传递一些参数。据说bootloader有两种方式向内核传递参数,另外一种我不了解,本文仅介绍arm架构下tag结构的传参方式。
uboot在启动内核时,会传递一些参数,包括:RAM位置和尺寸,命令行参数,initrd起始位置和尺寸,framebuffer物理地址和尺寸,开发板版本等。
uboot把每一个参数打包到一个tag结构中(tag结构参见表1),tag列表以ATAG_CORE tag起始,以ATAG_NONE tag做为终结,其他的参数则在这两个起始tag和终结tag之间。 tag结构一个挨一个的放在物理内存的某个地址处(在我的开发板上设置为PHYS_SDRAM_1 + 0x100)。所以kernel只要得到这个起始物理位置,就可以一个一个的解析这些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; };表一 tag结构
int do_bootm_linux(int flag, int argc, char *argv[], bootm_headers_t *images) { bd_t *bd = gd->bd; char *s; int machid = bd->bi_arch_number; void (*theKernel)(int zero, int arch, uint params); #ifdef CONFIG_CMDLINE_TAG char *commandline = getenv ("bootargs"); #endif if ((flag != 0) && (flag != BOOTM_STATE_OS_GO)) return 1; theKernel = (void (*)(int, int, uint))images->ep; s = getenv ("machid"); if (s) { machid = simple_strtoul (s, NULL, 16); printf ("Using machid 0x%x from environment\n", machid); } show_boot_progress (15); debug ("## Transferring control to Linux (at address %08lx) ...\n", (ulong) theKernel); #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); #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); #endif #ifdef CONFIG_CMDLINE_TAG setup_commandline_tag (bd, commandline); #endif #ifdef CONFIG_INITRD_TAG if (images->rd_start && images->rd_end) setup_initrd_tag (bd, images->rd_start, images->rd_end); #endif #if defined (CONFIG_VFD) || defined (CONFIG_LCD) setup_videolfb_tag ((gd_t *) gd); #endif setup_end_tag (bd); #endif /* we assume that the kernel is in place */ printf ("\nStarting kernel ...\n\n"); #ifdef CONFIG_USB_DEVICE { extern void udc_disconnect (void); udc_disconnect (); } #endif cleanup_before_linux (); theKernel (0, machid, bd->bi_boot_params); /* does not return */ return 1; }
上面代码就是uboot创建kernel参数的过程,每个参数的具体实现,请参考lib_arm/bootm.c
注意tag list由uboot构造,kernel负责解析tag list,因此uboot和kernel对tag list的结构必须有相同的定义。如果需要在uboot中增加新的tag 类型,那么kernel部分也需要增加相应类型的解析。