U-Boot系列之四: start_armboot()函数分析

来源:互联网 发布:java程序设计考试题库 编辑:程序博客网 时间:2024/06/08 01:22

在上一篇文章中,我们介绍了u-boot启动的时候汇编语言的部分,当时我们进行了一些简单的初始化,并且为C语言的执行建立的环境(堆栈),现在我们看看当从汇编语言转到C语言的时候执行的第一个函数( start_armboot (),在lib_arm/board.c中),该函数进行了一系列的外设初始化,然后调用main_loop (),根据配置来选择是直接加载Linux内核还是进入等待命令模式。

    在介绍该函数之前,我们需要看一看几个数据结构,这些是u-boot中几个重要的数据结构:

1)、gd_t该数据结构保存了u-boot需要的配置信息,注释简单明了
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? */
#ifdef CONFIG_VFD  //我们一般没有配置这个,这个是frame buffer的首地址
    unsigned long    fb_base;    /* base address of frame buffer */
#endif
} gd_t;

2)、bd_t 保存与板子相关的配置参数
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];//在我的板子上是1个
} bd_t;

现在我贴出start_armboot ()的源代码,然后具体的在其中解释一些代码的作用:


void start_armboot (void)
{
    DECLARE_GLOBAL_DATA_PTR;

    ulong size;
    gd_t gd_data;
    bd_t bd_data;
    init_fnc_t **init_fnc_ptr; //这个是函数的指针,指向一些硬件初始化的函数,见下面
   
                    //init_fnc_t *init_sequence[] = {
                        //    cpu_init,        /* basic cpu dependent setup */
                        //    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 */
                       //     display_banner,
                       //     dram_init,        /* configure available RAM banks */
                       //     display_dram_config,
                       //     NULL,
                       //     };

    //printf("**********Start *************/n");
    /* Pointer is writable since we allocated a register for it */
    gd = &gd_data;
    memset (gd, 0, sizeof (gd_t));//初始化为0
    gd->bd = &bd_data;
    memset (gd->bd, 0, sizeof (bd_t));//初始化为0

//注意,下面的循环是依次调用各个硬件初始化函数,顺序见上面的的数组,我们在下一篇中依次解释每种硬//件的初始化,第六个就是串口的初始化,这时候我们就可以通过串口输出信息了
    for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
        if ((*init_fnc_ptr)() != 0) {
            hang ();      //如果不成功的话,就“输出信息,然后就进入死循环”
        }
    }

    /* configure available FLASH banks */
    size = flash_init ();  //flash的初始化
    display_flash_config (size);//打印flash的配置信息

    /* initialize environment */
    env_relocate ();//环境的初始化,代码在common/env_common.c中

    /* IP Address */
    bd_data.bi_ip_addr = getenv_IPaddr ("ipaddr");//读取IP地址,保存到bd_t数据结构中

    /* MAC Address */ //MAC地址的初始化
    {
        int i;
        ulong reg;
        char *s, *e;
        uchar tmp[64];

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

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


    /* enable exceptions */  //允许中断
    enable_interrupts ();

#ifdef CONFIG_DRIVER_CS8900  //配置网卡
    if (!getenv ("ethaddr")) {
        cs8900_get_enetaddr (gd->bd->bi_enetaddr);
    } 
#endif

//直接进入main_loop 该函数在common/main.c中,至此,硬件初始化完成
    /* main_loop() can return to retry autoboot, if so just run it again. */
    for (;;) {
        main_loop ()
    }

    /* NOTREACHED - no way out of command loop except booting */
}

呵呵,好像很简单哦,其实我们并没有深入下去,只是在水面上来高屋建瓴,目的就是让大家有一个整体的映像,然后再下一篇,再根据start_armboot()中的初始化步骤来一个一个解释每种硬件的初始化。依次是cpu_init、board_init、interrupt_init、env_init、init_baudrate、serial_init、dram_init、flash_init等。

 

这里主要想说明一点的就是u-boot在内存中的分布情况,因为我们知道在汇编代码中,我们就已经将u-boot的主要代码段armboot以及bss段,还有我们分配的堆栈段,还有一些数据结构存放的空间,而在这里我们在刚开始初始化gd_t和bd_t结构时还需要在内存中来分配空间,所以理解u-boot在内存中分配是很重要的。

 

我们从汇编代码开始分析:

        ldr     r2, _armboot_start
        ldr     r3, _bss_start
        sub     r2, r3, r2              /* r2 <- size of armboot            */
        add     r2, r0, r2

这一段代码表明我们将u-boot代码重定位到RAM中指定的_TEXT_BASE处,而在_TEXT_BASE地址的上面比邻就是bss段。

 

在分配堆栈地址时:

        ldr     r0, _TEXT_BASE          /* upper 128 KiB: relocated uboot   */
        sub     r0, r0, #CFG_MALLOC_LEN /* malloc area                      */
        sub     r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo

表明在_TEXT_BASE比邻的下面就是分配的malloc分配区域,从_TEXT_BASE开始向地址低的方向分配,然后紧接着分配了一个结构体变量的空间也就是存储板子配置信息的bd_t.

 

#ifdef CONFIG_USE_IRQ
        sub     r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
        sub     sp, r0, #12             /* leave 3 words for abort-stack    */

这段代码表示如果定义了中断,则紧接着往地址低的方向分配用来存放中断向量的空间,有IRQ和FIQ每个占4KB.然后再分配处3个字来作为abort-stack

 

我们接着来看看在start_armboot函数开始对gd_t和bd_t两个结构体的内存分配和初始化:

 

比如gd_t结构的初始化:

251         gd = (gd_t*)(_armboot_start – CFG_MALLOC_LEN – sizeof(gd_t));

_armboot_startu-bootRAM中的开始地址(对于u-boot最终搬移到RAM中运行的情况),CFG_MALLOC_LENinclude/configs/<board name>.h中定义。

 

bd_t结构的初始化:

272         gd->bd = (bd_t*)((char*)gd-sizeof(bd_t));

u-bootbd_t结构紧接着gd_t结构存放。

 

内存分配的初始化

316         mem_malloc_init(_armboot_start-CFG_MALLOC_LEN);

经过以上的初始化后,u-boot在内存中的布局为(在底端为低地址)

......

-----------------------------  _bss_end

BSS

----------------------------- _bss_start

U-BOOT TEXT/DATA  u-boot映像

-----------------------------

CFG_MALLOC_LEN

-----------------------------

gd_t

-----------------------------

bd_t

-----------------------------

IRQ&FIQ栈区

----------------------------- 用户栈顶sp

用户栈区

原创粉丝点击