start_armboot函数的引入
注:本次分析的u-boot是九鼎官方的u-boot代码
下载地址:链接:http://pan.baidu.com/s/1gfpDZqj 密码:7cqe
一 start_armboot函数简介
start_armboot函数是一个长函数,在uboot/lib_arm/board.c的第444——908行,其中也调用了其他函数,共同构成了u-boot的第二阶段。
1.第二阶段的主要工作
我们之前已经分析过了第一阶段,主要是初始化了Soc内部的一些部件和初始化了DDR以及重定位,第二部分主要是初始化Soc外部的一些硬件设施(第一阶段没有初始化完生下来的)。
2.整个u-boot在什么地方结束
u-boot的目标是启动内核,我们在启动u-boot时,最后会出现倒数启动内核时间,如果用户没有干涉则会执行bootcmd进入自动启动内核流程;此时用户可以按下回车键打断uboot的自动启动进入u-boot的命令行下。
u-boot的命令行实际上是一个死循环,代码如下:
for (;;) {
main_loop ();
}
循环体内不断重复:接收命令、解析命令、执行命令。
接下来进行函数代码的分析;
二 变量的定义和初始化
**1.init_fnc_t **
代码为:init_fnc_t **init_fnc_ptr
其中init_fnc_ptr是一个二重指针,在c语言中二重指针有两个作用:一个是指向一个一重指针;第二个是指向一个指正数组,这里的init_fuc_ptr可以用来指向一个函数指针数组。
我们可以找到它的声明,typedef int (init_fnc_t) (void),这是一个函数类型。
2.DECLARE_GLOBAL_DATA_PTR
在变量的初始化过程中常见到“gd”和“bd”,那么是哪里来的呢?
在大部分的.c文件中,都有DECLARE_GLOBAL_DATA_PTR这个宏,这个宏的定义如下:
#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r8")
定义了一个全局变量名字叫gd,这个全局变量是一个指针类型,占4字节。用volatile修饰表示可变的,用register修饰表示这个变量要尽量放到寄存器中,后面的asm(“r8”)是gcc支持的一种语法,意思就是要把gd放到寄存器r8中。
简而言之,DECLARE_GLOBAL_DATA_PTR就是定义了一个要放在寄存器r8中的全局变量,名字叫gd,类型是一个指向gd_t类型变量的指针。
(1)为什么要用register修饰?
因为这个全局变量gd(global data的简称)是uboot中很重要的一个全局变量(准确的说这个全局变量是一个结构体,里面有很多内容,这些内容加起来构成的结构体就是uboot中常用的所有的全局变量),这个gd在程序中经常被访问,因此放在register中提升效率。
(2)gd是一个结构体指针,这个结构体的定义如下:
typedef struct global_data {
bd_t *bd;
unsigned long flags;/*标志位*/
unsigned long baudrate; /控制台的波特率/
unsigned long have_console; /* 只有一位(bool类型),判断有没有concole(控制台)*/
unsigned long reloc_off; /* 重定位偏移量 */
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 */
#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;
其中bd是一个bd_t类型的指针,指向的也是一个结构体,这个结构体里定义了开发板的板级信息,里面有不少硬件相关的参数,譬如波特率、IP地址、机器码、DDR内存分布。
代码如下:
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 */
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;
三 内存排布
1.上一节定义了gd和bd这两个结构体指针,但是并没有分配内存,也就是说这两个指针还是野指针,下面就开始为它们分配内存。
注:u-boot还是裸机程序,所以没有操作系统管理内存,所以也就没法使用malloc等函数。
#ifdef CONFIG_MEMORY_UPPER_CODE /* by scsuh */
ulong gd_base;
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
gd = (gd_t*)gd_base;
#else
gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));
#endif
代码分析:
首先定义了一个ulong类型的变量gd_base,然后对其进行赋值,其中
CFG_UBOOT_BASE = 0x33e00000
CFG_UBOOT_SIZE = 210241024 = 2M
CFG_MALLOC_LEN = CFG_ENV_SIZE + 8961024 = 912kb(分配的堆区大小)
CFG_STACK_SIZE = 5121024 = 512kb
sizeof(gd_t) 大约36字节,算的时候可以忽略不计
所以gd_base的地址大约可以算出来,大约在0x33e00000网上624kb左右的位置处。
然后将gd指针给实例化:gd = (gd_t*)gd_base;
这是将gd_base指针强制类型转换为gd_t*类型。
2.内嵌汇编
/* compiler optimization barrier needed for GCC >= 3.4 */
__asm__ __volatile__("": : :"memory");
这行代码的作用是为了防止高版本的gcc的优化造成错误。
3.内存清零
memset ((void*)gd, 0, sizeof (gd_t));
gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
memset (gd->bd, 0, sizeof (bd_t));
需要注意的是第二行代码中将指针gd强制类型转换为char*是因为指针相减的话,char减1就是减1,int减1就是减4。