u-boot分析。 关于start_armboot()函数的具体实现

####################
本文由极度寒冰原创,转载请注明出处。
####################

// start_armboot()函数主要初始化ARM系统的硬件和环境变量,包括FLASH存储器,FrameBuffer,网卡等,最后进入U-Boot应用程序主循环。

// 上一篇文章中,我们可以看到的是start_armboot是U-Boot启动过程中经历的第一个C语言的函数。   函数所在的文件为:    lib_arm/board.c


typedef int (init_fnc_t) (void);    // 首先在这里定义了一个新的数据类型init_fnc_t, 这个数据类型的返回值为int,无参数。

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

// init_sequence是一个指针数组,指向的是init_fnc_t类型的函数。  里面的每一个元素都是一个指针,指向的都是init_fnc_t类型的函数。。
init_fnc_t *init_sequence[] = {
        cpu_init,               /* basic cpu dependent setup */   // cpu架构方面的相关函数。
        board_init,             /* basic board dependent setup */    // 板极的初始化函数。  所在的位置为board下面的各个文件夹中。
        interrupt_init,         /* set up exceptions */       // 初始化中断。   所在文件的位置为./cpu/下面不同的处理器的interrupts.c中,是否所有的处理器都支持初始化中断,这点尚未研究。
        env_init,               /* initialize environment */     //  在./common/env_nvram.c中进行的实现。
        init_baudrate,          /* initialze baudrate settings */    // 初始化波特率的设置。
        serial_init,            /* serial communications setup */    // 串口通信的设置。一般都位于各个cpu,board的serial.c中。
        console_init_f,         /* stage 1 init of console */     // 控制台前期的初始化。具体实现位于 common/console.c 中
        display_banner,         /* say that we are here */     // 主要用来显示uboot的版本信息和编译时间。
#if defined(CONFIG_DISPLAY_CPUINFO)
        print_cpuinfo,          /* display cpu info (and speed) */    //cat /proc/cpu_info可以显示cpu的信息。
#endif
#if defined(CONFIG_DISPLAY_BOARDINFO)
        checkboard,             /* display board info */     // 显示板子的信息。  是否所有的板子都支持这个选项,现在还没有一一对比。
#endif
        dram_init,              /* configure available RAM banks */    // 这个函数是检查内存映射,即确定了板子上面使用了多少内存,它们的地址是什么。
        display_dram_config,     // 打印出DRAM的大小。
        NULL,
};

void start_armboot (void)
{
init_fnc_t **init_fnc_ptr;    // init_fnc_ptr是一个指针,这个指针指向了一个指针数组,指针数组里面每一个元素都是一个指针,指向的是init_fnc_t类型的函数。。。

char *s;
#ifndef CFG_NO_FLASH     // Flash is usable now or not
ulong size;
#endif
#if defined(CONFIG_VFD) || defined(CONFIG_LCD)      // send framebuffer setup or not || use LCD controller or not ...
unsigned long addr;
#endif

/* Pointer is writable since we allocated a register for it */
gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));   // 把gd作为全局数据结构体的指针,并给它安排空间。
/* compiler optimization barrier needed for GCC >= 3.4 */
__asm__ __volatile__("": : :"memory");    //__asm__ 用于告诉编译器,在这个地方插入一段汇编语言。  __volatile__用于告诉编译器,严禁将此处的汇编语言与其他的语句重优化组合。即:原原本本按照原来的样子来处理这一段汇编。memory强制gcc编译器假设RAM所有内存单元均被汇编指令修改,这样cpu中的register和cache中已经被缓存的内存单元中的数据将被作废。cpu将不得不在需要的时候重新读取内存中的数据。这样就阻止了cpu又将registers,cache中的数据去优化指令,而避免去访问内存。   ""::: 表示这个是个空指令。 

memset ((void*)gd, 0, sizeof (gd_t));         // 将gd给清零   数组的初始化一样
gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));    // 给板子的数据变量gd->bd安排空间。
memset (gd->bd, 0, sizeof (bd_t));       // 数组的初始化。

monitor_flash_len = _bss_start - _armboot_start;    // 计算u-boot的长度。

         // 顺序执行init_sequence数组中的初始化函数
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if ((*init_fnc_ptr)() != 0) {
hang ();
}
}

#ifndef CFG_NO_FLASH
/* configure available FLASH banks */
size = flash_init ();     // 初始化Flash存储器配置
display_flash_config (size);    //  显示Flash存储器配置
#endif /* CFG_NO_FLASH */

#ifdef CONFIG_VFD    // do not send framebuffer setup
# 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);    // 计算Framebuffer内存地址
size = vfd_setmem (addr);    // 设置Framebuffer占用内存大小
gd->fb_base = addr;    // 设置Framebuffer内存起始地址
#endif /* CONFIG_VFD */

#ifdef CONFIG_LCD   // us e LCD controller or not ...
# 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);   // 计算FrameBuffer内存地址。
size = lcd_setmem (addr);   // 设置FrameBuffer大小。
gd->fb_base = addr;    // 设置FrameBuffer内存起始地址。
#endif /* CONFIG_LCD */

/* armboot_start is defined in the board-specific linker script */
mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);   //初始化堆空间

#if (CONFIG_COMMANDS & CFG_CMD_NAND)
puts ("NAND:  ");
nand_init(); /* go init the NAND */    // 初始化NAND Flash存储器
#endif

#ifdef CONFIG_HAS_DATAFLASH
AT91F_DataflashInit();           //  初始化hash表
dataflash_print_info();
#endif

/* initialize environment */
env_relocate ();             //  重新设置环境变量

#ifdef CONFIG_VFD
/* must do this after the framebuffer is allocated */
drv_vfd_init();      // 初始化虚拟显示设备   什么是虚拟显示设备?最大的特点是在微小的体积内产生高品质的画面。最大用处是,在于可以为便携电子设备的用户提供优质的图像显示。
#endif /* CONFIG_VFD */

/* IP Address */
gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");      // 设置网卡的IP地址,  从环境变量中获得IP地址。

/* MAC Address */
{
int i;
ulong reg;
char *s, *e;
char tmp[64];

i = getenv_r ("ethaddr", tmp, sizeof (tmp));      // 从网卡寄存器中读取MAC地址。
s = (i > 0) ? tmp : NULL;   // 将mac地址保存到tmp中

for (reg = 0; reg < 6; ++reg) {
gd->bd->bi_enetaddr[reg] = s ? simple_strtoul (s, &e, 16) : 0;      // 为什么会小于6?这个是从哪儿来的?我认为是与tmp[64]对应的。2的6次方,这样的话方便对每一位进行操作。
if (s)
s = (*e) ? e + 1 : e;
}

#ifdef CONFIG_HAS_ETH1       // add support for "eth1addr"
i = getenv_r ("eth1addr", tmp, sizeof (tmp));     //  读取hash值
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 ();      // 初始化跳转表,主要是保存了一些系统函数的指针。便于以后的引用~  函数的具体实现在./common/exports.c

console_init_r (); /* fully init console as a device */    // 在这个地方,对控制台进行了全面的初始化。函数的具体实现在./common/console.c

#if defined(CONFIG_MISC_INIT_R)
/* miscellaneous platform dependent initialisations */
misc_init_r ();    // 初始化其他设备。
#endif

/* enable exceptions */
enable_interrupts ();      // 打开中断。

/* Perform network card initialisation if necessary */
#ifdef CONFIG_DRIVER_CS8900
cs8900_get_enetaddr (gd->bd->bi_enetaddr);     // 获取CS8900网卡的MAC地址
#endif

#if defined(CONFIG_DRIVER_SMC91111) || defined (CONFIG_DRIVER_LAN91C96)
if (getenv ("ethaddr")) {
smc_set_mac_addr(gd->bd->bi_enetaddr);     // 设置SMC网卡的MAC地址
}
#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));    // 保存FrameBuffer
}
#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分析。 关于start_armboot()函数的具体实现)