uboot启动第二阶段分析

之前介绍了 uboot 启动第一阶段,现在介绍启动的第二阶段
启动阶段的工作
启动第一阶段的主要工作是对处理器的内部资源(如时钟、串口)、内存(ddr)初始化,并进行 uboot 的重定位,并跳转到启动第二阶段
启动第二阶段的主要工作则是对处理器的外部资源(iNand、网卡芯片…)、uboot环境(uboot命令、环境变量…)等初始化,并等待命令输入
工作流程
正常情况下,在 uboot 的初始化工作完毕后,会启动内核,在启动内核后结束 uboot 程序 。
但是用户可以阻止 uboot 的结束,进入 uboot 的命令行模式,就是一个 uboot 中的死循环;uboot 在死循环中不断接受命令、解析命令、执行命令
如果说启动第一阶段主要工作是完成的,那么启动第二阶段的主要工作是完成的
相关变量介绍
它是一个类型的函数指针数组,通过int () (void); 定义
gd
#define DECLARE_GLOBAL_DATA_PTRregister volatile gd_t *gd asm ("r8")
gd 这个变量通过 PTR 定义,这个变量的作用是用来存储 uboot 需要使用到的全局变量,这样可以减少全局变量的数量,方便他人阅读代码 。具体的作用可以从结构体成员中得知
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; /* Addressof Environment struct */unsigned long env_valid; /* Checksum of Environment valid? */unsigned long fb_base; /* base address of frame buffer */#ifdef CONFIG_VFDunsigned char vfd_type; /* display type */#endifvoid**jt;/* jump table */} gd_t;
gd 变量的结构体成员的功能基本可以从名字和注释中得知,不过对第一个成员 bd 的描述很少,这里再从 bd_t 中获取信息
typedef struct bd_info {intbi_baudrate; /* serial console baudrate */unsigned long bi_ip_addr; /* IP Address */unsigned char bi_enetaddr[6]; /* Ethernet adress */struct environment_s*bi_env;ulongbi_arch_number; /* unique id for this board */ulongbi_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 charbi_enet1addr[6];#endif} bd_t;
从 bd_t 的定义可以看出,bd 这个成员变量的主要作用是用来保存一些和板级相关的信息,如波特率、ip 地址等
因为 gd 这个变量需要被频繁访问,所以使用了,避免编译器做出不适当的优化;另外使用asm (“r8”) 加速访问
内存排布
上面提到的 gd bd 指针变量只是声明,此时还不能使用,需要给他们分配内存空间
【uboot启动第二阶段分析】分配内存的原则就是够用、紧凑,所以首先需要做的是为 gd bd 分配基地址
在 uboot 中计算 gd 和 bd 的基地址的方式如下
gd_base = CFG_UBOOT_BASE + CFG_UBOOT_SIZE - CFG_MALLOC_LEN - CFG_STACK_SIZE - sizeof(gd_t);#ifdef CONFIG_USE_IRQgd_base -= (CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ);#endif...gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
其中 = =2MB =912KB =512KB gd_t=36字节 bd_t≈44字节
从而可以得到内存分配如图
部分外设初始化工作
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {if ((*init_fnc_ptr)() != 0) {hang ();}}
这里初始化工作是通过一个 for 循环完成的,使用先前定义的变量去指向一个预先定义的函数指针数组,在中存储了用来初始化外设的函数指针 。通过 for 循环依次执行每一个初始化函数,一旦返回的值不为0,就停止程序的运行并报错