u-boot 之start_armboot()函数的理解

/*
** 对u-boot-1.1.6中 start_armboot()函数的理解。
** 第一阶段全部用汇编语言实现。进入函数start_armboot表示进入u-boot的第二阶段,这个阶段全部用c语言实现。
** 所以在进入第二阶段前,一定要把堆栈设置好。
** 调用过程:start.S -> start_armboot() -> ......
*/

/*
** 注解:定义一个新的函数类型,函数的输入参数类型为void,返回参数类型为Int。
*/

typedef int (init_fnc_t) (void);

int print_cpuinfo (void); /* test-only */

/*
** 注解:定义一个函数指针数组,首先这是一个数组,数组的元素是指针,指针指向的是init_fnc_t函数类型。
**       所以,归纳的话就是定义了一个函数指针数组。数组名init_sequence表示整个数组内存的开始地址,
**       init_sequence[0]表示的是数组的第一个元素cpu_init,这样init_sequence[0]()就调用了cpu_init(),
**       或者解引用的方式(*init_sequence)() ,感觉还是数组下标的方式可读性好些,但是这里用的是解引用形式。
*/

init_fnc_t *init_sequence[] = {
    cpu_init,            /* basic cpu dependent setup */
    board_init,            /* basic board dependent setup */ //directory:board/smdk2410/sdmk2410.c
    interrupt_init,        /* set up exceptions */       //cpu/arm920t/s3c24x0/interrupts.c初始化一个定时器.
    env_init,            /* initialize environment,初始化环境变量,一开始环境放在哪里?如果是nor flash启动就是在nor flash 中 */
    init_baudrate,        /* initialze baudrate settings 初始化串口波特率*/
    serial_init,        /* serial communications setup,初始化串口 */  //directory:cpu/arm920t/s3c24x0/serial.c
    console_init_f,        /* stage 1 init of console */
    display_banner,        /* say that we are here */

#if defined(CONFIG_DISPLAY_CPUINFO)
    print_cpuinfo,        /* display cpu info (and speed) */
#endif

#if defined(CONFIG_DISPLAY_BOARDINFO)
    checkboard,        /* display board info */
#endif

    dram_init,            /* configure available RAM banks,配置SDRAM的范围 */ //directory:board/smdk2410/sdmk2410.c
    display_dram_config,
    NULL,
};

void start_armboot (void)
{
    /*
    ** 定义了init_fnc_t类型的函数指针init_fnc_ptr。
    */

    init_fnc_t **init_fnc_ptr;
    char *s;

    #ifndef CFG_NO_FLASH
    ulong size;
    #endif

    #if defined(CONFIG_VFD) || defined(CONFIG_LCD)
    unsigned long addr;
    #endif

    /* Pointer is writable since we allocated a register for it */
    /*
    ** 其中_armboot_start = TEXT_BASE, 在board/smdk2410/config.mk中定义;
    ** 其中CFG_MALLOC_LEN 在include/configs/smdk2410.h中定义;
    ** 划分一块内存,内存的大小为结构体gd_t的大小,内存的开始地址根据_armboot_start,CFG_MALLOC_LEN,
    ** 和结构体gd_t的长度确定. 然后用指针gd指向这个内存块的开始位置。
    */

    gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));

    /*
    ** 阻止编译器优化,GCC >= 3.4以上版本需要这样做。
    ** memory:强制gcc编译器假设RAM所有内存单元均被汇编指令修改,
    ** 这样cpu中的registers和cache中已缓存的内存单元中的数据将作废。
    ** cpu将不得不在需要的时候重新读取内存中的数据。这就阻止了cpu将
    ** registers,cache中的数据用于去优化指令,而避免去访问内存。
    */
    /* compiler optimization barrier needed for GCC >= 3.4 */

    __asm__ __volatile__("": : :"memory");

    /*
    ** 初始化内存块清零.
    */

    memset ((void*)gd, 0, sizeof (gd_t));

    /*
    ** 划分一块内存,内存的大小为结构体bd_t的大小,内存的开始地址根据gd和结构体bd_t的长度确定.
    ** 然后用指针gd->bd指向这个内存块的开始位置。
    ** 初始化这块内存的值为0.
    */

    gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
    memset (gd->bd, 0, sizeof (bd_t));

    /*
    ** 变量monitor_flash_len表示的就是所谓monitor code 的大小。
    ** 其实就是u-boot代码relocate到SDRAM的内存大小。
    ** u-boot镜像大小。
    */

    monitor_flash_len = _bss_start - _armboot_start;

    /*
    ** init_fnc_ptr = init_sequence,把数组的开始地址赋值给指针init_fnc_ptr,这样init_fnc_ptr指向了数组的开始地址;
    ** 那么*init_fnc_ptr表示的就是数组内部的元素,最后一个元素是NULL,退出for循环;
    ** 解引用(*init_fnc_ptr)()表示的就是调用函数,返回int类型的值,如果为0表示初始化正常,否则调用函数hang()报错。
    */

    for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
        if ((*init_fnc_ptr)() != 0) {
            hang ();
        }
    }

    /*
    ** 初始化nor flash。
    */

    #ifndef CFG_NO_FLASH
    /* configure available FLASH banks */
    size = flash_init ();
    display_flash_config (size);
    #endif /* CFG_NO_FLASH */

    #ifdef CONFIG_VFD
    #    ifndef PAGE_SIZE
    #      define PAGE_SIZE 4096
    #    endif
    /*
     * reserve memory for VFD display (always full pages)
     */
    /* bss_end is defined in the board-specific linker script */

    addr = (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
    size = vfd_setmem (addr);
    gd->fb_base = addr;
    #endif /* CONFIG_VFD */

    #ifdef CONFIG_LCD
    #    ifndef PAGE_SIZE
    #      define PAGE_SIZE 4096
    #    endif
    /*
     * reserve memory for LCD display (always full pages)
     */
    /* bss_end is defined in the board-specific linker script */

    addr = (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
    size = lcd_setmem (addr);
    gd->fb_base = addr;
    #endif /* CONFIG_LCD */

    /* armboot_start is defined in the board-specific linker script */
    /*
    ** 初始化 malloc 内存块,清零。
    */

    mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);

    /*
    ** 初始化NAND flash。
    */

    #if (CONFIG_COMMANDS & CFG_CMD_NAND)
    puts ("NAND:  ");
    nand_init();        /* go init the NAND,drivers/nand/nand.c */
    #endif

    #ifdef CONFIG_HAS_DATAFLASH
    AT91F_DataflashInit();
    dataflash_print_info();
    #endif

    /* initialize environment */
    /*
    ** 把环境变量从flash读取到SDRAM。
    */

    env_relocate ();

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

    /*
    ** 这一段初始化网络设备的一些参数,比如:IP地址,MAC地址等。
    */
    /* IP Address */

    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
    }
    
    /*
    ** 一些外设的初始化,比如I2C,lcd,keyboard,等。
    ** 根据自己的需要初始化这些外设,大多数情况,u-boot中不会用到这些外设。
    ** 通过board/smdk2410/smdk2410.h中定义的宏开关这些外设。
    */

    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 */

    #if defined(CONFIG_MISC_INIT_R)
    /* miscellaneous platform dependent initialisations */
    misc_init_r ();
    #endif

    /* enable exceptions */
    /*
    ** 使能中断IRQ.
    */

    enable_interrupts ();

    /* Perform network card initialisation if necessary */
    /*
    ** 初始化网卡外设cs8900
    */

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

    #if defined(CONFIG_DRIVER_SMC91111) || defined (CONFIG_DRIVER_LAN91C96)
    if (getenv ("ethaddr")) {
        smc_set_mac_addr(gd->bd->bi_enetaddr);
    }
    #endif /* CONFIG_DRIVER_SMC91111 || CONFIG_DRIVER_LAN91C96 */

    /* Initialize from environment */
    if ((s = getenv ("loadaddr")) != NULL) {
        load_addr = simple_strtoul (s, NULL, 16);
    }
    #if (CONFIG_COMMANDS & CFG_CMD_NET)
    if ((s = getenv ("bootfile")) != NULL) {
        copy_filename (BootFile, s, sizeof (BootFile));
    }
    #endif    /* CFG_CMD_NET */

    #ifdef BOARD_LATE_INIT
    board_late_init ();
    #endif

    #if (CONFIG_COMMANDS & CFG_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 ();
    }

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

/*
** u-boot中使用的全局变量,定义在这个结构体。
*/

typedef    struct    global_data {
    bd_t            *bd;
    unsigned long    flags;          /* u-boot镜像是否重定位到SDRAM,1-是,0-否。*/
    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? 环境变量内存块校验结果,1-正常,0-异常 */
    unsigned long    fb_base;        /* base address of frame buffer,lcd 的帧缓存基地址 */

    #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;

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.机器类型ID = 193 */
    /*
    ** 参数bi_boot_params是标记列表的基地址0x30000100,标记列表是u-boot传递给kernel的参数
    ** 在board_init()函数中给这个参数赋值;
    ** 通过函数指针theKernel(0,***,***)启动内核且把参数传递给kernel.
    */

    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;

你可能感兴趣的:(u-boot)