U-Boot启动过程源码分析(2)-第二阶段

先总述:第一阶段cpu/arm920t/start.S和board/smdk2410/lowlevel_init.S进行初始化,再跳到第二阶段的入口点lib_arm/board.c中的start_armboot函数。
第二阶段start_armboot函数需要用到include/asm-arm/global_data.h中结构体gd_t; include/asm-arm.u/u-boot.h中结构体bd_t。gd_t,bd_t结构体中的信息来设置标记列表,U-Boot启动内核时给内核传递参数。逐个调用init_sequence数组中的初始化函数。最后执行 common/main.c中的 main_loop函数
U-Boot启动第二阶段代码分析
这里写图片描述
重要数据结构
(1)gd_t结构体
U-Boot使用了一个结构体gd_t来存储全局数据区的数据,这个结构体在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      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来记录全局数据区的地址:

define DECLARE_GLOBAL_DATA_PTR (register volatile gd_t *)gd asm (“r8”)
DECLARE_GLOBAL_DATA_PTR定义一个gd_t全局数据结构的指针,这个指针存放在指定的寄存器r8中。这个声明也避免编译器把r8分配给其它的变量。任何想要访问全局数据区的代码,只要代码开头加入“DECLARE_GLOBAL_DATA_PTR”一行代码,然后就可以使用gd指针来访问全局数据区了。
根据U-Boot内存使用图中可以计算gd的值:
gd = TEXT_BASE -CONFIG_SYS_MALLOC_LEN - sizeof(gd_t)

(2)bd_t结构体
bd_t在include/asm-arm.u/u-boot.h中定义如下:

typedef struct bd_info {
    int bi_baudrate;               /* 串口通讯波特率 */
    unsigned long   bi_ip_addr;          /* IP 地址*/
    struct environment_s   *bi_env;     /* 环境变量开始地址 */
    ulong            bi_arch_number;      /* 开发板的机器码 */
    ulong            bi_boot_params;       /* 内核参数的开始地址 */
    struct                         /* RAM配置信息 */
    {
              ulong start;
              ulong size;
    }bi_dram[CONFIG_NR_DRAM_BANKS];  
} bd_t;

U-Boot启动内核时要给内核传递参数,这时就要使用gd_t,bd_t结构体中的信息来设置标记列表。第一阶段调用start_armboot指向C语言执行代码区,首先它要从内存上的重定位数据获得不完全配置的全局数据表格和板级信息表格,即获得gd_t和bd_t,这两个类型变量记录了刚启动时的信息,并将要记录作为引导内核和文件系统的参数,如bootargs等等,并且将来还会在启动内核时,由uboot交由kernel时会有所用。

(3)init_sequence数组
U-Boot使用一个数组init_sequence来存储对于大多数开发板都要执行的初始化函数的函数指针。init_sequence数组中有较多的编译选项,去掉编译选项后init_sequence数组如下所示:

init_fnc_t *init_sequence[] = {
       board_init,         /*开发板相关的配置--board/samsung/mini2440/mini2440.c */
       timer_init,            /* 时钟初始化-- cpu/arm920t/s3c24x0/timer.c */
       env_init,            /*初始化环境变量--common/env_flash.c 或common/env_nand.c*/
       init_baudrate,      /*初始化波特率-- lib_arm/board.c */
       serial_init,            /* 串口初始化-- drivers/serial/serial_s3c24x0.c */
       console_init_f,    /* 控制通讯台初始化阶段1-- common/console.c */
       display_banner,   /*打印U-Boot版本、编译的时间-- gedit lib_arm/board.c */
       dram_init,            /*配置可用的RAM-- board/samsung/mini2440/mini2440.c */
       display_dram_config,              /* 显示RAM大小-- lib_arm/board.c */
       NULL,
};

其中的board_init函数在board/smdk2410/smdk2410.c中定义,该函数设置了MPLLCOM,UPLLCON,以及一些GPIO寄存器的值,还设置了U-Boot机器码和内核启动参数地址 :

/* smdk2410开发板的机器码 */

gd->bd->bi_arch_number = MACH_TYPE_SMDK2410;

/* 内核启动参数地址 */

gd->bd->bi_boot_params = 0x30000100;

dram_init函数在board/smdk2410/smdk2410.c中定义int
检测系统内存映射,特定的开发板其内存映射是明确的,这些参数在后面传递给内核。

dram_init (void)

{
      gd->bd->bi_dram[0].start = PHYS_SDRAM_1;
      gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE;
      return 0;

}

分析完上述结构,下析start_armboot函数:

void start_armboot (void)
{
       /* 计算全局数据结构的地址gd */
       gd = (gd_t*)(_armboot_start - CONFIG_SYS_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));
       gd->flags |= GD_FLG_RELOC;
       monitor_flash_len = _bss_start - _armboot_start;
/* 逐个调用init_sequence数组中的初始化函数  */
       for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
              if ((*init_fnc_ptr)() != 0) {
                     hang ();
              }
       }
/* armboot_start 在cpu/arm920t/start.S 中被初始化为u-boot.lds连接脚本中的_start */
       mem_malloc_init (_armboot_start - CONFIG_SYS_MALLOC_LEN,
                     CONFIG_SYS_MALLOC_LEN);
/* NOR Flash初始化 */
#ifndef CONFIG_SYS_NO_FLASH
       /* configure available FLASH banks */
       display_flash_config (flash_init ());
#endif /* CONFIG_SYS_NO_FLASH */

       … …
/* NAND Flash 初始化*/
#if defined(CONFIG_CMD_NAND)
       puts ("NAND:  ");
       nand_init();         /* go init the NAND */
#endif
       … …
       /*配置环境变量,重新定位 */
       env_relocate ();
       … …
       /* 从环境变量中获取IP地址 */
       gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");
       stdio_init (); /* get the devices list going. */
       jumptable_init ();
       … …
       console_init_r (); /* fully init console as a device */
       … …
       /* enable exceptions */
       enable_interrupts ();

#ifdef CONFIG_USB_DEVICE
       usb_init_slave();
#endif

       /* Initialize from environment */
       if ((s = getenv ("loadaddr")) != NULL) {
              load_addr = simple_strtoul (s, NULL, 16);
       }
#if defined(CONFIG_CMD_NET)
       if ((s = getenv ("bootfile")) != NULL) {
              copy_filename (BootFile, s, sizeof (BootFile));
       }
#endif
       … …
       /* 网卡初始化 */
#if defined(CONFIG_CMD_NET)
#if defined(CONFIG_NET_MULTI)
       puts ("Net:   ");
#endif
       eth_initialize(gd->bd);
… …
#endif

       /* main_loop() can return to retry autoboot, if so just run it again. */
       for (;;) {
              main_loop ();//main_loop!!
       }
       /* NOTREACHED - no way out of command loop except booting */
}

main_loop函数在common/main.c中定义。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值