转载请注明原地址:http://blog.csdn.net/oyhb_1992/article/category/7082238
Uboot 启动流程分析:启动阶段2 board.c
由启动阶段一,ldr pc, _start_armboot这条语句绝对跳转到内存中继续执行_start_armboot这个函数,开始阶段2的代码。
首先要介绍几个结构体
1.gd_t全局数据结构,主要保存了uboot的配置信息。include/asm-arm/global_data.h中定义:
typedef struct global_data { bd_t *bd;//板子相关的结构体 unsigned long flags; //是否跳转到内存中执行代码
unsigned long baudrate;//波特率 unsigned long have_console; /* 串口调用*/ unsigned long reloc_off; /* Relocation Offset 重定位偏移*/ unsigned long env_addr; /* 环境变量基址*/ unsigned long env_valid; /* 环境变量是否有效 */ unsigned long fb_base; /*LCD帧缓冲基地址*/ #ifdef CONFIG_VFD unsigned char vfd_type; /* display type */ #endif #if 0 //这段代码屏蔽掉 unsigned long cpu_clk; /* cpu频率*/ unsigned long bus_clk;/*总线频率*/ phys_size_t ram_size; /* RAM大小设置,64M*/ unsigned long reset_status; /* 重新设置状态地址*/ #endif void **jt; /* jump table 保存着些函数的入口地址,在 common/Exports.c中进行填充*/ } gd_t;
2.bd_t板子数据结构体,主要保存与板子相关的信息。定义在include/asm-arm/u-boot.h中
typedef struct bd_info { int bi_baudrate; /* 串口频率*/ unsigned long bi_ip_addr; /*IP地址*/ structenvironment_s *bi_env;环境变量指针 ulong bi_arch_number; /* 板子ID*/ ulong bi_boot_params; /* 启动参数存放地址*/ struct /* RAM configuration */ {
ulong start;
ulong size; } bi_dram[CONFIG_NR_DRAM_BANKS];//SDRAM的大小,此处的CONFIG_NR_DRAM_BANKS在smdk2410.h中宏定义 } bd_t;
3.环境变量结构体
#define ENV_SIZE (CONFIG_ENV_SIZE - ENV_HEADER_SIZE)
typedef structenvironment_s {
uint32_t crc; /*CRC32 over data bytes */
#ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT
unsigned char flags; /*active/obsolete flags */
#endif
unsigned char data[ENV_SIZE]; /* Environment data */
} env_t;
4.初始化函数队列。定义在lib_arm/Board.c
typedef int (init_fnc_t) (void);定义函数指针;
下面是函数指针数组
init_fnc_t *init_sequence[] = { cpu_init, /* CPU初始化,定义在cpu/arm920t/cpu.c*/ #if defined(CONFIG_ARCH_CPU_INIT) arch_cpu_init, /* 相关体系结构CPU初始化cpu/.../arch/cpu.c */ #endif board_init, /* 相关板子初始化board/sbc2410x/sbc2410.c*/ interrupt_init, /* 中断初始化cpu/arm920t/s3c24x0/interrupts.c*/ env_init, /* 环境变量初始化*/ init_baudrate, /* 初始化波特率*/ serial_init, /* 串口初始化drivers/serial/serial.c*/ console_init_f, /* stage 1 init of console ??? */ display_banner, /* say that we are here */ #if defined(CONFIG_DISPLAY_CPUINFO) print_cpuinfo, /* 打印CPU信息 */ #endif #if defined(CONFIG_DISPLAY_BOARDINFO) checkboard, /* 显示板块信息*/ #endif #if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C) init_func_i2c,//IIC初始化函数 #endif dram_init, /* configure available RAM banks */ #if defined(CONFIG_CMD_PCI) || defined (CONFIG_PCI) arm_pci_init, #endif display_dram_config,存储器件初始化 NULL,
}
start_armboot函数主要是初始化外设, 和初始化gd数据结构,以便后来Linux内核访问。
下面开始分析start_armboot函数流程
void start_armboot (void)
{
init_fnc_t**init_fnc_ptr; //定义了一个二级指针,用来指向函数数组,实现遍历调用函数数组里的函数。
char*s;
#if defined(CONFIG_VFD) || defined(CONFIG_LCD)
unsignedlong addr;
#endif
/*Pointer is writable since we allocated a register for it */
/*_armboot_start是uboot下载到内存中的地址值,减掉CONFIG_SYS_MALLOC_LEN和gd_t结构体大小就是gd变量的地址,看博文http://blog.csdn.net/oyhb_1992/article/details/76994140*/
/*为gd指针变量分配内存*/
gd= (gd_t*)(_armboot_start - CONFIG_SYS_MALLOC_LEN - sizeof(gd_t));
/*compiler optimization barrier needed for GCC >= 3.4 */
__asm____volatile__("": : :"memory");
/*清零gd结构体变量的内存*/
memset((void*)gd, 0, sizeof (gd_t));
/*注意:内存上gd_t结构体的后面存的是bd_t结构体*/
gd->bd= (bd_t*)((char*)gd - sizeof(bd_t));
memset(gd->bd, 0, sizeof (bd_t));
gd->flags|= GD_FLG_RELOC;//标志代码已经调到内存中运行了
/*_armboot_start=_start*/
/*得到uboot代码在内存中的存储区域大小*/
monitor_flash_len= _bss_start - _armboot_start;
/*执行函数数组里的函数,在下一篇博客启动阶段3中分析*/
for(init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if((*init_fnc_ptr)() != 0) {
hang();
}
}
/*初始化堆空间*/
/*armboot_start is defined in the board-specific linker script */
mem_malloc_init(_armboot_start - CONFIG_SYS_MALLOC_LEN,
CONFIG_SYS_MALLOC_LEN);
#ifndef CONFIG_SYS_NO_FLASH
/*configure available FLASH banks自己对flash分区,更好管理*/
display_flash_config(flash_init ());
#endif /* CONFIG_SYS_NO_FLASH */
#ifdef CONFIG_VFD
# ifndefPAGE_SIZE
# define PAGE_SIZE 4096
# endif
/*
* reserve memory for VFD display (always fullpages)
*/
/*bss_end is defined in the board-specific linker script */
addr= (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
vfd_setmem(addr); //分配VFD显示的帧缓存
gd->fb_base= addr;
#endif /* CONFIG_VFD */
#ifdef CONFIG_LCD
/*board init may have inited fb_base */
if(!gd->fb_base) {
# ifndefPAGE_SIZE
# define PAGE_SIZE 4096
# endif
/*
* reserve memory for LCD display (always fullpages)
*/
/*bss_end is defined in the board-specific linker script */
addr= (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
lcd_setmem(addr); //分配LCD显示的帧缓存
gd->fb_base= addr;
}
#endif /* CONFIG_LCD */
#if defined(CONFIG_CMD_NAND)
puts("NAND: ");
nand_init(); /* go init the NAND初始化NAND flash控制器*/
#endif
#if defined(CONFIG_CMD_ONENAND)
onenand_init(); /*很复杂! */
#endif
#ifdef CONFIG_HAS_DATAFLASH
/*AT91F_DataflashInit()完成 DataFlash的初始化。这个函数在drivers/dataflash.c中。首先调用AT91F_SpiInit ()初始化SPI接口,然后调用AT91F_DataflashProbe()扫描所有的 SPI片选,检测DataFlash是否存在,实现原理和NAND Flash类似,都是向芯片发送查询ID命令,根据返回值判断芯片的类型和容量。*/
AT91F_DataflashInit();
dataflash_print_info();
#endif
/*初始化环境参数,就是UBOOT启动时输入命令printenv,setenv里面的参数*/
/*initialize environment */
env_relocate();
#ifdef CONFIG_VFD
/*must do this after the framebuffer is allocated */
/*初始化framebuff*/
drv_vfd_init();
#endif /* CONFIG_VFD */
#ifdef CONFIG_SERIAL_MULTI
serial_initialize();
#endif
/*IP Address */
//通过命令行参数传递获取ip地址
gd->bd->bi_ip_addr= getenv_IPaddr ("ipaddr");
stdio_init(); /* get the devices list going. */
jumptable_init();
#if defined(CONFIG_API)
/*Initialize API */
api_init();
#endif
console_init_r(); /* fully init console as a device*/
#if defined(CONFIG_ARCH_MISC_INIT)
/*miscellaneous arch dependent initialisations */
arch_misc_init();
#endif
#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_TI_EMAC
/*XXX: this needs to be moved to board init */
extern void davinci_eth_set_mac_addr (constu_int8_t *addr);
if(getenv ("ethaddr")) {
ucharenetaddr[6];
eth_getenv_enetaddr("ethaddr",enetaddr);
davinci_eth_set_mac_addr(enetaddr);
}
#endif
#if defined(CONFIG_DRIVER_SMC91111) ||defined (CONFIG_DRIVER_LAN91C96)
/*XXX: this needs to be moved to board init */
if(getenv ("ethaddr")) {
ucharenetaddr[6];
eth_getenv_enetaddr("ethaddr",enetaddr);
smc_set_mac_addr(enetaddr);
}
#endif /* CONFIG_DRIVER_SMC91111 ||CONFIG_DRIVER_LAN91C96 */
/*Initialize from environment */
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
#ifdef CONFIG_GENERIC_MMC
puts("MMC: ");
mmc_initialize(gd->bd);
#endif
#ifdef CONFIG_BITBANGMII
bb_miiphy_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() can return to retry autoboot, if so just run it again. */
for(;;) {
main_loop();
}
/*NOTREACHED - no way out of command loop except booting */
}
void hang (void)
{
puts("### ERROR ### Please RESET the board ###\n");
for(;;);
}
参照文章:http://www.360doc.com/content/14/1101/11/19351147_421634710.shtml