uboot启动第二阶段要做的事情:
宏观上来讲,就是要初始化剩下的还未被初始化的硬件,主要是SoC外部硬件(网卡,iNand等)和uboot本身的一些东西(uboot命令,环境变量等).
uboot在何处完结:
- uboot启动后自动运行,打印一些信息(这些信息是uboot第一阶段和第二阶段初始化时打印的),然后uboot进入bootdelay,倒数完后执行bootcmd的启动命令,启动OS后uboot就消亡了。
- 在uboot的bootdelay时,用户可以选择按下回车打断uboot进入uboot的命令下,然后uboot就一直工作在命令行下(直到用户输入启动命令,启动OS,然后uboot消亡)
- uboot的命令行是一个死循环,不断重复接收、解析、执行命令。
下面开始uboot第二阶段详解:
uboot第一阶段完成后会自动跳转到start_armboot函数:
- 这个函数整个构成了uboot启动的第二阶段。
- 这个函数在uboot/lib_arm/board.c的第444行开始到908行结束
1.定义了多个数据类型:
其中难以理解的:
init_fnc_t **init_fnc_ptr;
这个二重函数指针在另一篇文章里分析过了
2.分配内存:
#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r8")
-
定义了一个全局变量gd,这个全局变量是指针类型,指向gd_t类型
(1) gd_t定义在include/asm-arm/global_data.h中,是一个结构体
(2)gd_t中定义了很多全局变量,都是整个uboot使用的;其中有一个bd_t类型的指针,指向一个bd_t类型的变量,这个bd是开发板的板级信息的结构体,里面有不少硬件相关的参数,譬如波特率、IP地址、机器码、DDR内存分布。 -
volatile修饰表示可变,register修饰表示优先放在寄存器中
-
后面的asm(“r8”)是gcc支持的一种语法,意思就是要把gd放到寄存器r8中
下面进行了内存分配:
/* Pointer is writable since we allocated a register for it */
#ifdef CONFIG_MEMORY_UPPER_CODE /* by scsuh */
ulong gd_base; #gd在ddr中的基地址
#计算gd_base
gd_base = CFG_UBOOT_BASE + CFG_UBOOT_SIZE - CFG_MALLOC_LEN - CFG_STACK_SIZE - sizeof(gd_t);
gd = (gd_t*)gd_base; #
/* compiler optimization barrier needed for GCC >= 3.4 */
__asm__ __volatile__("": : :"memory"); #为了防止高版本的gcc的优化造成错误
#清理内存
memset ((void*)gd, 0, sizeof (gd_t));
gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
memset (gd->bd, 0, sizeof (bd_t));
分配内存的原因:
(1)DECLARE_GLOBAL_DATA_PTR只能定义了一个指针,也就是说gd里的这些全局变量并没有被分配内存,我们在使用gd之前要给他分配内存,否则gd也只是一个野指针而已。
(2)gd和bd需要内存,内存当前没有被人管理(因为没有操作系统统一管理内存),大片的DDR内存散放着可以随意使用(只要使用内存地址直接去访问内存即可)。但是因为uboot中后续很多操作还需要大片的连着内存块,因此这里使用内存要本着够用就好,紧凑排布的原则。所以我们在uboot中需要有一个整体规划。
内存排布:
(1)uboot区 CFG