大家都知道U-Boot启动的时候会将启动参数的地址放入R2中,然后再启动内核。我们看看这些参数是如何设置的。
首先看两个重要的数据结构:
第一个是global_data,定义在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 */
- #ifdef CONFIG_VFD
- unsigned char vfd_type ; /* display type */
- #endif
- #if 0
- unsigned long cpu_clk ; /* CPU clock in Hz! */
- unsigned long bus_clk ;
- unsigned long ram_size ; /* RAM size */
- unsigned long reset_status ; /* reset status register at boot */
- #endif
- void ** jt ; /* jump table */
- } gd_t ;
在同一个文件中有如下定义:
- #define DECLARE_GLOBAL_DATA_PTR register volatile gd_t * gd asm ( " r8 " )
在需要使用gd指针的时候,只需要加入DECLARE_GLOBAL_DATA_PTR这句话就可以了。可以知道,gd指针始终是放在r8中的。
其中的第一个变量,bd_t结构体,定义于include/asm-arm/u-boot.h中:
- typedef struct bd_info {
- int bi_baudrate ; /* serial console baudrate */
- unsigned long bi_ip_addr ; /* IP Address */
- unsigned char bi_enetaddr [ 6 ] ; /* Ethernet adress */
- struct environment_s * bi_env ;
- ulong bi_arch_number ; /* unique id for this board */
- ulong bi_boot_params ; /* where this board expects params */
- struct /* RAM configuration */
- {
- ulong start ;
- ulong size ;
- } bi_dram [ CONFIG_NR_DRAM_BANKS ] ;
- #ifdef CONFIG_HAS_ETH1
- /* second onboard ethernet port */
- unsigned char bi_enet1addr [ 6 ] ;
- #endif
- } bd_t ;
bd_t中的变量bi_boot_params,表示传递给内核的参数的位置。
然后看看gd和bd的初始化,在lib_arm/board.c中:
- gd = ( gd_t * )( _armboot_start - CFG_MALLOC_LEN - sizeof ( gd_t )) ;
- memset (( void * ) gd , 0 , sizeof ( gd_t )) ;
- gd -> bd = ( bd_t * )(( char * ) gd - sizeof ( bd_t )) ;
- memset ( gd -> bd , 0 , sizeof ( bd_t )) ;
说明这两个结构体在内存中的位置是在 uboot 的代码在往下的地址处,所以进行操作的时候不要覆盖了这个位置!
在board/up270/up270.c中,有如下初始化:
- gd -> bd -> bi_boot_params = 0 xa0000100 ;
说明参数位置在0xa0000100。
现在,具体看看uboot 是如何(按什么格式)把参数放入内存中。
在lib_arm/armlinux.c的do_bootm_linux函数中:
- bd_t * bd = gd -> bd ;
- setup_start_tag ( bd ) ;
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 ) ;
- }
其中用到了一个重要的指针:params,这是一个指向struct tag的指针,在文件的开始处声明,可以被这个文件中的所有函数访问:
- static struct tag * params ;
struct tag的定义位于include/asm-arm/setup.h中:
- 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 ;
- } ;
包括teg_header和tag的内容。tag_header定义在同一个文件中,如下:
- struct tag_header {
- u32 size ;
- u32 tag ;
- } ;
tag和tag_header和内核中的结构一模一样。tag_header中的tag字段表示的是这个tag的类型,在内核和Bootloader中通过一些固定的整形常量来表示:
- #define ATAG_CORE 0x54410001
- #define ATAG_NONE 0x00000000
- #define ATAG_CORE 0x54410001
- #define ATAG_MEM 0x54410002
- #define ATAG_VIDEOTEXT 0x54410003
- #define ATAG_RAMDISK 0x54410004
- #define ATAG_INITRD 0x54410005
- #define ATAG_INITRD2 0x54420005
- #define ATAG_SERIAL 0x54410006
- #define ATAG_REVISION 0x54410007
- #define ATAG_VIDEOLFB 0x54410008
- #define ATAG_CMDLINE 0x54410009
- #define ATAG_ACORN 0x41000101
- #define ATAG_MEMCLK 0x41000402
set_xxx_tags函数给不同的参数赋值,和set_start_tag类似,就不多说了。需要注意的一个是tag_next。这是一个宏:
- #define tag_next ( t ) (( struct tag * )(( u32 * )( t ) + ( t ) -> hdr . size ))
作用就是让param跳过刚刚设置好的tag,指向下一个tag开始的地方。所以在每个set_xxx_tag函数的最好都调用这个宏。
具体每个tag的定义可以查看相关文件。
这样,就把每个tag放到了从内存0xa0000100开始的地址,然后由内核根据tag_header的tag字段来识别到底是什么参数,然后再解析出来使用。