u-boot第二阶段分析(一)

start_armboot函数的引入

注:本次分析的u-boot是九鼎官方的u-boot代码
下载地址:链接:http://pan.baidu.com/s/1gfpDZqj 密码:7cqe


一 start_armboot函数简介
start_armboot函数是一个长函数,在uboot/lib_arm/board.c的第444——908行,其中也调用了其他函数,共同构成了u-boot的第二阶段。

1.第二阶段的主要工作
我们之前已经分析过了第一阶段,主要是初始化了Soc内部的一些部件和初始化了DDR以及重定位,第二部分主要是初始化Soc外部的一些硬件设施(第一阶段没有初始化完生下来的)。

2.整个u-boot在什么地方结束
u-boot的目标是启动内核,我们在启动u-boot时,最后会出现倒数启动内核时间,如果用户没有干涉则会执行bootcmd进入自动启动内核流程;此时用户可以按下回车键打断uboot的自动启动进入u-boot的命令行下。
u-boot的命令行实际上是一个死循环,代码如下:

for (;;) {
		main_loop ();
	}

循环体内不断重复:接收命令、解析命令、执行命令。


接下来进行函数代码的分析;

二 变量的定义和初始化
**1.init_fnc_t **
代码为:init_fnc_t **init_fnc_ptr
其中init_fnc_ptr是一个二重指针,在c语言中二重指针有两个作用:一个是指向一个一重指针;第二个是指向一个指正数组,这里的init_fuc_ptr可以用来指向一个函数指针数组。
我们可以找到它的声明,typedef int (init_fnc_t) (void),这是一个函数类型。

2.DECLARE_GLOBAL_DATA_PTR
在变量的初始化过程中常见到“gd”和“bd”,那么是哪里来的呢?
在大部分的.c文件中,都有DECLARE_GLOBAL_DATA_PTR这个宏,这个宏的定义如下:

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

定义了一个全局变量名字叫gd,这个全局变量是一个指针类型,占4字节。用volatile修饰表示可变的,用register修饰表示这个变量要尽量放到寄存器中,后面的asm(“r8”)是gcc支持的一种语法,意思就是要把gd放到寄存器r8中。
简而言之,DECLARE_GLOBAL_DATA_PTR就是定义了一个要放在寄存器r8中的全局变量,名字叫gd,类型是一个指向gd_t类型变量的指针。

(1)为什么要用register修饰?
因为这个全局变量gd(global data的简称)是uboot中很重要的一个全局变量(准确的说这个全局变量是一个结构体,里面有很多内容,这些内容加起来构成的结构体就是uboot中常用的所有的全局变量),这个gd在程序中经常被访问,因此放在register中提升效率。

(2)gd是一个结构体指针,这个结构体的定义如下:

typedef	struct	global_data {
	bd_t		*bd;
	unsigned long	flags;/*标志位*/
	unsigned long	baudrate; /控制台的波特率/
	unsigned long	have_console;	/* 只有一位(bool类型),判断有没有concole(控制台)*/                                
	unsigned long	reloc_off;	/* 重定位偏移量 */
	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是一个bd_t类型的指针,指向的也是一个结构体,这个结构体里定义了开发板的板级信息,里面有不少硬件相关的参数,譬如波特率、IP地址、机器码、DDR内存分布。
代码如下:

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;

三 内存排布
1.上一节定义了gd和bd这两个结构体指针,但是并没有分配内存,也就是说这两个指针还是野指针,下面就开始为它们分配内存。
注:u-boot还是裸机程序,所以没有操作系统管理内存,所以也就没法使用malloc等函数。

#ifdef CONFIG_MEMORY_UPPER_CODE /* by scsuh */
	ulong gd_base;
	gd_base = CFG_UBOOT_BASE + CFG_UBOOT_SIZE - CFG_MALLOC_LEN - CFG_STACK_SIZE - sizeof(gd_t);
#ifdef CONFIG_USE_IRQ
	gd_base -= (CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ);
#endif
	gd = (gd_t*)gd_base;
#else
	gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));
#endif

代码分析:
首先定义了一个ulong类型的变量gd_base,然后对其进行赋值,其中
CFG_UBOOT_BASE = 0x33e00000
CFG_UBOOT_SIZE = 210241024 = 2M
CFG_MALLOC_LEN = CFG_ENV_SIZE + 8961024 = 912kb(分配的堆区大小)
CFG_STACK_SIZE = 512
1024 = 512kb
sizeof(gd_t) 大约36字节,算的时候可以忽略不计
所以gd_base的地址大约可以算出来,大约在0x33e00000网上624kb左右的位置处。
然后将gd指针给实例化:gd = (gd_t*)gd_base;
这是将gd_base指针强制类型转换为gd_t*类型。

2.内嵌汇编

/* compiler optimization barrier needed for GCC >= 3.4 */
	__asm__ __volatile__("": : :"memory");

这行代码的作用是为了防止高版本的gcc的优化造成错误。

3.内存清零

memset ((void*)gd, 0, sizeof (gd_t));
gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
memset (gd->bd, 0, sizeof (bd_t));

需要注意的是第二行代码中将指针gd强制类型转换为char*是因为指针相减的话,char减1就是减1,int减1就是减4。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值