Linux内核启动时需要一些配置信息,如根文件系统的类型、flash分区情况、串口终端的编号、内存的使用情况等等,而由于U-Boot和Linux Kernel的镜像是独立的两个文件,所以只能两者约定好在内存的什么地方存放启动参数,这样U-Boot在启动引导时就将启动参数放置在相应的地址处,而Linux Kernel则去相应的地址读取,然后加以处理。
对于Tiny210开发板,启动参数放置在DDR2 SDRAM起始地址后的0x100偏移处,U-Boot中board/samsung/goni/goni.c中有如下代码:
1 int board_init(void) 2 { 3 /* Set Initial global variables */ 4 s5pc110_gpio = (struct s5pc110_gpio *)S5PC110_GPIO_BASE; 5 6 gd->bd->bi_arch_number = MACH_TYPE_GONI; 7 gd->bd->bi_boot_params = PHYS_SDRAM_1 + 0x100; 8 9 return 0; 10 }
PHYS_SDRAM_1在include/configs/s5p_goni.h中就被定义为DDR2 SDRAM的起始地址。
而在内核中,arch/arm/mach-s5pv210/mach-mini210.c中也有相关的定义,两者的地址是一样的。
那么参数应该如何在内存中组织呢?对于内核来说,它应该知道参数是从哪开始,从哪结束,这一点的解决方法是,第一个参数和最后一个参数都约定好形式,下面有说明。
先看几个用到的数据结构。
1 struct tag_header { 2 u32 size; 3 u32 tag; 4 }; 5 6 struct tag { 7 struct tag_header hdr; 8 union { 9 struct tag_core core; 10 struct tag_mem32 mem; 11 struct tag_videotext videotext; 12 struct tag_ramdisk ramdisk; 13 struct tag_initrd initrd; 14 struct tag_serialnr serialnr; 15 struct tag_revision revision; 16 struct tag_videolfb videolfb; 17 struct tag_cmdline cmdline; 18 19 /* 20 * Acorn specific 21 */ 22 struct tag_acorn acorn; 23 24 /* 25 * DC21285 specific 26 */ 27 struct tag_memclk memclk; 28 } u; 29 }; 30 31 struct tag *params;
在启动内核的函数中,全局变量被初始化为:
params = ( struct tag *)bd->bi_boot_params;
对U-Boot来说,只要牵住params这个变量,不断向后面写参数就行了,下面简单分析几个参数的填写过程。
第一个参数必须是TAG_CORE
1 设置ATAG_CORE的伪代码 2 params->hdr.tag = ATAG_CORE; 3 params->hdr.size = ( sizeof ( struct tag_header ) + sizeof( struct tag_core ) ) >> 2; 4 params->u.core.flags = 0; 5 params->u.core.pagesize = 0; 6 params->u.core.rootdev = 0; 7 params = params + params->hdr.size;
重要的参数CMDLING_TAG
1 设置ATAG_CMDLINE的伪代码 2 params->hdr.tag = ATAG_CMDLINE; 3 params->hdr.size = ( sizeof( struct tag_header ) + sizeof( struct tag_cmdline ) ) >> 2; 4 p = getenv( "bootargs" ); 5 check_bootargs_not_null( p ) && bootargs_valid( p ); 6 strcpy( params->u.cmdline.cmdline, p ); 7 params = params + params->hdr.size;
重要参数CONFIG_SETUP_MEMORY_TAGS
1 for( i = 0; i < CONFIG_NR_DRAM_BANKS; i++ ) //tiny210中此宏为1 2 { 3 params->hdr.tag = ATAG.MEM; 4 params->hdr.size = ( sizeof( struct tag_header ) + sizeof( struct tag_mem32 ) ) >> 2; 5 params->u.mem.start = bd->bi_dram.start; 6 params->u.mem.size = bd->bi_dram.size; 7 params = params + params->hdr.size; 8 9 }
可选参数CONFIG_REVISION_TAG
1 params->hdr.tag = ATAG_REVISION; 2 params->hdr.size = ( sizeof( struct tag_header ) + sizeof( struct tag_revision ) ) >> 2; 3 revision = board_get_revision(); 4 params->u.revision.rev = revision; 5 params = params + params->hdr.size;
可选参数SERIAL_TAG
1 params->hdr.tag = ATAG_SERIAL; 2 params->hdr.size = ( sizeof( struct tag_header ) + sizeof( struct tag_serial ) ) >> 2; 3 serialnr = board_get_serialnr(); 4 params->u.serialnr.low = serialnr.low; 5 params->u.serialnr.high = serialnr.high 6 params = params + params->hdr.size;
INITRD_TAG,所谓initrd 就是init ramdisk
1 if( images->rd_start && images->rd_end ) 2 { 3 setup_initrd_tag( gd->bd, images->rd_start, images->rd_end ); 4 虽然s5p_goni.h中使用了宏定义,但是没有使用。 5 params->hdr.tag = ATAG_INITRD2; ATAG_INITRD是压缩的ramdisk image所在 的虚拟地址,ATAG_INITRD2是相应的物理地址。 6 params->hdr.size = ( sizeof( struct tag_header ) + sizeof( struct tag_initrd ) ) >> 2; 7 params->u.initrd.start = initrd_start; 8 params->u.initrd.size = initrd_end - initrd_start; 9 params += params->hdr.size; 10 }
对应于开始的ATAG_CORE,最后一个也需要固定格式的tag:
1 params->hdr.tag = ATAG_NONE; 2 params->hdr.size = 0;