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

来源:互联网 发布:大闹天宫数据库修改 编辑:程序博客网 时间:2024/05/16 14:15

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 = 2*1024*1024 = 2M
CFG_MALLOC_LEN = CFG_ENV_SIZE + 896*1024 = 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。

原创粉丝点击