前面分析了 [38]_uboot启动第一阶阶段分析,主要是关注start.S和lowlevel_init.S 这两个文件即可
现在分析它的第二阶段,从start_armboot函数开始分析,这个函数非常长,足足有400多行,还没包括中间要调用的其它函数,分析文件是board.c文件:
1.先来看看这两个结构体,一个是关于uboot全局变量的结构体gd_t,一个是关于板级信息的结构体bd_t,代码如下:
typedef struct global_data {
bd_t *bd; // 存放开发板相关的信息 ,譬如波特率、IP地址、网卡地址、机器码、启动参数
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? */ //内存中的那份环境变量的值,原来的环境变量可能存在其它启动介质中
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;
phys_size_t ram_size; /* RAM size */
unsigned long reset_status; /* reset status register at boot */
#endif
void **jt; /* jump table */
} gd_t; // 根据结构体内存对齐特性,这个结构体的大小为36字节
------------------------------------------
typedef struct bd_info {
int bi_baudrate; /* serial console baudrate */ //串口控制台波特率
unsigned long bi_ip_addr; /* IP Address */ //板子上的ip地址
unsigned char bi_enetaddr[6]; /* Ethernet adress */ //网卡ip地址
struct environment_s *bi_env;
ulong bi_arch_number; /* unique id for this board */ //uboot机器码
ulong bi_boot_params; /* where this board expects params */ //uboot启动参数
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; // 根据结构体内存对齐特性,这个结构体的大小为44字节
2.因为上面两个结构体定义时在内存中是没有被分配内存,还有此时目前还是处于uboot的启动阶段,不能使用malloc去申请内存,只能是手工分配gd_base:
#ifdef CONFIG_MEMORY_UPPER_CODE /* by scsuh */
ulong gd_base; //gd_base是DDR
//内存排布:uboot的起始地址(0xc3e00000)、uboot的大小2M、堆大小912KB、栈大小512KB、gd_t大小36字节
gd_base = CFG_UBOOT_BASE + CFG_UBOOT_SIZE - CFG_MALLOC_LEN - CFG_STACK_SIZE - sizeof(gd_t);
#ifdef CONFIG_USE_IRQ
gd_base -= (CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ);
#endif
3.内存排布总结如下:
(1)uboot区 CFG_UBOOT_BASE-xx (长度为uboot的实际长度CFG_UBOOT_SIZE )
(2)堆区 长度为CFG_MALLOC_LEN,实际为912KB,包括环境变量的大小。
(3)栈区 长度为CFG_STACK_SIZE,实际为512KB
(4)gd 长度为sizeof(gd_t),实际36字节
(5)bd 长度为sizeof(bd_t),实际为44字节左右
(6)内存间隔 为了防止高版本的gcc的优化造成错误。譬如在uboot中使用C语言内嵌汇编的方式来实现
/* compiler optimization barrier needed for GCC >= 3.4 */
__asm__ __volatile__("": : :"memory"); //“ __asm__ ”表示在C语言中内嵌汇编,防止高版本gcc优化它
//这里一个for循环,主要是为了去遍历函数指针数组init_sequence,目的是依次执行init_sequence这个函数指针数组中元素
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if ((*init_fnc_ptr)() != 0) { //如果遍历执行的这些函数的结果不等于0,则初始化就会失败被挂起
hang (); //挂起
}
}
4.关于init_sequence函数指针数组的定义如:
init_fnc_t *init_sequence[] = { /* 这些函数指针数组 */
cpu_init, /* basic cpu dependent setup */
#if defined(CONFIG_SKIP_RELOCATE_UBOOT)
reloc_init, /* Set the relocation done flag, must
do this AFTER cpu_init(), but as soon
as possible */
#endif
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 */
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
#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C)
init_func_i2c,
#endif
dram_init, /* configure available RAM banks */ /*bank的个数配置*/
display_dram_config,
NULL,
};
init_sequence这个函数指针数组里面囊括了一个个的函数,我们很多工作都是在这些函数中展开的,譬如CPU初始化(cpu_init)、板级初始化(board_init)、中断初始化(interrupt_init)、环境变量初始化(env_init) ...........等等。所以这个函数指针数组是我们需要分析的关键。
-----------------------------------------------------------------------------------------------------------------------
int cpu_init (void)
{
/*
* setup up stacks if necessary
*/
#ifdef CONFIG_USE_IRQ / /
IRQ_STACK_START = _armboot_start - CFG_MALLOC_LEN - CFG_GBL_DATA_SIZE - 4;
FIQ_STACK_START = IRQ_STACK_START - CONFIG_STACKSIZE_IRQ;
#endif
return 0;
}
解析:CONFIG_USE_IRQ没有被定义,所以这里不成立,cpu_init是空函数,原因是该做的事在start.S中都做了。
--------------------------------------------------
/*
* This routine sets the relocation done flag, because even if
* relocation is skipped, the flag is used by other generic code.
*/
static int reloc_init(void)
{
gd->flags |= GD_FLG_RELOC;
return 0;
}
#endif
解析:reloc_init这个函数只是对gd_t结构体中的flag标志位进行置位,表示介质中的uboot的BL2部分已经被拷贝到DDR中了。
-----------------------------------------------------
int board_init(void)
{
DECLARE_GLOBAL_DATA_PTR;
#ifdef CONFIG_DRIVER_SMC911X
smc9115_pre_init(); //网卡相关
#endif
#ifdef CONFIG_DRIVER_DM9000
dm9000_pre_init();
#endif
gd->bd->bi_arch_number = MACH_TYPE; //机器码
gd->bd->bi_boot_params = (PHYS_SDRAM_1+0x100); //uboot启动参数:0x30000000 + 0x100 = 0x30000100
//uboot启动内核后,内核在bi_boot_params内存内地址(0x30000100)处读取uboot传给它的参数