由第一阶段,已经将在SD卡的整个uboot重定位拷贝到了DDR的链接地址中去后,又回到了调用这个函数的位置
。接着往下执行遇到了这个start_armboot函数(uboot启动的第二阶段)。
uboot启动的第二阶段分析:
6、1、start_armboot函数
(1)一个很长的函数,从444行到908行。这个函数不仅仅只有这么长,这个函数的内部还调用了一些函数,所以说这个函数是很庞大的。这个函数构成了整个uboot启动的第二个阶段。
(2)宏观上分析,uboot启动的第二阶段应该做什么?
(3)uboot的第一阶段主要完成的任务就是,初始化SOC内部的一些部件(关看门狗,初始化时钟等),也初始化了DDR并且将整个uboot重定位到了DDR中去。
(4)所以uboot的第二阶段应该要做的就是,去初始化一些在uboot第一阶段没有初始化的硬件,主要是初始化SOC外部的硬件(,如iNand、网卡芯片····),还有uboot本身的东西(uboot的命令,环境变量等·····),然后最终完成这些任务时,进入到uboot的命令行,准备接受命令,人机开始交互。
(5)uboot启动时是开机自动启动的,在启动的过程中,会打印出来很多的信息(这些信息就是uboot在第一阶段和第二阶段不断进行初始化时,打印出来的信息,我通过这些信息也可以跟踪uboot的轨迹,看uboot执行是否成功运行。)然后uboot开始bootdelay倒数,如果在倒数的过程中,我们敲了回车,则会进入到uboot的命令行下,如果我们没有干涉bootdelay月的倒数,倒数结束后,将直接执行bootcmd对应的命令(一般是启动内核命令),uboot就会死掉了。不管内核这次是否启动成功,uboot这一生都已经死掉了。
(6)uboot的命令行就是一个死循环,这个循环体内,不断的接受命令,解析命令,执行命令,直到接收解析执行了启动内核命令,uboot就会死掉。
6、2、start_armboot函数解析1
1、init_fnc_t 通过搜索可以知道这是一个函数类型。
(1)typedef int (init_fnc_t) (void); 这是将一个返回值为int的,参数为void的函数类型,重命名为init_fnc_t,所以这个init_fuc_t是一个函数类型。
(2)init_fnc_t **init_fnc_ptr; 所以这句话的意思就是定义了一个二重的函数指针。
init_fnc_ptr就是一个二重函数指针。回顾学过的高级C语言,二重指针有两个作用,一个是用来指向一重指针,一个是用来指向指针数组(数组中的没个元素都是指针)。
所以这个init_fnc_ptr这个二重函数指针,可以用来指向一个函数指针数组(数组里面的元素全是函数指针)
2、board.c文件中,开始的内容有个下面的东西
(1)DECLARE_GLOBAL_DATA_PTR;
//#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r8")
(2)这个宏的意思就是,定义了一个全局变量gd,这个全局变量gd是一个指针,用volatile修饰,表示这个变量是可变的,在编译之外的情况下,这个变量的值是可以改变的,不让编译对这个变量做优化,用register修饰,表示让这个变量尽量放在寄存器中,提高速度,asm ("r8")意思是,让这个变量放在寄存器的r8中,就是不仅让这个变量放在了寄存器中,还要让这个变量放到的寄存器是寄存器r8中。
(3)综合分析:DECLARE_GLOBAL_DATA_PTR 这个宏的意思就是,定义了一个放在寄存器r8中,并且值是可以在编译之外改变的全局变量,名字叫gd,并且这个全局变量是一个指针类型,指向gd_t类型变量的指针。
(4)为什么要用register修饰这个全局变量呢,因为这个全局变量是一个gd_t * 类型的,是一个指向结构体的指针。同时这个全局变量gd(global data)指向的那个结构体中放了很多内容,组成了这个结构体,这些内容就是uboot中常用的全局变量。
也就是说,这个gd全局变量所指向的那个结构体中放的内容是uboot常用的全局变量。也就是说,那个把那个放了uboot常用的全局变量的结构体的地址放再了gd这个全局变量指针中。gd这个指针本身占4个字节。
所以这个gd全局变量是会经常被访问的,所以为了提升程序的效率,用register来修饰
(5)gd_t这个结构体类型定义在include/asm-arm/global_data.h中,这个结构体类型中,第一个又是定义了一个结构体类型的指针变量,这个结构体类型bd_t定义在include/asm-arm/U-boot.h中,找到bd_t这个结构体类型的封装,可以看着个封装的名字bd_info,知道这个结构体封装的是开发板相关的硬件信息。
(6)typedef struct global_data {
bd_t *bd; //结构体类型指针变量,指向一个struct bd_info结构体,里面封装了开发板的
//硬件相关信息
unsigned long flags; //标志位,干嘛的现在不清楚
unsigned long baudrate; //波特率 因为uboot要用串口发送到人机界面上,实现人机交互,所以需要波特率
//都是全局的,因为都是由gd这个全局变量指针开始指向的。控制台的波特率
unsigned long have_console; /* serial_init() was called */ //bool类型的变量,虽然定义是四字节,但
//是后面会知道只用了一个位,可以看出应该是代表有无控制台的,就是能否进行printf scanf ,在控制台建立起来以后就可以printf scanf了,不然只能用串口的putc等
unsigned long reloc_off; /* Relocation Offset */ //重定位时候的偏移量
unsigned long env_addr; /* Address of Environment struct */ //封装环境变量那个结构体的偏移量
//地址
unsigned long env_valid; /* Checksum of Environment valid? */ //在内存里的环境变量当前是可以使用的
//还是不可以使用的。也是一个bool类型的变量,如果是1就表示
//内存中的那一份环境变量已经可以使用了。
//为什么要检查内存中那一份环境变量是否可以使用呢,因为开始的时候我们的
//环境变量是放在SD卡中,当我们把在SD卡中的环境变量读取到内存中的时候,这些个环境
//变量是不能马上建立好的,因为内存中开始没有的,所以要检查,等所有的环境变量从SD卡
//读取到内存中,建立好后,我们才可以使用内存中那一份环境变量
unsigned long fb_base; /* base address of frame buffer */ //显存空间的起始地址,因为在裸机中的LCD
//那一章我们知道显存空间中的数据图像,会通过lcd控制器自动硬件刷到lcd液晶屏上显示,
//所以需要一个显存空间的起始地址
#ifdef CONFIG_VFD //这个宏没有定义
unsigned char vfd_type; /* display type */
#endif
#if 0 //用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 */ //调转表,在uboot中应该是没有用到
} gd_t;
/***************************************************************************/
typedef struct bd_info {
int bi_baudrate; /* serial console baudrate */ //开发板上的波特率,这个波特率和上面的那
//个波特率设置为一模一样的,可以能是重复了
//bi表示是在bd_info这个结构体中的。也有
//告诉我们这些是跟开发板板上有关的
unsigned long bi_ip_addr; /* IP Address */ //开发板的IP地址
unsigned char bi_enetaddr[6]; /* Ethernet adress */ //开发板的MAC地址
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]; //开发板上DDR的信息,定义了一个结构体数组变量
//这个结构体数组bi_dram有两个元素,CONFIG_NR_DRAM_BANKS值
//是看代码知道的,为什么是2呢,因为我们有两个DDR,每一个
//元素是一个结构体,里面有两个信息一个是在哪里个地址开始,
//一个是有多大。一个元素表示一个DDR
#ifdef CONFIG_HAS_ETH1 //这个宏我们有定义
/* second onboard ethernet port */ //两个网卡的意思,但是上面的宏没有定义
unsigned char bi_enet1addr[6];
#endif
} bd_t; //将这封装了很多东西的结构体类型重命名为bd_t
来源: http://www.bubuko.com/infodetail-1948726.html