对uboot启动的BL2阶段的主体代码的分析
BL2阶段从BL1阶段调用start_armboot函数完成远跳转至此阶段继续,start_armboot函数与其中调用的函数一起组成了我们BL2阶段的实现,而start_armboot包含在Board.c文件中,所以此份笔记以Board.s中的start_armboot函数作为分析目标。
宏观的分析:
BL2阶段主要是进行开发板外部硬件的初始化、以及软件数据结构的初始化。uboot在启动完后打印出一些信息,然后进入倒数bootdelay秒后执行bootcmd对应的启动命令。如果用户没有进行干涉则会执行bootcmd对应的命令,然后进入自启动内核流程(此后uboot就死掉了),此时用户可以按回车键打断uboot的自启动过程进入uboot的命令行下,然后uboot就会一直工作在命令行下。而uboot的命令行就是一个死循环,循环体内不断地重复:接受、解析、执行命令。 ---执行的快,死得快。
核心的工作:
初始化第一阶段结束还没被初始化的硬件:SOC的外部硬件;uboot本身的一些东西:uboot的命令;环境变量等。
分析的开始:
1.定义了一个二重函数指针类型的变量:
init_fnc_t **init_fnc_ptr;
而这个init_fnc_t类型的变量是在之前被定义:
typedef int (init_fnc_t) (void);
对这个变量在后面会被使用。
2.uboot的全局变量的管理:gd_t 结构体变量:
DECLARE_GLOBAL_DATA_PTR
在Board.c中了一声明个宏,而这个宏的定义如下:
#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r8")
这里的宏定义就是定义了一个名字叫做gd_t的指针类型的全局变量,register将变量放在寄存器中,提升读取、运行的效率,而根据后面gcc支持的一种语法可知将其放在了寄存器r8中,volatile设置变量为可变类型的。这个全局变量实际是一个结构体变量,里面存放着uboot常用的所有全局变量(或者说是所有全局变量的指针)。
对于gd_t内具体的内容如下:
typedef struct global_data {
bd_t *bd;
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 */
#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_t 这个结构体内:
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;
3.定义了存储全局变量的地址起始地址,为全局变量分配内存:
在这一步之前,gd_base还只是一个指针,没有没有被分配内存,下面为其分配内存
ulong gd_base;
gd_base = CFG_UBOOT_BASE + CFG_UBOOT_SIZE - CFG_MALLOC_LEN - CFG_STACK_SIZE - sizeof(gd_t);
下面对gd进行实例化:
gd = (gd_t*)gd_base;
内存的分析:
CFG_UBOOT_BASE :0x33e00000
CFG_UBOOT_SIZE : 2MB
CFG_MALLOC_LEN : 896+16=912KB 堆区
CFG_STACK_SIZE : 512KB 栈区
gd sizeof(gd_t) : 4*9=36字节
bd sizeof(bd_t) : 约为44字节
这里使用的内存就是上一个阶段BL1第三次设置的2MB的栈,进过分析大致可知这些全局变量从uboot的起始地址也就是0x33e00000上方0.4MB左右的区域开始向上存储(起始地址向上走200~300KB的内存区域是uboot镜像的存储位置)
4.循环执行函数指的逻辑分析:
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if ((*init_fnc_ptr)() != 0) {
hang ();
}
对循环的分析:
(1)循环的初始条件:init_fnc_ptr = init_sequence。这里的init_fnc_ptr 就相当于循环中的i