u-boot启动代码之start_armboot分析(原创)

来源:互联网 发布:淘宝美工教程视频入门 编辑:程序博客网 时间:2024/05/29 12:17

u-boot-1.1.3 

这里主要对start_armboot函数执行流程进行分析

首先是定义一个指针变量DECLARE_GLOBAL_DATA_PTR,这是一个宏定义,原型在

u-boot-1.1.3\include\asm-arm\global_data.h文件中

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

其实就是定义一个指向结构体gd_t的寄存器变量,

    /* Pointer is writable since we allocated a register for it */

    gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));

    /* compiler optimization barrier needed for GCC >= 3.4 */

    __asm__ __volatile__("": : :"memory");

    memset ((void*)gd, 0, sizeof (gd_t));

    gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));

    memset (gd->bd, 0, sizeof (bd_t));

这段代码是为结构体指针变量分配内存

_armboot_start=0x33f80000,

CFG_MALLOC_LEN=0x30000,

sizeof(gd_t)=0x24,

sizeof(bd_t)=0x24

接下来是一个for循环,作一系列的初始化工作                            

    for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {

       if ((*init_fnc_ptr)() != 0) {

           hang ();

       }

    }

init_fnc_ptr这个变量的定义如下

init_fnc_t **init_fnc_ptr;

它是指向指针的指针变量,变量的类型为init_fnc_t,这是一个使用typedef定义的函数类型,

typedef int (init_fnc_t) (void);

全局变量init_sequence的定义如下

init_fnc_t *init_sequence[] = {

    cpu_init,     /* basic cpu dependent setup   u-boot-1.1.3\cpu\arm920t\cpu.c*/

    board_init,       /* basic board dependent setup   u-boot-1.1.3\board\te2410\te2410.c*/

    interrupt_init,/* set up exceptions  u-boot-1.1.3\cpu\arm920t\s3c24x0\interrupts.c*/

    env_init,     /* initialize environment u-boot-1.1.3\common\env_nand.c*/

    init_baudrate,       /* initialze baudrate settings u-boot-1.1.3\lib_arm\board.c*/

    serial_init,  /* serial communications setup u-boot-1.1.3\cpu\arm920t\s3c24x0\serial.c*/

    console_init_f,      /* stage 1 init of console u-boot-1.1.3\common\console.c*/

    display_banner,      /* say that we are here u-boot-1.1.3\lib_arm\board.c*/

    dram_init,    /* configure available RAM banks u-boot-1.1.3\board\te2410\te2410.c*/

    display_dram_config,

#if defined(CONFIG_VCMA9) || defined (CONFIG_CMC_PU2)

    checkboard,

#endif

    NULL,

};

可以看出这里定义了一个指针数组,它的每一个元素都是指针变量,这些指针变量指向的类型为init_fnc_t,在C语言中函数的入口地址就是函数名,所以这里使用一系列函数名来初始化这个数组

现在来看看到底做了哪些初始化工作

int cpu_init (void)

{

    /*

     * setup up stacks if necessary

     */

#ifdef CONFIG_USE_IRQ

    DECLARE_GLOBAL_DATA_PTR;

 

    IRQ_STACK_START = _armboot_start - CFG_MALLOC_LEN - CFG_GBL_DATA_SIZE - 4;

    FIQ_STACK_START = IRQ_STACK_START - CONFIG_STACKSIZE_IRQ;

#endif

    return 0;

}

其实这个函数没有做任何工作,因为CONFIG_USE_IRQ这个宏没有定义,那么怎么知道这个宏是否被定义了呢,在使用SourceInsight的搜索功能时,发现有些宏会在很多头文件被定义,而我们又很难判断这些头文件是否被当前的c文件包含了,我使用一个简便的方法,利用编译器的预处理功能,

#ifdef CONFIG_USE_IRQ

#error CONFIG_USE_IRQ_xxxxxx

    DECLARE_GLOBAL_DATA_PTR;

    IRQ_STACK_START = _armboot_start - CFG_MALLOC_LEN - CFG_GBL_DATA_SIZE - 4;

    FIQ_STACK_START = IRQ_STACK_START - CONFIG_STACKSIZE_IRQ;

#endif

这样如果这个宏被定义了,那么编译时就会报错输出#error CONFIG_USE_IRQ_xxxxxx,并终止编译.

board_init主要完成了与硬件相关的寄存器的初始化:时钟、GPIO、使能ICache和DCache,

interrupt_init中断初始化

env_init环境变量相关的初始化

init_baudrate波特率初始化

serial_init串口初始化

console_init_f控制台初始化

display_banner从串口输出u-boot相关的信息

dram_init初始化DRAM

display_dram_config从串口输出DRAM的配置信息

 

     /* configure available FLASH banks */

    size = flash_init ();

    display_flash_config (size);

#ifdef CONFIG_VFD//CONFIG_VFD没有定义

#   ifndef PAGE_SIZE

#     define PAGE_SIZE 4096

#   endif

    /*

     * reserve memory for VFD display (always full pages)

     */

    /* bss_end is defined in the board-specific linker script */

    addr = (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);

    size = vfd_setmem (addr);

    gd->fb_base = addr;

#endif /* CONFIG_VFD */

#ifdef CONFIG_LCD//CONFIG_LCD没有定义

#   ifndef PAGE_SIZE

#     define PAGE_SIZE 4096

#   endif

    /*

     * reserve memory for LCD display (always full pages)

     */

    /* bss_end is defined in the board-specific linker script */

    addr = (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);

    size = lcd_setmem (addr);

    gd->fb_base = addr;

#endif /* CONFIG_LCD */

    /* armboot_start is defined in the board-specific linker script */

    /*_armboot_start=0x33f80000,CFG_MALLOC_LEN=0x30000,mem_malloc_init对*/

    /*(0x33f80000-0x30000)~(0x33f80000)这段存储区清零*/

    mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);

#if (CONFIG_COMMANDS & CFG_CMD_NAND)

    puts ("NAND:");

nand_init();     /* go init the NAND 探测NAND flash并根据NAND flash的类型*/

/*填充相应的数据结构,全局结构体变量nand_dev_desc[0](类型struct nand_chip)*/

#endif

#ifdef CONFIG_HAS_DATAFLASH//CONFIG_HAS_DATAFLASH没有定义

    AT91F_DataflashInit();

    dataflash_print_info();

#endif

 

env_relocate ();

这个函数在u-boot-1.1.3\common\env_common.c中

 

void env_relocate (void)

{

    DECLARE_GLOBAL_DATA_PTR;

    DEBUGF ("%s[%d] offset = 0x%lx\n", __FUNCTION__,__LINE__,

       gd->reloc_off);

#ifdef CONFIG_AMIGAONEG3SE//没有定义

    enable_nvram();

#endif

#ifdef ENV_IS_EMBEDDED//没有定义

    /*

     * The environment buffer is embedded with the text segment,

     * just relocate the environment pointer

     */

    env_ptr = (env_t *)((ulong)env_ptr + gd->reloc_off);

    DEBUGF ("%s[%d] embedded ENV at %p\n", __FUNCTION__,__LINE__,env_ptr);

#else

    /*

     * We must allocate a buffer for the environment

     */

    env_ptr = (env_t *)malloc (CFG_ENV_SIZE);//为环境变量分配内存,env_ptr是全局变量,在下面的

//env_relocate_spec ()函数中将环境变量读取到env_ptr指向的内存中

    DEBUGF ("%s[%d] malloced ENV at %p\n", __FUNCTION__,__LINE__,env_ptr);

#endif

    /*

     * After relocation to RAM, we can always use the "memory" functions

     */

    env_get_char = env_get_char_memory;

/*在env_init函数中gd->env_valid被初始化为1*/

    if (gd->env_valid == 0) {//表达式为假

#if defined(CONFIG_GTH)  || defined(CFG_ENV_IS_NOWHERE)/* Environment not changable *///没有定义

       puts ("Using default environment\n\n");

#else

       puts ("*** Warning - bad CRC, using default environment\n\n");

       SHOW_BOOT_PROGRESS (-1);

#endif

       if (sizeof(default_environment) > ENV_SIZE)

       {

           puts ("*** Error - default environment is too large\n\n");

           return;

       }

       memset (env_ptr, 0, sizeof(env_t));

       memcpy (env_ptr->data,

           default_environment,

           sizeof(default_environment));

#ifdef CFG_REDUNDAND_ENVIRONMENT

       env_ptr->flags = 0xFF;

#endif

       env_crc_update ();

       gd->env_valid = 1;

    }

    else {

       env_relocate_spec ();/*该函数实现真正的重定位功能,先从NAND flash中读取环境变量,如果读取成*/

       /*功,并且crc校验正确的话,就使用NAND flash中读取出来的环境变量,否则使用默认的环境变量*/

    }

    gd->env_addr = (ulong)&(env_ptr->data);//填充结构体变量

   

#ifdef CONFIG_AMIGAONEG3SE//没有定义

    disable_nvram();

#endif

}

现在进入函数env_relocate_spec ()

void env_relocate_spec (void)//u-boot-1.1.3\common\env_nand.c

{

#if !defined(ENV_IS_EMBEDDED)

    int ret, total;

    //从nand flash 中读取环境变量到全局变量env_ptr指向的内存中,这里需要注意的是CFG_ENV_OFFSET、//CFG_ENV_SIZE这两个宏定义,它们表示从NAND flash中偏移为CFG_ENV_OFFSET处读取大小为CFG_ENV_SIZE字节//的数据,注意这两个宏定义决定了环境变量在nand flash中保存的位置和大小,不能和nand flash 中的其他分区(kernel分区、rootfs分区)相冲突,不然在执行saveenv命令时可能会覆盖其他分区的内容,导致系统无法启动.

    ret = nand_rw(nand_dev_desc + 0,

                NANDRW_READ | NANDRW_JFFS2, CFG_ENV_OFFSET,CFG_ENV_SIZE,

                 &total, (u_char*)env_ptr);

    if (ret || total != CFG_ENV_SIZE)//ret=0,total==CFG_ENV_SIZE=65536

       return use_default();//使用默认的环境变量

    if (crc32(0, env_ptr->data, ENV_SIZE) != env_ptr->crc){

       puts(" CRC ERROR!!!!!!!!!! \r\n");

       return use_default();

    }

#endif /* ! ENV_IS_EMBEDDED */

}

到此env_relocate ()结束.

 

#ifdef CONFIG_VFD//宏CONFIG_VFD没有定义

    /* must do this after the framebuffer is allocated */

    drv_vfd_init();

#endif /* CONFIG_VFD */

    /* IP Address */

    gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");//从环境变量获取ip地址并填充gd结构体变量

 

    /* MAC Address */

    {

       int i;

       ulong reg;

       char *s, *e;

       uchar tmp[64];

       /*从环境变量获取网卡的MAC地址并填充gd结构体变量 */

       i = getenv_r ("ethaddr", tmp, sizeof (tmp));

       s = (i > 0) ? tmp : NULL;

       for (reg = 0; reg < 6; ++reg) {

           gd->bd->bi_enetaddr[reg] = s ? simple_strtoul (s, &e, 16) : 0;

           if (s)

              s = (*e) ? e + 1 : e;

       }

    }

    /*对设备进行初始化*/

    devices_init ();  /* get the devices list going. *///注册串口设备

devices_init ()在u-boot-1.1.3\common\devices.c文件中.

int devices_init (void)

{

#ifndef CONFIG_ARM    /* already relocated for current ARM implementation *///CONFIG_ARM已定义

    DECLARE_GLOBAL_DATA_PTR;

    ulong relocation_offset = gd->reloc_off;

    int i;

    /* relocate device name pointers */

    for (i = 0; i < (sizeof (stdio_names) / sizeof (char *)); ++i) {

       stdio_names[i] = (char *) (((ulong) stdio_names[i]) +

                     relocation_offset);

    }

#endif

    /* Initialize the list */

    devlist = ListCreate (sizeof (device_t));

 

    if (devlist == NULL) {

       eputs ("Cannot initialize the list of devices!\n");

       return -1;

    }

#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C)//not defined

    i2c_init (CFG_I2C_SPEED, CFG_I2C_SLAVE);

#endif

#ifdef CONFIG_LCD//not defined

    drv_lcd_init ();

#endif

#if defined(CONFIG_VIDEO) || defined(CONFIG_CFB_CONSOLE)//not defined

    drv_video_init ();

#endif

#ifdef CONFIG_KEYBOARD//not defined

    drv_keyboard_init ();

#endif

#ifdef CONFIG_LOGBUFFER//not defined

    drv_logbuff_init ();

#endif

    drv_system_init ();

#ifdef CONFIG_SERIAL_MULTI//not defined

    serial_devices_init ();

#endif

#ifdef CONFIG_USB_TTY//not defined

    drv_usbtty_init ();

#endif

#ifdef CONFIG_NETCONSOLE//not defined

    drv_nc_init ();

#endif

    return (0);

}

drv_system_init ()这个函数对串口设备初始化,然后注册串口设备.

 

继续往下

    /*跳转表初始化,函数在u-boot-1.1.3\common\exports.c文件中*/

    jumptable_init ();

/*console_init_r函数在u-boot-1.1.3\common\console.c文件中*/

    console_init_r ();   /* fully init console as a device */

 

#if defined(CONFIG_MISC_INIT_R)//没有定义

    /* miscellaneous platform dependent initialisations */

    misc_init_r ();

#endif

    /* enable exceptions */

    enable_interrupts ();//使能中断

    /* Perform network card initialisation if necessary */

#ifdef CONFIG_DRIVER_CS8900

    cs8900_get_enetaddr (gd->bd->bi_enetaddr);//获取网卡的MAC地址

#endif

 

#if defined(CONFIG_DRIVER_SMC91111) || defined (CONFIG_DRIVER_LAN91C96)//没有定义

    if (getenv ("ethaddr")) {

       smc_set_mac_addr(gd->bd->bi_enetaddr);

    }

#endif /* CONFIG_DRIVER_SMC91111 || CONFIG_DRIVER_LAN91C96 */

 

    /* Initialize from environment */

    if ((s = getenv ("loadaddr")) != NULL) {

       load_addr = simple_strtoul (s, NULL, 16);//读取环境变量loadaddr到全局变量load_addr中

    }

#if (CONFIG_COMMANDS & CFG_CMD_NET)

    if ((s = getenv ("bootfile")) != NULL) {

        copy_filename (BootFile, s, sizeof (BootFile));//读取环境变量bootfile到

//全局变量BootFile中

    }

#endif /* CFG_CMD_NET */

   

#ifdef BOARD_LATE_INIT//没有定义

    board_late_init ();

#endif

#if (CONFIG_COMMANDS & CFG_CMD_NET) && defined(CONFIG_NET_MULTI)//没有定义

    puts ("Net:   ");

    eth_initialize(gd->bd);

#endif

    /* main_loop() can return to retry autoboot, if so just run it again. */

    for (;;) {

       main_loop ();//进入主循环

    }

SDRAM 内存分配图



************************重点bd_t   gd_t 及board.c的分析***************************

gd_t和bd_t是u-boot中两个重要的数据结构,在初始化操作很多都要靠这两个数据结构来保存或传递.分别定义在./include/asm/global_data.h和./include/asm/u_boot.h

1.gd_t: global data数据结构定义,位于文件 include/asm-arm/global_data.h。

成员主要是一些全局的系统初始化参数。需要用到时用宏定义:DECLARE_GLOBAL_DATA_PTR,指定占用寄存器R8。
2.bd_t : board info数据结构定义,位于文件 include/asm-arm/u-boot.h。保存板子参数。

#ifndef __ASM_GBL_DATA_H
#define __ASM_GBL_DATA_H


typedef struct global_data
{
bd_t  *bd;                                   //开发板相关参数,结构体变量,参考u-boot.h
unsigned long flags;                     //指示标志,如设备已经初始化标志等
unsigned long baudrate;               //串行口通讯速率
unsigned long have_console;      
unsigned long reloc_off;            
unsigned longenv_addr;              
unsigned longenv_valid;            
unsigned longfb_base;                

#ifdef CONFIG_VFD
unsigned charvfd_type;              
#endif

#if  0     
unsigned long cpu_clk;              
unsigned long bus_clk;               // 总线时钟
unsigned long ram_size;            
unsigned long reset_status;      
#endif

void   **jt;                                
}gd_t;
#define GD_FLG_RELOC 0x00001            
#define GD_FLG_DEVINIT 0x00002        
#define GD_FLG_SILENT 0x00004          
#define DECLARE_GLOBAL_DATA_PTR     register volatile gd_t *gd asm ("r8")
#endif        

typedef struct bd_info {
int                   bi_baudrate;        
unsigned long         bi_ip_addr;        
unsigned char         bi_enetaddr[6];    
struct environment_s  *bi_env;            //结构体变量定义见46行
ulong                 bi_arch_number;  
ulong                 bi_boot_params;  
struct                                  
{
ulong start;
ulong size;
} bi_dram[CONFIG_NR_DRAM_BANKS];
}bd_t;
=====================================================================
=====================================================================

下面是lib_arm\board.c文件分析  
typedef int (init_fnc_t) (void); //自定义数据类型

init_fnc_t   *init_sequence[]= {
cpu_init,              
board_init,            
interrupt_init,        
env_init,              
init_baudrate,        
serial_init,          
console_init_f,        
display_banner,        
dram_init,            
display_dram_config,      //显示RAM的配置大小 -- 
                          lib_arm/board.c 

#if defined(CONFIG_VCMA9)
checkboard,
#endif

NULL,
};

//整个u-boot的执行就进入等待用户输入命令,解析并执行命令的死循环中。
//start_armboot是U-Boot执行的第一个C语言函数,完成系统初始化工作,进入主循环,处理用户输入的命令。

void start_armboot (void)
{
DECLARE_GLOBAL_DATA_PTR;   
        ulong size;
init_fnc_t**init_fnc_ptr;            //这个是函数的指针,指向==>硬件初始化的函数
char *s;

#if defined(CONFIG_VFD)
unsigned long addr;
#endif


#if CFG_LED_FLASH
LED();
#endif
gd = (gd_t*)(_armboot_start -CFG_MALLOC_LEN-sizeof(gd_t)); //计算出gd在RAM中的地址
memset ((void*)gd, 0, sizeof (gd_t));                   //给全局数据变量gd安排空间,用0填充全局数据表*gd

gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));                    //给板子数据变量gd->bd安排空间
memset (gd->bd, 0, sizeof (bd_t));                                   //gd区包含了bd区,                                                    gd_t,bd_t都是结构体变量,
//用0填充(初始化) *gd->bd board info数据结构定义,位于文件 include/asm-arm/u-boot.h
monitor_flash_len = _bss_start - _armboot_start;      // 取u-boot的长度

for (init_fnc_ptr = init_sequence;    *init_fnc_ptr;     ++init_fnc_ptr)
{
if ((*init_fnc_ptr)() != 0) {
hang ();      //打印错误信息并死锁
}
}
size = flash_init ();                      //drivers/cfi_flash.c或自定义
display_flash_config (size);
#ifdef CONFIG_VFD                        //如果定义了VFD(真空荧光显示),就定义页面大小为4096B
# ifndef PAGE_SIZE
#   define PAGE_SIZE 4096
# endif


addr = (_bss_start + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
size = vfd_setmem (addr);
gd->fb_base = addr;     //全局数据gd中定义的帧缓冲区的基地址
#endif

mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);            //malloc使用的内存空间地址  0x0c700000-129k(0x00080400)
#if (CONFIG_COMMANDS & CFG_CMD_NAND)              //如果有nandflash的话就在下面的代码中进行初  始化
puts ("NAND:");
nand_init();          
#endif
#ifdef CONFIG_HAS_DATAFLASH
AT91F_DataflashInit();
dataflash_print_info();
#endif
env_relocate ();
#ifdef CONFIG_VFD
drv_vfd_init();           //vfd初始化,在分配帧缓冲区之后必须的
#endif
gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");
{
int       i;
ulong    reg;
char      *s,    *e;
uchar    tmp[64];
         i = getenv_r ("ethaddr", tmp, sizeof (tmp));
s =  (i  >  0)  ?  tmp  :  NULL;
         for (reg = 0;   reg < 6;   ++reg){
gd->bd->bi_enetaddr[reg] = s ? simple_strtoul (s, &e, 16) : 0;
if (s)
s = (*e) ? e + 1 : e;          
}
}
devices_init ();              
jumptable_init ();          //跳转表初始化
console_init_r ();          
#if defined(CONFIG_MISC_INIT_R)
misc_init_r ();
#endif
enable_interrupts ();
。。。。。。
for  ( ;  ; ){
main_loop ();      //主循环函数处理执行用户命令 -- common/main.c
}
}
void hang (void)
{
puts ("### ERROR ### Please RESET the board ###\n");     //输出错误信息需要

reset,进入死循环
for (  ;  ;  );
}
 

bd_t <wbr> <wbr> <wbr>gd_t <wbr>及board.c的分析