uboot——启动第二阶段分析

  uboot中一个很长的函数start_armboot构成了整个uboot启动的第二阶段,在这个很长的函数中还调用了其他函数用来实现很多功能。
1、init_sequence

init_fnc_t *init_sequence[] = {
    cpu_init,       /* basic cpu dependent setup */
#if defined(CONFIG_SKIP_RELOCATE_UBOOT)
    reloc_init,     /* Set the relocation done flag, must
                   do this AFTER cpu_init(), but as soon
                   as possible */
#endif
    board_init,     /* basic board dependent setup */
    interrupt_init,     /* set up exceptions */
    env_init,       /* initialize environment */
    init_baudrate,      /* initialze baudrate settings */
    serial_init,        /* serial communications setup */
    console_init_f,     /* stage 1 init of console */
    display_banner,     /* say that we are here */
#if defined(CONFIG_DISPLAY_CPUINFO)
    print_cpuinfo,      /* display cpu info (and speed) */
#endif
#if defined(CONFIG_DISPLAY_BOARDINFO)
    checkboard,     /* display board info */
#endif
#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C)
    init_func_i2c,
#endif
    dram_init,      /* configure available RAM banks */
    display_dram_config,
    NULL,
};

init_sequence是一个函数指针数组,数组中存储了很多个函数指针,这些指向指向的函数都是init_fnc_t类型(特征是接收参数是void类型,返回值是int)。通过一个for循环实现遍历,将所有的函数调用。都是板级硬件的初始化以及gd、gd->bd中的数据结构的初始化。
譬如:
网卡初始化、机器码(gd->bd->bi_arch_number)、内核传参DDR地址(gd->bd->bi_boot_params)、Timer4初始化为10ms一次、波特率设置(gd->bd->bi_baudrate和gd->baudrate)、console第一阶段初始化(gd->have_console设置为1)、打印uboot的启动信息、打印cpu相关设置信息、检查并打印当前开发板名字、DDR配置信息初始化(gd->bd->bi_dram)、打印DDR总容量。
cpu_init 空的
board_init 网卡、机器码、内存传参地址
dm9000_pre_init 网卡
gd->bd->bi_arch_number 机器码
gd->bd->bi_boot_params 内存传参地址
interrupt_init 定时器
env_init
init_baudrate gd数据结构中波特率
serial_init 空的
console_init_f 空的
display_banner 打印启动信息
print_cpuinfo 打印CPU时钟设置信息
checkboard 检验开发板名字
dram_init gd数据结构中DDR信息
display_dram_config 打印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、mem_malloc_init
初始化uboot自己维护的堆管理器的内存
(1)mem_malloc_init函数用来初始化uboot的堆管理器。
(2)uboot中自己维护了一段堆内存,肯定自己就有一套代码来管理这个堆内存。有了这些东西uboot中你也可以malloc、free这套机制来申请内存和释放内存。我们在DDR内存中给堆预留了896KB的内存。

    /* armboot_start is defined in the board-specific linker script */
#ifdef CONFIG_MEMORY_UPPER_CODE /* by scsuh */
    mem_malloc_init (CFG_UBOOT_BASE + CFG_UBOOT_SIZE - CFG_MALLOC_LEN - CFG_STACK_SIZE);
#else
    mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);
#endif


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);
}

3、mmc_initialize
inand/SD卡的SoC控制器和卡的初始化
(1)mmc_initialize看名字就应该是MMC相关的一些基础的初始化,其实就是用来初始化SoC内部的SD/MMC控制器的。函数在uboot/drivers/mmc/mmc.c里。
(2)uboot中对硬件的操作(譬如网卡、SD卡···)都是借用的linux内核中的驱动来实现的,uboot根目录底下有个drivers文件夹,这里面放的全都是从linux内核中移植过来的各种驱动源文件。
(3)mmc_initialize是具体硬件架构无关的一个MMC初始化函数,所有的使用了这套架构的代码都掉用这个函数来完成MMC的初始化。mmc_initialize中再调用board_mmc_init和cpu_mmc_init来完成具体的硬件的MMC控制器初始化工作。
(4)cpu_mmc_init在uboot/cpu/s5pc11x/cpu.c中,这里面又间接的调用了drivers/mmc/s3c_mmcxxx.c中的驱动代码来初始化硬件MMC控制器。这里面分层很多,分层的思想一定要有,否则完全就糊涂了。

#if defined(CONFIG_X210)

    #if defined(CONFIG_GENERIC_MMC)
        puts ("SD/MMC:  ");
        mmc_exist = mmc_initialize(gd->bd);
        if (mmc_exist != 0)
        {
            puts ("0 MB\n");
#ifdef CONFIG_CHECK_X210CV3
            check_flash_flag=0;//check inand error!
#endif
        }
#ifdef CONFIG_CHECK_X210CV3
        else
        {
            check_flash_flag=1;//check inand ok! 
        }
#endif
    #endif

        #if defined(CONFIG_MTD_ONENAND)
            puts("OneNAND: ");
            onenand_init();
            /*setenv("bootcmd", "onenand read c0008000 80000 380000;bootm c0008000");*/
        #else
            //puts("OneNAND: (FSR layer enabled)\n");
        #endif

        #if defined(CONFIG_CMD_NAND)
            puts("NAND:    ");
            nand_init();
        #endif

#endif /* CONFIG_X210 */

4、env_relocate
环境变量重定位
1)env_relocate是环境变量的重定位,完成从SD卡中将环境变量读取到DDR中的任务。
(2)环境变量到底从哪里来?SD卡中有一些(8个)独立的扇区作为环境变量存储区域的。但是我们烧录/部署系统时,我们只是烧录了uboot分区、kernel分区和rootfs分区,根本不曾烧录env分区。所以当我们烧录完系统第一次启动时ENV分区是空的,本次启动uboot尝试去SD卡的ENV分区读取环境变量时失败(读取回来后进行CRC校验时失败),我们uboot选择从uboot内部代码中设置的一套默认的环境变量出发来使用(这就是默认环境变量);这套默认的环境变量在本次运行时会被读取到DDR中的环境变量中,然后被写入(也可能是你saveenv时写入,也可能是uboot设计了第一次读取默认环境变量后就写入)SD卡的ENV分区。然后下次再次开机时uboot就会从SD卡的ENV分区读取环境变量到DDR中,这次读取就不会失败了。
(3)真正的从SD卡到DDR中重定位ENV的代码是在env_relocate_spec内部的movi_read_env完成的。

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

    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 (-60);
#endif
        set_default_env();
    }
    else {
        env_relocate_spec ();
    }
    gd->env_addr = (ulong)&(env_ptr->data);

#ifdef CONFIG_AMIGAONEG3SE
    disable_nvram();
#endif
}

5、 devices_init 空的
jumptable_init 不用关注的

devices_init ();    /* get the devices list going. */
jumptable_init ();

6、console_init_r
真正的控制台初始化
(1)console_init_f是控制台的第一阶段初始化,console_init_r是第二阶段初始化。实际上第一阶段初始化并没有实质性工作,第二阶段初始化才进行了实质性工作。
(2)console_init_r就是console的纯软件架构方面的初始化(说白了就是去给console相关的数据结构中填充相应的值),所以属于纯软件配置类型的初始化。
(3)uboot的console实际上并没有干有意义的转化,它就是直接调用的串口通信的函数。所以用不用console实际并没有什么分别。(在linux内console就可以提供缓冲机制等不用console不能实现的东西)。

/* Called after the relocation - use desired console functions */
int console_init_r (void)
{
    device_t *inputdev = NULL, *outputdev = NULL;
    int i, items = ListNumItems (devlist);

#ifdef CONFIG_SPLASH_SCREEN
    /* suppress all output if splash screen is enabled and we have
       a bmp to display                                            */
    if (getenv("splashimage") != NULL)
        gd->flags |= GD_FLG_SILENT;
#endif

    /* Scan devices looking for input and output devices */
    for (i = 1;
         (i <= items) && ((inputdev == NULL) || (outputdev == NULL));
         i++
        ) {
        device_t *dev = ListGetPtrToItem (devlist, i);

        if ((dev->flags & DEV_FLAGS_INPUT) && (inputdev == NULL)) {
            inputdev = dev;
        }
        if ((dev->flags & DEV_FLAGS_OUTPUT) && (outputdev == NULL)) {
            outputdev = dev;
        }
    }

    /* Initializes output console first */
    if (outputdev != NULL) {
        console_setfile (stdout, outputdev);
        console_setfile (stderr, outputdev);
    }

    /* Initializes input console */
    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 */

#ifndef CONFIG_X210
    /* Setting environment variables */
    for (i = 0; i < 3; i++) {
        setenv (stdio_names[i], stdio_devices[i]->name);
    }
#endif

#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);
}

7、enable_interrupts 、 board_late_init 和 eth_initialize
空的

void enable_interrupts (void)
{
    return;
}

8、x210_preboot_init
LCD初始化和显示logo

int x210_preboot_init(void)
{
    mpadfb_init();
    return 1;
}

9、check_menu_update_from_sd
检查自动更新

/*
 * GPH0_2: LEFT
 */
static int check_menu_update_from_sd(void)
{
    unsigned int i;
    unsigned int reg;

    //GPH0_2
    reg = readl(GPH0CON);
    reg = reg & ~(0xf<<8) | (0x0<<8);
    writel(reg,GPH0CON);

    for(i=0;i<100;i++)
        udelay(500);

    reg = readl(GPH0DAT);
    reg = reg & (0x1<<2);

    if(reg)
        return 1;
    else //update mode
        return 0;
}

10、main_loop
主循环,不负责任的认为是一个死循环。

总结:第二阶段主要是对开发板级别的硬件、软件数据结构进行初始化。
init_sequence
  cpu_init   空的
   board_init   网卡、机器码、内存传参地址
   dm9000_pre_init   网卡
   gd->bd->bi_arch_number   机器码
   gd->bd->bi_boot_params   内存传参地址
  interrupt_init   定时器
   env_init
   init_baudrate   gd数据结构中波特率
   serial_init    空的
   console_init_f   空的
   display_banner   打印启动信息
   print_cpuinfo   打印CPU时钟设置信息
   checkboard    检验开发板名字
   dram_init   gd数据结构中DDR信息
   display_dram_config   打印DDR配置信息表
mem_malloc_init   初始化uboot自己维护的堆管理器的内存
mmc_initialize    inand/SD卡的SoC控制器和卡的初始化
env_relocate   环境变量重定位
gd->bd->bi_ip_addr   gd数据结构赋值
gd->bd->bi_enetaddr   gd数据结构赋值
devices_init   空的
jumptable_init   不用关注的
console_init_r   真正的控制台初始化
enable_interrupts   空的
loadaddr、bootfile   环境变量读出初始化全局变量
board_late_init    空的
eth_initialize   空的
x210_preboot_init   LCD初始化和显示logo
check_menu_update_from_sd   检查自动更新
main_loop   主循环

启动过程特征总结
(1)第一阶段为汇编阶段、第二阶段为C阶段
(2)第一阶段在SRAM中、第二阶段在DRAM中
(3)第一阶段注重SoC内部、第二阶段注重SoC外部Board内部

你可能感兴趣的:(uboot——启动第二阶段分析)