U-Boot启动引导内核分析(一)

标签:ARM   bootloader   U-Boot   代码分析 抵岸科技

U-Boot启动流程

大多数bootloader都分为stage1stage2两大部分,u-boot也不例外。依赖于CPU体系结构的代码(如设备初始化代码等) 通常都放在stage1,且可以用汇编语言来实现,而stage2则通常用C语言来实现,这样可以实现复杂的功能,而且有更好的可读性和移植性。

u-boot启动大致流程如图1所示U-Boot启动引导内核分析(一)_第1张图片

                                                                                        图 1

> Stage1

在flash中执行的引导代码,也就是bootloader中的stage1,负责初始化硬件环境,把u-boot从flash加载到RAM中去,然后跳到lib_arm/board.c中的start_armboot中去执行。

u-bootstage1代码通常放在start.s文件中,它用汇编语言写成,其主要代码部分如下:

1)  定义入口由于一个可执行的Image必须有一个入口点,并且只能有一个全局入口,通常这个入口放在ROM(Flash) 0x0地址,因此,必须通知编译器以使其知道这个入口,该工作可通过修改连接器脚本来完成。

2)  设置异常向量(Exception Vector)

3)  设置CPU的速度、时钟频率及中断控制寄存器。

4)  初始化内存控制器

5)  ROM中的程序复制到RAM中。

6)  初始化堆栈

7)  转到RAM中执行,该工作可使用指令ldr pc, _start_armboot来完成。

> Stage2

lib_arm/board.c中的start_armboot是C语言开始的函数,也是整个启动代码中C语言的主函数,同时还是整个u-boot(armboot) 的主函数,该函数主要流程分析如下:

void start_armboot (void)
{
    init_fnc_t **init_fnc_ptr;
    char *s;
#if !defined(CFG_NO_FLASH) || defined (CONFIG_VFD) || defined(CONFIG_LCD)
    ulong size;
#endif
#if defined(CONFIG_VFD) || defined(CONFIG_LCD)
    unsigned long addr;
#endif

    /* Pointer is writable since we allocated a register for it */
    /* 给全局数据变量gd安排空间 */
    gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));
   
    /* compiler optimization barrier needed for GCC >= 3.4 */
    __asm__ __volatile__("": : :"memory");

    /* 给板子数据变量gd->bd安排空间 */
    memset ((void*)gd, 0, sizeof (gd_t));
    gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
    memset (gd->bd, 0, sizeof (bd_t));

    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 ();
        }
    }
    /*初始化函数列表:
    init_fnc_t *init_sequence[] = {
    cpu_init,                 /* basic cpu dependent setup */
#if defined(CONFIG_SKIP_RELOCATE_UBOOT)
    reloc_init,            /* Set the relocation done flag, must
                           do this AFTER cpu_init(), but as soon
                           as possible */
#endif
    board_init,            /* basic board dependent setup */
    interrupt_init,        /* set up exceptions */
    env_init,              /* initialize environment */
    init_baudrate,         /* initialze baudrate settings */
    serial_init,           /* serial communications setup */
    console_init_f,        /* stage 1 init of console */
    display_banner,        /* say that we are here */
#if defined(CONFIG_HW_WATCHDOG)
    hw_watchdog_init,      /* watchdog setup */
#endif
#if defined(CONFIG_DISPLAY_CPUINFO)
    print_cpuinfo,         /* display cpu info (and speed) */
#endif
#if defined(CONFIG_DISPLAY_BOARDINFO)
    checkboard,            /* display board info */
#endif
#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C)
    init_func_i2c,
#endif
    dram_init,                /* configure available RAM banks */
    display_dram_config,
    NULL,
};
    */

    /* armboot_start is defined in the board-specific linker script */
    mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);

#if defined(CONFIG_CMD_NAND)
    puts ("NAND: ");
    /* NAND FLASH初始化 */
    nand_init();        /* go init the NAND */
#endif

    /* 重新定位环境变量 */
    env_relocate ();

#ifdef CONFIG_VFD
    /* must do this after the framebuffer is allocated */
    drv_vfd_init();
#endif /* CONFIG_VFD */

#ifdef CONFIG_SERIAL_MULTI
    /* 串口初始化 */
    serial_initialize();
#endif

    /* 从环境变量中获取IP地址和MAC地址 */
    gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");
    /* MAC Address */
    {
        int i;
        ulong reg;
        char *s, *e;
        char tmp[64];

        i = getenv_r ("ethaddr", tmp, sizeof (tmp));
        s = (i > 0) ? tmp : NULL;

        for (reg = 0; reg < 6; ++reg) {
            gd->bd->bi_enetaddr[reg] = s ? simple_strtoul (s, &e, 16) : 0;
            if (s)
                s = (*e) ? e + 1 : e;
        }

#ifdef CONFIG_HAS_ETH1
        i = getenv_r ("eth1addr", tmp, sizeof (tmp));
        s = (i > 0) ? tmp : NULL;

        for (reg = 0; reg < 6; ++reg) {
            gd->bd->bi_enet1addr[reg] = s ? simple_strtoul (s, &e, 16) : 0;
            if (s)
                s = (*e) ? e + 1 : e;
        }
#endif
    }

    devices_init ();    /* get the devices list going. */

#ifdef CONFIG_CMC_PU2
    load_sernum_ethaddr ();
#endif /* CONFIG_CMC_PU2 */
   
    /* 跳转表的初始化*/
    jumptable_init ();

    /* 控制台的初始化 */
    console_init_r ();    /* fully init console as a device */

    /* IRQ中断使能 */
    enable_interrupts ();

    /* 各种型号网络设备的初始化 */
#ifdef CONFIG_DRIVER_TI_EMAC
extern void dm644x_eth_set_mac_addr (const u_int8_t *addr);
    if (getenv ("ethaddr")) {
        dm644x_eth_set_mac_addr(gd->bd->bi_enetaddr);
    }
#endif

#ifdef CONFIG_DRIVER_CS8900
    cs8900_get_enetaddr (gd->bd->bi_enetaddr);
#endif

    /* 通过环境变量初始化load_addr
默认定义ulong load_addr = CFG_LOAD_ADDR; */
    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

#ifdef BOARD_LATE_INIT
    board_late_init ();
#endif
#if defined(CONFIG_CMD_NET)
#if defined(CONFIG_NET_MULTI)
    puts ("Net:   ");
#endif
    eth_initialize(gd->bd);
#if defined(CONFIG_RESET_PHY_R)
    debug ("Reset Ethernet PHY/n");
    reset_phy();
#endif
#endif
    /* 循环不断地执行main_loop ()函数
main_loop ()主要处理用户命令 */
    for (;;) {
        main_loop ();
    }
}

整个u-boot的执行就进入等待用户输入命令,解析并执行命令的死循环中。

你可能感兴趣的:(Flash,null,语言,代码分析,optimization,linker)