uboot——启动第二阶段分析

来源:互联网 发布:舒尔特表训练软件 编辑:程序博客网 时间:2024/06/06 17:58

  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);#endifstatic 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内部

0 0
原创粉丝点击