现在的位置: 首页 > 综合 > 正文

uboot启动第二阶段详细分析(1)

2018年03月18日 ⁄ 综合 ⁄ 共 29531字 ⁄ 字号 评论关闭

先了解个大体流程 :                                                               

                                                                                                      开始 

在上一篇文章中,我uboot第二阶段代码分析 - 小白 - 小白的博客

们介绍了u-boot启动的时候汇编语言的部分,当时我们进行了一些简单的初始化,并且为C语言的执行建立的环境(堆栈),现在我们看看当从汇编语言转到C语言的时候执行的第一个函数( start_armboot (),在lib_arm\board.c中),该函数进行了一系列的外设初始化,然后调用main_loop (),根据配置来选择是直接加载Linux内核还是进入等待命令模式。


1、在介绍start_armboot ()函数之前,我们需要看一看几个数据结构,这些是u-boot中几个重要的数据结构:

1)bd_t 结构体:保存与板子相关的配置参数

bd_t在include/asm-arm/u-boot.h中定义如下:

 ********************************************************************
 * NOTE: This header file defines an interface to U-Boot. Including
 * this (unmodified) header file in another file is considered normal
 * use of U-Boot, and does *not* fall under the heading of "derived
 * work".
 ********************************************************************
 */

#ifndef _U_BOOT_H_
#define _U_BOOT_H_    1

typedef struct bd_info {
    int            bi_baudrate;    /* serial console baudrate   串口通讯波特率 */
    unsigned long    bi_ip_addr;    /* IP Address IP地址 */
    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  RAM配置信息 */
    {
    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;

#define bi_env_data bi_env->data
#define bi_env_crc  bi_env->crc

#endif    /* _U_BOOT_H_ */

2)gd_t 结构体:U-Boot使用了一个结构体gd_t来存储全局数据区的数据,
    这个结构体在include/asm-arm/global_data.h中定义如下:

#ifndef    __ASM_GBL_DATA_H
#define __ASM_GBL_DATA_H
/*
 * The following data structure is placed in some memory wich is
 * available very early after boot (like DPRAM on MPC8xx/MPC82xx, or
 * some locked parts of the data cache) to allow for a minimum set of
 * global variables during system initialization (until we have set
 * up the memory controller so that we can use RAM).
 *
 * Keep it *SMALL* and remember to set CFG_GBL_DATA_SIZE > sizeof(gd_t)
 */

typedef    struct    global_data {
    bd_t        *bd;        //board data pointor板子数据指针
    unsigned long    flags;     //指示标志,如设备已经初始化标志等。
    unsigned long    baudrate;      //串口波特率
    unsigned long    have_console;    / *serial_init() was called   /* 串口初始化标志*/
    unsigned long    reloc_off;    /* Relocation Offset */  /* 重定位偏移,就是实际定向的位置与编译连接时指定的位置之差,一般为0

    unsigned long    env_addr;    /* Address  of Environment struct */    
/* 环境参数地址*/

    unsigned long    env_valid;    /* Checksum of Environment valid? */   
/* 环境参数CRC检验有效标志 */

    unsigned long    fb_base;    /* base address of frame buffer */
#ifdef CONFIG_VFD         //我们一般没有配置这个,这个是frame buffer的首地址
    unsigned char    vfd_type;    /* display type */
#endif
#if 0
    unsigned long    cpu_clk;    /* CPU clock in Hz!        */
    unsigned long    bus_clk;
    unsigned long    ram_size;    /* RAM size */
    unsigned long    reset_status;    /* reset status register at boot */
#endif
    void        **jt;        /* jump table */   /* 跳转表,1.1.6中用来函数调用地址登记 */
} gd_t;

/*
 * Global Data Flags
 */
#define    GD_FLG_RELOC    0x00001        /* Code was relocated to RAM        */
#define    GD_FLG_DEVINIT    0x00002        /* Devices have been initialized    */
#define    GD_FLG_SILENT    0x00004        /* Silent mode                */

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

#endif /* __ASM_GBL_DATA_

看点:
1. 在global_data.h中U-Boot使用了一个存储在寄存器中的指针gd来记录全局数据区的地址
2. #define DECLARE_GLOBAL_DATA_PTR     register volatile gd_t *gd asm ("r8")
        DECLARE_GLOBAL_DATA_PTR定义一个gd_t全局数据结构的指针,这个指针存放在指定的寄存器r8中。
        这个声明也避免编译器把r8分配给其它的变量。任何想要访问全局数据区的代码,只要代码开头加入“DECLARE_GLOBAL_DATA_PTR”一行代码,然后就可以使用gd指针来访问全局数据区了。

3. 根据U-Boot内存使用图中可以计算gd的值:

gd = TEXT_BASE -CONFIG_SYS_MALLOC_LEN - sizeof(gd_t)

           u-boot重定位后的内存分布:
   对于smdk2410,RAM范围从0x30000000~0x34000000. u-boot占用高端内存区。从高地址到低地址内存分配如下:


 显示缓冲区                (.bss_end~34000000)
     u-boot(bss,data,text)  (33f00000~.bss_end)
     heap(for malloc)
     gd(global data)
     bd(board data)
     stack                        
     ....
     nor flash                      (0~2M)

http://blog.chinaunix.net/attachment/201203/23/14114479_1332486694smIZ.jpg

U-Boot启动内核时要给内核传递参数,这时就要使用gd_t,bd_t结构体中的信息来设置标记列表。

3). init_sequence数组, 初始化函数列表(以数组的形式)

相关代码在lib-arm/board.c中定义

typedef int (init_fnc_t) (void);    /*定义一个函数指针类型*/

int print_cpuinfo (void); /* test-only */

  /*下面定义一个函数指针数组,每个函数指针指向一个初始化处理器的函数*/

init_fnc_t *init_sequence[] = {   
    cpu_init,        /* basic cpu dependent setup  CPU的相关配置,如初始化IRQ/FIQ模式的栈cpu/arm920t/cpu.c */
    board_init,        /* basic board dependent setup开发板相关配置,是对板子的初始化,设置MPLL,改变系统时钟,以及一些GPIO寄存器的值,

                                                                                          还设置了U-Boot机器码和内核启动参数地址,它是开发板相关的函数,

                                                                                            比如2410是在:board/smdk2410/smdk2410.c中实现 */
    interrupt_init,        /* set up exceptions   初始化中断,在cpu/arm920t/s3c24x0/interrupts.c实现 */
    env_init,        /* initialize environment    初始化环境变量,检查flash上的环境变量是否有效common/env_flash.c 或common/env_nand.c*/
    init_baudrate,        /* initialze baudrate settings  初始化波特率lib_arm/board.c*/
    serial_init,        /* serial communications setup  串口初始化串口初始化后我们就可以打印信息了cpu/arm920t/s3c24x0/serial.c*/
    console_init_f,        /* stage 1 init of console  控制台初始化第一阶段common/console.c
*/
    display_banner,        /* say that we are here  通知代码已经运行到该处,打印U-Boot版本、编译的时间-- lib_arm/board.c */
#if defined(CONFIG_DISPLAY_CPUINFO)
    print_cpuinfo,        /* display cpu info (and speed) */
#endif
#if defined(CONFIG_DISPLAY_BOARDINFO)
    checkboard,        /* display board info */
#endif
    dram_init,        /* configure available RAM banks  配置可用的内存区,检测系统内存映射,设置内存的起始地址和大小board/smdk2410/smdk2410.c */
    display_dram_config,    /*显示RAM大小--lib_arm/board.c*/
    NULL,
};

 4)环境变量指针 env_t *env_ptr = (env_t *)(&environment[0]);(common/env_flash.c)
   env_ptr指向环境参数区,系统启动时默认的环境参数environment[],定义在common/environment.c中。 
   参数解释
    bootdelay 定义执行自动启动的等候秒数
    baudrate 定义串口控制台的波特率
    netmask 定义以太网接口的掩码
    ethaddr 定义以太网接口的MAC地址
    bootfile 定义缺省的下载文件
    bootargs 定义传递给Linux内核的命令行参数
    bootcmd 定义自动启动时执行的几条命令
    serverip 定义tftp服务器端的IP地址
    ipaddr 定义本地的IP地址
    stdin 定义标准输入设备,一般是串口
    stdout 定义标准输出设备,一般是串口
    stderr 定义标准出错信息输出设备,一般是串口
  4)设备相关
   标准IO设备数组evice_t *stdio_devices[] = { NULL, NULL, NULL };
   设备列表    list_t    devlist = 0;
   device_t的定义:include/devices.h中:
    typedef struct {
     int flags;          /* Device flags: input/output/system */
     int ext;           /* Supported extensions   */
     char name[16];        /* Device name    */    
    /* GENERAL functions */    
     int (*start) (void);     /* To start the device   */
     int (*stop) (void);      /* To stop the device   */    
    /* 输出函数 */    
     void (*putc) (const char c); /* To put a char   */
     void (*puts) (const char *s); /* To put a string (accelerator) */   
    /* 输入函数 */   
     int (*tstc) (void);      /* To test if a char is ready... */
     int (*getc) (void);      /* To get that char   */   
    /* Other functions */    
     void *priv;          /* Private extensions   */
    } device_t;
   u-boot把可以用为控制台输入输出的设备添加到设备列表devlist,并把当前用作标准IO的设备指针加入stdio_devices数组中。
   在调用标准IO函数如printf()时将调用stdio_devices数组对应设备的IO函数如putc()。
     5)命令相关的数据结构,后面介绍。
     6)与具体设备有关的数据结构
      如flash_info_t flash_info[CFG_MAX_FLASH_BANKS];记录nor flash的信息。
      nand_info_t nand_info[CFG_MAX_NAND_DEVICE]; nand flash块设备信息


上边的函数我们会在后面详细解释,下面看start_armboot 函数的框架


2.
start_armboot ()
函数详细解释:lib_arm\board.c

void start_armboot (void)
{
    init_fnc_t **init_fnc_ptr;    /*它是指向指针的指针变量,指针变量的类型为init_fnc_t,这是一个使用typedef定义的函数类型, 其中init_fnc_ptr将在后面指向一个数组指针*/
    char *s;
#ifndef CFG_NO_FLASH
    ulong size;
#endif
#if defined(CONFIG_VFD) || defined(CONFIG_LCD)
    unsigned long addr;
#endif

    /* Pointer is writable since we allocated a register for it */
    gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));     /*_armboot_start: .word _start  计算全局数据结构的地址gd(gd是一个全局静态变量)*/
    /* compiler optimization barrier needed for GCC >= 3.4 */
    __asm__ __volatile__("": : :"memory");

    memset ((void*)gd, 0, sizeof (gd_t));   //初始化全局数据区数据结构为0
  gd在哪里定义的  还有gd是个结构体变量啊??

    gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));    //为bd_t分配空间,并赋值到gd
    memset (gd->bd, 0, sizeof (bd_t));  //初始化 bd 表为0;

    monitor_flash_len = _bss_start - _armboot_start;     /*monitor_falsh_len定义在 /lib_arm/Board.c      _bss_start(BSS段基址) = 代码段长度+数据段长度*/


/*下面是 逐个调用init_sequence数组中的初始化函数,具体函数分析在后面,现只是看框架  */

    for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
        if ((*init_fnc_ptr)() != 0) {
            hang ();      //函数功能是打印提示### ERROR ### Please RESET the board ###,然后就无限循环,等待重启
        }
    }

/* CFG_NO_FLASH 表示没有flash,如果没定义该常量则表示板子上有flash,此时调用flash_init()对其进行初始化.这里是nor flash初始化 */
#ifndef CFG_NO_FLASH
    /* configure available FLASH banks */
    size = flash_init ();   //详见后分析
    display_flash_config (size);    ////打印flash的信息,仅是其大小
#endif /* CFG_NO_FLASH */

#ifdef CONFIG_VFD    //smdk2410没定义
#    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
#    ifndef PAGE_SIZE
#      define PAGE_SIZE 4096
#    endif
    /*
     * reserve memory for LCD display (always full pages)  
//为LCD分配RAM(内存)空间     */
    /* 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 */
#endif /* CONFIG_LCD */


/* 初始化堆空间 */

    /* armboot_start is defined in the board-specific linker script */
    mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);     
//malloc函数使用缓冲区的初始化    

                                                             //_  armboot_start=TEXT_BASE,,mem_malloc_init对这段存储区清零

#if (CONFIG_COMMANDS & CFG_CMD_NAND)    //如果定义了命令和NAND命令,则初始化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
    AT91F_DataflashInit();
    dataflash_print_info();
#endif

/* 重新定位环境变量, */
    /* initialize environment
配置环境变量,重新定位,代码在common\env_common.c中
*/
    env_relocate ();

#ifdef 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地址 
, ipaddr在smdk2440.h中的CONFIG_IPADDR中出现,应该是该常量 

    /* MAC Address   从环境变量获取网卡的MAC地址并填充gd结构体变量 */
    {
        int i;
        ulong reg;
        char *s, *e;
        char 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;
        }

#ifdef CONFIG_HAS_ETH1
        i = getenv_r ("eth1addr", tmp, sizeof (tmp));
        s = (i > 0) ? tmp : NULL;

        for (reg = 0; reg < 6; ++reg) {
            gd->bd->bi_enet1addr[reg] = s ? simple_strtoul (s, &e, 16) : 0;
            if (s)
                s = (*e) ? e + 1 : e;
        }
#endif
    }

/* 设备初始化 */
    devices_init ();    /* get the devices list going. 对设备初始化,在common/devices.c文件中,会根据配置来完成各个设备的初始化,

                                                                                                          其中会调用函数drv_system_init(),这个
函数对串口设备初始化,然后注册串口设备*/

#ifdef CONFIG_CMC_PU2      //没定义
    load_sernum_ethaddr ();
#endif /* CONFIG_CMC_PU2 */

//跳转表初始化
    jumptable_init ();  //跳转表初始化,函数在common\exports.c文件中 
对gd中的jt(函数跳转表)数组进行初始化,其中保存着一些函数的入口地址

/* 完整地初始化控制台设备 */

    console_init_r ();    /* fully init console as a device  函数在common\console.c */

#if defined(CONFIG_MISC_INIT_R)   //没有定义
    /* miscellaneous platform dependent initialisations */
    misc_init_r ();
#endif

/* 使能中断处理 */
    /* enable exceptions  使能中断,cpu/arm920t/interrupts.c smdk2410是个空函数void enable_interrupts (void){return;}*/
    enable_interrupts ();

    /* Perform network card initialisation if necessary */
#ifdef CONFIG_DRIVER_CS8900     /*smdk2410没定义*/
    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)
#if defined(CONFIG_NET_MULTI)     // smdk2410没定义
    puts ("Net:   ");
#endif
    eth_initialize(gd->bd);
#endif


    /* main_loop() can return to retry autoboot, if so just run it again. */
    for (;;) {
        main_loop ();   //直接进入main_loop 该函数在common\main.c中,至此,硬件初始化完成
    }

    /* NOTREACHED - no way out of command loop except booting */
}   //end start_armboot()

3. init_sequence数组中各个函数的具体分析

     cpu_init :

功能:初始化IRQ/FIQ模式的栈

位置:cpu/arm920t/cpu.c */

int cpu_init (void)
{
	/*
	 * setup up stacks if necessary
	 */
#ifdef CONFIG_USE_IRQ
	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;
}


    board_init

功能:是对板子的初始化,设置MPLL,改变系统时钟,以及一些GPIO寄存器的值,使能I/D cache,还设置了U-Boot机器码和内核启动参数地址,它是开发板相关的函数

位置:board/smdk2410/smdk2410.c

int board_init (void)
{

//获取power和clock及GPIO方面的寄存器地址,稍后的操作会对这些寄存器操作,需要看到的是,象S3C24X0_CLOCK_POWER里面的field对象都是按照实际寄存器的地址来安排的

    S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER();
    S3C24X0_GPIO * const gpio = S3C24X0_GetBase_GPIO();

//降低PLL的lock time的值
    /* to reduce PLL lock time, adjust the LOCKTIME register */
    clk_power->LOCKTIME = 0xFFFFFF;

//配置MPLL UPLL
    /* configure MPLL */
    clk_power->MPLLCON = ((M_MDIV << 12) + (M_PDIV << 4) + M_SDIV);

    /* some delay between MPLL and UPLL */
    delay (4000);

    /* configure UPLL */
    clk_power->UPLLCON = ((U_M_MDIV << 12) + (U_M_PDIV << 4) + U_M_SDIV);

    /* some delay between MPLL and UPLL */
    delay (8000);

/* 配置每个GPIO的功能,输入输出,等参数 */
    /* set up the I/O ports */
    gpio->GPACON = 0x007FFFFF;
    gpio->GPBCON = 0x00044555;
    gpio->GPBUP = 0x000007FF;
    gpio->GPCCON = 0xAAAAAAAA;
    gpio->GPCUP = 0x0000FFFF;
    gpio->GPDCON = 0xAAAAAAAA;
    gpio->GPDUP = 0x0000FFFF;
    gpio->GPECON = 0xAAAAAAAA;
    gpio->GPEUP = 0x0000FFFF;
    gpio->GPFCON = 0x000055AA;
    gpio->GPFUP = 0x000000FF;
    gpio->GPGCON = 0xFF95FFBA;
    gpio->GPGUP = 0x0000FFFF;
    gpio->GPHCON = 0x002AFAAA;
    gpio->GPHUP = 0x000007FF;


 /* SMDK2410开发板的机器码即板子的ID,没啥意义 */ */
    /* arch number of SMDK2410-Board */
    gd->bd->bi_arch_number = MACH_TYPE_SMDK2410;


/* 内核启动参数地址,运行时在linux内核之下/
    /* adress of boot parameters */
    gd->bd->bi_boot_params = 0x30000100;

//使能指令cache和数据cache

    icache_enable();
    dcache_enable();

    return 0;
}

    interrupt_init
功能:初始化2410的PWM timer 4,使其能自动装载计数值
用于产生10ms定时中断,但是中断被屏蔽了用不上

位置:cpu/arm920t/s3c24x0/interrupts.c


int interrupt_init (void)
{
    S3C24X0_TIMERS * const timers = S3C24X0_GetBase_TIMERS();    //返回定时器配置寄存器地址0x51000000,即TCFG0寄存器地址

 /*这里使用定时器4,定时器4有只有一个内部定器没有输出管脚*/
    /* use PWM Timer 4 because it has no output */
    /* prescaler for Timer 4 is 16 */
    timers->TCFG0 = 0x0f00;
    if (timer_load_val == 0)
    {
        /*
         * for 10 ms clock period @ PCLK with 4 bit divider = 1/2
         * (default) and prescaler = 16. Should be 10390
         * @33.25MHz and 15625 @ 50 MHz
         */
        timer_load_val = get_PCLK()/(2 * 16 * 100);    //PCLK(返回PCLK频率
    }
    /* load value for 10 ms timeout */
    lastdec = timers->TCNTB4 = timer_load_val;    //设置计数缓存寄存器初始值

 /*设置定时器4手动更新,自动加载模式,并关闭定时器4*/
    /* auto load, manual update of Timer 4 */
    timers->TCON = (timers->TCON & ~0x0700000) | 0x600000;

 /*  启动Timer 4 */
    /* auto load, start Timer 4 */
    timers->TCON = (timers->TCON & ~0x0700000) | 0x500000;
    timestamp = 0;

    return (0);
}


    env_init

  位置:common/env_flash.c 或common/env_nand.c

功能:指定环境区的地址 初始化环境变量,检查flash上的环境变量是否有效common/env_flash.c 或common/env_nand.c

int  env_init(void)
{
    int crc1_ok = 0, crc2_ok = 0;

    uchar flag1 = flash_addr->flags;
    uchar flag2 = flash_addr_new->flags;

    ulong addr_default = (ulong)&default_environment[0];     //default_environment是默认的环境参数设置
    ulong addr1 = (ulong)&(flash_addr->data);
    ulong addr2 = (ulong)&(flash_addr_new->data);

#ifdef CONFIG_OMAP2420H4
    int flash_probe(void);

    if(flash_probe() == 0)
        goto bad_flash;
#endif

    crc1_ok = (crc32(0, flash_addr->data, ENV_SIZE) == flash_addr->crc);
    crc2_ok = (crc32(0, flash_addr_new->data, ENV_SIZE) == flash_addr_new->crc);

    if (crc1_ok && ! crc2_ok) {
        gd->env_addr  = addr1;
        gd->env_valid = 1;
    } else if (! crc1_ok && crc2_ok) {
        gd->env_addr  = addr2;
        gd->env_valid = 1;
    } else if (! crc1_ok && ! crc2_ok) {
        gd->env_addr  = addr_default;
        gd->env_valid = 0;
    } else if (flag1 == ACTIVE_FLAG && flag2 == OBSOLETE_FLAG) {
        gd->env_addr  = addr1;
        gd->env_valid = 1;
    } else if (flag1 == OBSOLETE_FLAG && flag2 == ACTIVE_FLAG) {
        gd->env_addr  = addr2;
        gd->env_valid = 1;
    } else if (flag1 == flag2) {
        gd->env_addr  = addr1;
        gd->env_valid = 2;
    } else if (flag1 == 0xFF) {
        gd->env_addr  = addr1;
        gd->env_valid = 2;
    } else if (flag2 == 0xFF) {
        gd->env_addr  = addr2;
        gd->env_valid = 2;
    }

#ifdef CONFIG_OMAP2420H4
bad_flash:
#endif
    return (0);
}

 

init_baudrate

功能://从环境变量中获取波特率值,初始化全局数据区波特率

位置:lib_arm/board.c


static int init_baudrate (void)
{
    char tmp[64];    /* long enough for environment variables */
    int i = getenv_r ("baudrate", tmp, sizeof (tmp));
    gd->bd->bi_baudrate = gd->baudrate = (i > 0)
            ? (int) simple_strtoul (tmp, NULL, 10)
            : CONFIG_BAUDRATE;       //从上面刚初始化好的环境变量列表里找波特率值,如果没有就赋初始值为CONFIG_BAUDRATE

    return (0);
}


    serial_init

位置:cpu/arm920t/s3c24x0/serial.c*/

功能:根据gd中波特率值和pclk,配置串口控制器,配置好后就可以打印信息了

int serial_init (void)
{
    serial_setbrg ();

    return (0);
}

void serial_setbrg (void)
{
    S3C24X0_UART * const uart = S3C24X0_GetBase_UART(UART_NR);
    int i;
    unsigned int reg = 0;

    /* value is calculated so : (int)(PCLK/16./baudrate) -1 */
    reg = get_PCLK() / (16 * gd->baudrate) - 1;

    /* FIFO enable, Tx/Rx FIFO clear */
    uart->UFCON = 0x07;
    uart->UMCON = 0x0;
    /* Normal,No parity,1 stop,8 bit */
    uart->ULCON = 0x3;
    /*
     * tx=level,rx=edge,disable timeout int.,enable rx error int.,
     * normal,interrupt or polling
     */
    uart->UCON = 0x245;
    uart->UBRDIV = reg;

#ifdef CONFIG_HWFLOW
    uart->UMCON = 0x1; /* RTS up */
#endif
    for (i = 0; i < 100; i++);
}


    console_init_f

位置:common/console.c

功能: 控制台初始化第一阶段,由于标准设备还没有初始化这时控制台使用串口作为控制台

/* Called before relocation - use serial functions */
int console_init_f (void)
{
    gd->have_console = 1;

#ifdef CONFIG_SILENT_CONSOLE
    if (getenv("silent") != NULL)
        gd->flags |= GD_FLG_SILENT;   //设置控制台模式
#endif

    return (0);
}


    display_banner,        /* say that we are here  通知代码已经运行到该处,打印U-Boot版本、编译的时间--

位置:lib_arm/board.c */

功能:打印U-Boot版本、编译的时间等信息,通知使用者代码已经运行到该处

static int display_banner (void)
{
    printf ("\n\n%s\n\n", version_string);    //打印U-BOOT的版本信息
    debug ("U-Boot code: %08lX -> %08lX  BSS: -> %08lX\n",
           _armboot_start, _bss_start, _bss_end);    //打印U-BOOT代码位置
#ifdef CONFIG_MODEM_SUPPORT
    debug ("Modem Support enabled\n");
#endif
#ifdef CONFIG_USE_IRQ
    debug ("IRQ Stack: %08lx\n", IRQ_STACK_START);
    debug ("FIQ Stack: %08lx\n", FIQ_STACK_START);
#endif

    return (0);
}


#if defined(CONFIG_DISPLAY_CPUINFO)
    print_cpuinfo,        /* display cpu info (and speed) */
#endif
#if defined(CONFIG_DISPLAY_BOARDINFO)
    checkboard,        /* display board info */
#endif


    dram_init,          

位置:board/smdk2410/smdk2410.c

功能:给gd->bd中内存信息表赋值,配置可用的内存区,检测系统内存映射,设置内存的起始地址和大小

int dram_init (void)
{

    gd->bd->bi_dram[0].start = PHYS_SDRAM_1;     //RAM起始地址   PHYS_SDRAM_1 PHYS_SDRAM_1_SIZE这两个宏定义在相应的地板头文件里,include/configs/smdk2410.h
    gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE;       //RAM大小

    return 0;
}


    display_dram_config

位置:lib_arm/board.c

功能:打印RAM的信息

static int display_dram_config (void)
{
    int i;

#ifdef DEBUG
    puts ("RAM Configuration:\n");

    for(i=0; i<CONFIG_NR_DRAM_BANKS; i++) {
        printf ("Bank #%d: %08lx ", i, gd->bd->bi_dram[i].start);
        print_size (gd->bd->bi_dram[i].size, "\n");
    }
#else
    ulong size = 0;

    for (i=0; i<CONFIG_NR_DRAM_BANKS; i++) {
        size += gd->bd->bi_dram[i].size;
    }
    puts("DRAM:  ");
    print_size(size, "\n");
#endif

    return (0);
}

init_sequence数组中各个函数的具体分析现已结束

这样整个初始化函数表的函数都看完了,总结一下主要做了如下过程:

1. cpu, borad, interrupt的初始化,包括cache等,这些都于特定板子的配置有关。

2. 环境变量的初始化,

3. 串口,控制台,RAM的初始化,

4. 在控制台上实时的显示系统配置等相关参数。

最后需要说明的是,大部分的配置参数都是预先在include/configs/board_name.h下定义的,因此如果我们要移植我们自己的板子的话,这个文件必不可少,它描述了我们板子的配置情况如CPU型号,RAM大小等。

4. start_armboot函数中init_sequence数组之后的后续函数具体分析

flash_init

位置:board/smdk2410/flash.c

功能:nor
flash数据结构初始化 ,记录下flash的大小,数量,sector的大小数量等,并对flash上重要的数据进行保护

ulong flash_init (void)
{
    int i, j;
    ulong size = 0;

    for (i = 0; i < CFG_MAX_FLASH_BANKS; i++) {
        ulong flashbase = 0;
 

         /*保存flash ID*/

         flash_info[i].flash_id =
#if defined(CONFIG_AMD_LV400)
            (AMD_MANUFACT & FLASH_VENDMASK) |
            (AMD_ID_LV400B & FLASH_TYPEMASK);
#elif defined(CONFIG_AMD_LV800)
            (AMD_MANUFACT & FLASH_VENDMASK) |
            (AMD_ID_LV800B & FLASH_TYPEMASK);
#else
#error "Unknown flash configured"
#endif

            /*保存每个flash blank的大小,sector数量,起始地址等信息*/
            flash_info[i].size = FLASH_BANK_SIZE;
        flash_info[i].sector_count = CFG_MAX_FLASH_SECT;
        memset (flash_info[i].protect, 0, CFG_MAX_FLASH_SECT);
        if (i == 0)
            flashbase = PHYS_FLASH_1;
        else
            panic ("configured too many flash banks!\n");

         /*为每个sector分配不同的大小,作为不同的用途*/

        for (j = 0; j < flash_info[i].sector_count; j++) {
            if (j <= 3) {
                /* 1st one is 16 KB */
                if (j == 0) {
                    flash_info[i].start[j] =
                        flashbase + 0;
                }

                /* 2nd and 3rd are both 8 KB */
                if ((j == 1) || (j == 2)) {
                    flash_info[i].start[j] =
                        flashbase + 0x4000 + (j -
                                      1) *
                        0x2000;
                }

                /* 4th 32 KB */
                if (j == 3) {
                    flash_info[i].start[j] =
                        flashbase + 0x8000;
                }
            } else {
                flash_info[i].start[j] =
                    flashbase + (j - 3) * MAIN_SECT_SIZE;
            }
        }
        size += flash_info[i].size;
    }

      /*对flash上保存有RO, RW的地方进行保护,monitor_flash_len = RO + RW的长度*/
    flash_protect (FLAG_PROTECT_SET,
               CFG_FLASH_BASE,
               CFG_FLASH_BASE + monitor_flash_len - 1,
               &flash_info[0]);

   //对flash上保存有环境变量的地方进行保护
    flash_protect (FLAG_PROTECT_SET,
               CFG_ENV_ADDR,
               CFG_ENV_ADDR + CFG_ENV_SIZE - 1, &flash_info[0]);

    return size;
}

display_flash_config

位置:lib_arm/board.c:

功能:打印flash的大小信息

#ifndef CFG_NO_FLASH

static void display_flash_config (ulong size)

{

       puts ("Flash: ");

       print_size (size, "\n");

}

#endif

mem_malloc_ini

位置:lib_arm/board.c:

功能:保存malloc区域的起始地址,结束地址,并清0这块区域,这块区域在RAM中的具体位置可查看前面的图。

/*
 * Begin and End of memory area for malloc(), and current "brk"
 */
static ulong mem_malloc_start = 0;
static ulong mem_malloc_end = 0;
static ulong mem_malloc_brk = 0;

static
void mem_malloc_init (ulong dest_addr)
{
    mem_malloc_start = dest_addr;
    mem_malloc_end = dest_addr + CFG_MALLOC_LEN;
    mem_malloc_brk = mem_malloc_start;

    memset ((void *) mem_malloc_start, 0,
            mem_malloc_end - mem_malloc_start);
}


    env_relocate ();

位置:common\env_common.c中 

功能:配置环境变量,重新定位 ,该函数实现真正的重定位功能,先从NAND flash中读取环境变量,如果读取成功,并且crc校验正确的话,就使用NAND flash中读取出来的环境变量,否则使用默认的环境变量

void env_relocate (void)
{
    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);
    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;

    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 ();
    }
    gd->env_addr = (ulong)&(env_ptr->data);

#ifdef CONFIG_AMIGAONEG3SE
    disable_nvram();
#endif
}

devices_init:

位置: common/devices.c:

功能:根据板子的配置初始化各种设备,并调用device_register()注册到系统中去,在这个函数中仅对串口设备初始化,然后注册串口设备

int devices_init (void)
{
#ifndef CONFIG_ARM     /* already relocated for current ARM implementation */
    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

创建一个保存device的列表
    /* 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)   //没定义
    i2c_init (CFG_I2C_SPEED, CFG_I2C_SLAVE);
#endif
#ifdef CONFIG_LCD    //没定义
    drv_lcd_init ();
#endif
#if defined(CONFIG_VIDEO) || defined(CONFIG_CFB_CONSOLE)    //没定义
    drv_video_init ();
#endif
#ifdef CONFIG_KEYBOARD     //没定义
    drv_keyboard_init ();
#endif
#ifdef CONFIG_LOGBUFFER     //没定义
    drv_logbuff_init ();
#endif
    drv_system_init ();         //这里其实是定义了一个串口设备,并且注册到devlist中
#ifdef CONFIG_SERIAL_MULTI     //没定义
    serial_devices_init ();
#endif
#ifdef CONFIG_USB_TTY
    drv_usbtty_init ();
#endif
#ifdef CONFIG_NETCONSOLE     //没定义
    drv_nc_init ();
#endif

    return (0);

}


以 drv_system_init为例解释一下,定义也在common/devices.c,其他代码类似。

该函数注册了一个串口设备和一个空设备(根据配置而定)。其他的设备初始化函数以此大同小异,主要就是初始化好相关设备的设备信息,并注册到系统中去,详细代码大家可以自己去分析。

static void drv_system_init (void)

{

       device_t dev;

 

       memset (&dev, 0, sizeof (dev));

 

       strcpy (dev.name, "serial"); /*串口设备*/

       dev.flags = DEV_FLAGS_OUTPUT | DEV_FLAGS_INPUT | DEV_FLAGS_SYSTEM; /*设备属性*/

 

#ifdef CONFIG_SERIAL_SOFTWARE_FIFO

    /*注册设备操作函数集(输入函数,输出函数等)*/

       dev.putc = serial_buffered_putc;

       dev.puts = serial_buffered_puts;

       dev.getc = serial_buffered_getc;

       dev.tstc = serial_buffered_tstc;

#else

       dev.putc = serial_putc;

       dev.puts = serial_puts;

       dev.getc = serial_getc;

       dev.tstc = serial_tstc;

#endif

 

       device_register (&dev); /*把该设备注册进系统中去,即把dev添加进上面创建的设备列表中去*/

 

#ifdef CFG_DEVICE_NULLDEV

       memset (&dev, 0, sizeof (dev));

 

       strcpy (dev.name, "nulldev");

       dev.flags = DEV_FLAGS_OUTPUT | DEV_FLAGS_INPUT | DEV_FLAGS_SYSTEM;

       dev.putc = nulldev_putc;

       dev.puts = nulldev_puts;

       dev.getc = nulldev_input;

       dev.tstc = nulldev_input;

 

       device_register (&dev);

#endif

}

jumptable_init ();

功能:初始化gd->jt, 1.1.6版本的jumptable只起登记函数地址的作用,并没有其他作用。

位置:common/exports.c

void jumptable_init (void)
{
    int i;

    /*分配一块buffer用于存放跳转函数地址*/
    gd->jt = (void **) malloc (XF_MAX * sizeof (void *));

    for (i = 0; i < XF_MAX; i++)
        gd->jt[i] = (void *) dummy;   /*默认跳转函数地址,是一个空函数的地址*/

  

    /*为每种功能定义各自的跳转函数*/
    gd->jt[XF_get_version] = (void *) get_version;
    gd->jt[XF_malloc] = (void *) malloc;
    gd->jt[XF_free] = (void *) free;
    gd->jt[XF_getenv] = (void *) getenv;
    gd->jt[XF_setenv] = (void *) setenv;
    gd->jt[XF_get_timer] = (void *) get_timer;
    gd->jt[XF_simple_strtoul] = (void *) simple_strtoul;
    gd->jt[XF_udelay] = (void *) udelay;
#if defined(CONFIG_I386) || defined(CONFIG_PPC)
    gd->jt[XF_install_hdlr] = (void *) irq_install_handler;
    gd->jt[XF_free_hdlr] = (void *) irq_free_handler;
#endif    /* I386 || PPC */
#if (CONFIG_COMMANDS & CFG_CMD_I2C)
    gd->jt[XF_i2c_write] = (void *) i2c_write;
    gd->jt[XF_i2c_read] = (void *) i2c_read;
#endif    /* CFG_CMD_I2C */

}

/* 完整地初始化控制台设备 */

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

位置:函数在common\console.c */

功能:初始化控制台设备二阶段

int console_init_r (void)
{
    char *stdinname, *stdoutname, *stderrname;
    device_t *inputdev = NULL, *outputdev = NULL, *errdev = NULL;
#ifdef CFG_CONSOLE_ENV_OVERWRITE
    int i;
#endif /* CFG_CONSOLE_ENV_OVERWRITE */

    /* set default handlers at first */
    gd->jt[XF_getc] = serial_getc;
    gd->jt[XF_tstc] = serial_tstc;
    gd->jt[XF_putc] = serial_putc;
    gd->jt[XF_puts] = serial_puts;
    gd->jt[XF_printf] = serial_printf;

    /* stdin stdout and stderr are in environment */
    /* scan for it */
    stdinname  = getenv ("stdin");
    stdoutname = getenv ("stdout");
    stderrname = getenv ("stderr");

    if (OVERWRITE_CONSOLE == 0) {     /* if not overwritten by config switch */
        inputdev  = search_device (DEV_FLAGS_INPUT,  stdinname);
        outputdev = search_device (DEV_FLAGS_OUTPUT, stdoutname);
        errdev    = search_device (DEV_FLAGS_OUTPUT, stderrname);
    }
    /* if the devices are overwritten or not found, use default device */
    if (inputdev == NULL) {
        inputdev  = search_device (DEV_FLAGS_INPUT,  "serial");         /*找到输入设备,参考drv_system_init */
    }
    if (outputdev == NULL) {
        outputdev = search_device (DEV_FLAGS_OUTPUT, "serial");    / *找到输出设备,参考drv_system_init */
    }
    if (errdev == NULL) {
        errdev    = search_device (DEV_FLAGS_OUTPUT, "serial");      /*找到出错输出设备*/
    }

    /*由drv_system_init可知,我们可以找到输入,输出设备,而且都是同一个serial设备*/
    /* Initializes output console first */
    if (outputdev != NULL) {
        console_setfile (stdout, outputdev);     /*设置标准输出设备*/
    }
    if (errdev != NULL) {
        console_setfile (stderr, errdev);    /*设置标准的错误输出设备*/
    }
    if (inputdev != NULL) {
        console_setfile (stdin, inputdev);      /*设置标准输入设备*/
    }

    gd->flags |= GD_FLG_DEVINIT;    /* device initialization completed */

#ifndef CFG_CONSOLE_INFO_QUIET

/*打印相关信息*/
    /* Print information */
    puts ("In:    ");
    if (stdio_devices[stdin] == NULL) {
        puts ("No input devices available!\n");
    } else {
        printf ("%s\n", stdio_devices[stdin]->name);
    }

    puts ("Out:   ");
    if (stdio_devices[stdout] == NULL) {
        puts ("No output devices available!\n");
    } else {
        printf ("%s\n", stdio_devices[stdout]->name);
    }

    puts ("Err:   ");
    if (stdio_devices[stderr] == NULL) {
        puts ("No error devices available!\n");
    } else {
        printf ("%s\n", stdio_devices[stderr]->name);
    }
#endif /* CFG_CONSOLE_INFO_QUIET */

#ifdef CFG_CONSOLE_ENV_OVERWRITE
    /* set the environment variables (will overwrite previous env settings) */
    for (i = 0; i < 3; i++) {
        setenv (stdio_names[i], stdio_devices[i]->name);
    }
#endif /* CFG_CONSOLE_ENV_OVERWRITE */

#if 0
    /* If nothing usable installed, use only the initial console */
    if ((stdio_devices[stdin] == NULL) && (stdio_devices[stdout] == NULL))
        return (0);
#endif
    return (0);
}





main_loop函数我们放在下一篇详细分析。。。。。。ok


参考:http://blog.chinaunix.net/uid-14114479-id-3145513.html

http://blog.csdn.net/liukun321/article/details/5680504

还有一些没法找到名字的 在此也感谢

大家发现什么问题 或者描述的不恰当的地方,欢迎评论

抱歉!评论已关闭.