嵌入式之uboot源码分析-启动第二阶段学习笔记(上篇)

注:以下内容来自朱老师物联网大讲堂

本部分主要是init_sequence函数中子函数的介绍

在启动的第一阶段我们最后调用start_armboot,进入uboot的第二阶段,下面我们将来世第二阶段的分析。

1. start_armboot函数简介

1.1 一个很长的函数

(1)这个函数在uboot/lib_arm/board.c的第444行开始到908行结束。
(2)450行还不是全部,因为里面还调用了别的函数。
(3)为什么这么长的函数,怎么不分成两三个函数?主要因为这个函数整个构成了uboot启动的第二阶段。

1.2 一个函数组成uboot第二阶段

1.3 宏观分析:uboot第二阶段应该做什么

(1)概括来讲uboot第一阶段主要就是初始化了SoC内部的一些部件(譬如看门狗、时钟),然后初始化DDR并且完成重定位。
(2)由宏观分析来讲,uboot的第二阶段就是要初始化剩下的还没被初始化的硬件。主要是SoC外部硬件(譬如iNand、网卡芯片····)、uboot本身的一些东西(uboot的命令、环境变量等····)。然后最终初始化完必要的东西后进入uboot的命令行准备接受命令。

1.4 思考:uboot第二阶段完结于何处?

(1)uboot启动后自动运行打印出很多信息(这些信息就是uboot在第一和第二阶段不断进行初始化时,打印出来的信息)。然后uboot进入了倒数bootdelay秒然后执行bootcmd对应的启动命令。
(2)如果用户没有干涉则会执行bootcmd进入自动启动内核流程(uboot就死掉了);此时用户可以按下回车键打断uboot的自动启动进入uboot的命令行下。然后uboot就一直工作在命令行下。
(3)uboot的命令行就是一个死循环,循环体内不断重复:接收命令、解析命令、执行命令。这就是uboot最终的归宿。

2. start_armboot解析1

2.1 init_fnc_t

首先我们发现定义了一个二重指针,其次我们发现init_fnc_t是一个使用typedef重新定义的类型,综合分析我们知道,init_fnc_ptr是一个二重函数指针

typedef int (init_fnc_t) (void);
init_fnc_t **init_fnc_ptr;

(1)typedef int (init_fnc_t) (void); 这是一个函数类型
(2)init_fnc_ptr是一个二重函数指针,回顾高级C语言中讲过:二重指针的作用有2个(其中一个是用来指向一重指针),一个是用来指向指针数组。因此这里的init_fuc_ptr可以用来指向一个函数指针数组。

2.2 DECLARE_GLOBAL_DATA_PTR

我们在467行开始发现有一个gd变量,这个时候我们发现在70行有个宏定义,顺着这个宏定义我们看到了变量的原型。

gd = (gd_t*)gd_base;
DECLARE_GLOBAL_DATA_PTR
#define DECLARE_GLOBAL_DATA_PTR     register volatile gd_t *gd asm ("r8")

(1)#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm (“r8”)
定义了一个全局变量名字叫gd,这个全局变量是一个指针类型,占4字节。用volatile修饰表示可变的,用register修饰表示这个变量要尽量放到寄存器中,后面的asm(“r8”)是gcc支持的一种语法,意思就是要把gd放到寄存器r8中。
(2)综合分析,DECLARE_GLOBAL_DATA_PTR就是定义了一个要放在寄存器r8中的全局变量,名字叫gd,类型是一个指向gd_t类型变量的指针。
(3)为什么要定义为register?因为这个全局变量gd(global data的简称)是uboot中很重要的一个全局变量(准确的说这个全局变量是一个结构体,里面有很多内容,这些内容加起来构成的结构体就是uboot中常用的所有的全局变量),这个gd在程序中经常被访问,因此放在register中提升效率。因此纯粹是运行效率方面考虑,和功能要求无关。并不是必须的。
(4)gd_t定义在include/asm-arm/global_data.h中。
gd_t中定义了很多全局变量,都是整个uboot使用的;其中有一个bd_t类型的指针,指向一个bd_t类型的变量,这个bd是开发板的板级信息的结构体,里面有不少硬件相关的参数,譬如波特率、IP地址、机器码、DDR内存分布。

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;

2.3 内存使用排布

2.3.1 为什么要分配内存

(1)DECLARE_GLOBAL_DATA_PTR只能定义了一个指针,也就是说gd里的这些全局变量并没有被分配内存,我们在使用gd之前要给他分配内存,否则gd也只是一个野指针而已。
(2)gd和bd需要内存,内存当前没有被人管理(因为没有操作系统统一管理内存),大片的DDR内存散放着可以随意使用(只要使用内存地址直接去访问内存即可)。但是因为uboot中后续很多操作还需要大片的连着内存块,因此这里使用内存要本着够用就好,紧凑排布的原则。所以我们在uboot中需要有一个整体规划。

2.3.2 DDR中的内存排布
gd_base = CFG_UBOOT_BASE + CFG_UBOOT_SIZE - CFG_MALLOC_LEN - CFG_STACK_SIZE - sizeof(gd_t);

我们知道我们在第一阶段进行了DDR的初始化和uboot的重定位,所以根据如上代码我们可以得到如下的DDR内存分配图。
DDR中的内存分配
(1)uboot区 CFG_UBOOT_BASE-xx(长度为uboot的实际长度)
(2)堆区 长度为CFG_MALLOC_LEN,实际为912KB
(3)栈区 长度为CFG_STACK_SIZE,实际为512KB
(4)gd 长度为sizeof(gd_t),实际36字节
(5)bd 长度为sizeof(bd_t),实际为44字节左右
(6)内存间隔 为了防止高版本的gcc的优化造成错误。

3. start_armboot解析2

3.1 for循环执行init_sequence(487~491)

	for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
   
		if ((*init_fnc_ptr)() != 0) {
   
			hang ()
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值