您的位置:首页 > 其它

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

2012-12-11 13:15 423 查看
####################

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

####################

// 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 // use 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 */
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: