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

来源:互联网 发布:mac打不开加密的pdf 编辑:程序博客网 时间:2024/06/06 00:56

start_armboot函数(五)

注:本次分析的u-boot是九鼎官方的u-boot代码
下载地址:链接:http://pan.baidu.com/s/1gfpDZqj 密码:7cqe


上一章结束,一些板级的初始化已经完成,下面从Board.c的489行继续开始;

1.初始化Norflash并打印配置信息

#ifndef CFG_NO_FLASH    /* configure available FLASH banks */    size = flash_init ();    display_flash_config (size);#endif /* CFG_NO_FLASH */

(1)虽然NandFlash和NorFlash都是Flash,但是一般NandFlash会简称为Nand而不是Flash,一般讲Flash都是指的Norflash。这里2行代码是Norflash相关的。
(2)flash_init执行的是开发板中对应的NorFlash的初始化、display_flash_config打印的也是NorFlash的配置信息(Flash: 8 MB就是这里打印出来的)。

2.初始化u-boot堆管理器

#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

(1)mem_malloc_init函数用来初始化uboot的堆管理器,传入的参数是一个起始地址;
(2)uboot中自己维护了一段堆内存,肯定自己就有一套代码来管理这个堆内存。有了这些东西uboot中你也可以malloc、free这套机制来申请内存和释放内存。我们在DDR内存中给堆预留了896KB的内存。

3.MMC控制器的初始化
从536到768行为开发板独有的初始化。意思是三星用一套uboot同时满足了好多个系列型号的开发板,然后在这里把不同开发板自己独有的一些初始化写到了这里。用#if条件编译配合CONFIG_xxx宏来选定特定的开发板。
和我们x210相关的是其中的599—632行,代码如下:

#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();//nand的初始化    #endif#endif /* CONFIG_X210 */

主要是调用了mmc_initialize函数,进入mmc_initialize函数,可见在函数里继续调用了 cpu_mmc_init函数

int cpu_mmc_init(bd_t *bis){#ifdef CONFIG_S3C_HSMMC    setup_hsmmc_clock();//初始化MMC控制器时钟    setup_hsmmc_cfg_gpio();//初始化MMC控制器GPIO    return smdk_s3c_hsmmc_init();#else    return 0;#endif}

如果初始化失败,则执行代码:

printf("Card init fail!\n");return err;

如果成功则执行代码:

printf("%ldMB\n", (mmc->capacity/(1024*1024/(1<<9))));return 0;

最后会在uboot启动时打印出“SD/MMC: xxMB”。

4.env_relocate
(1)函数功能是将环境变量从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)真正执行重定位的代码是env_relocate_spec函数,代码如下:

void env_relocate_spec (void){#if !defined(ENV_IS_EMBEDDED)    uint *magic = (uint*)(PHYS_SDRAM_1);    if ((0x24564236 != magic[0]) || (0x20764316 != magic[1]))        movi_read_env(virt_to_phys((ulong)env_ptr));    if (crc32(0, env_ptr->data, ENV_SIZE) != env_ptr->crc)        return use_default();#endif /* ! ENV_IS_EMBEDDED */}

从代码分析中可以看出movi_read_env函数执行了环境变量的重定位;

5.获取IP地址

gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");

这句代码的意思是获取环境变量中IP地址的值,赋值到bd中去;
getenv_IPaddr函数的实现方式:

IPaddr_t getenv_IPaddr (char *var){    return (string_to_ip(getenv(var)));}

是先由getenv函数获取字符串格式的IP地址,然后用string_to_ip将字符串格式的IP地址转成字符串格式的点分十进制格式。

6.获取MAC地址

/* MAC Address */    {        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;        }

7.devices_init
这个函数的功能是初始化驱动设备,uboot的这个函数其实就是从linux内核中移植过来的,它的作用也是去执行所有的从linux内核中继承来的那些硬件驱动的初始化函数。

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

9.enable_interrupts
(1)看名字应该是中断初始化代码。这里指的是CPSR中总中断标志位的使能。
(2)因为我们uboot中没有使用中断,因此没有定义CONFIG_USE_IRQ宏,因此我们这里这个函数是个空壳子。
(3)uboot中经常出现一种情况就是根据一个宏是否定义了来条件编译决定是否调用一个函数内部的代码。uboot中有2种解决方案来处理这种情况:方案一:在调用函数处使用条件编译,然后函数体实际完全提供代码。方案二:在调用函数处直接调用,然后在函数体处提供2个函数体,一个是有实体的一个是空壳子,用宏定义条件编译来决定实际编译时编译哪个函数进去。

10.board_late_init
开发板级别的最后一部分的初始化,函数里用了条件编译的方式选择函数体,对于x210开发板来说,这个函数为空。

11.eth_initialize
网卡芯片本身的一些初始化,但是对于x210(DM9000)来说,这个函数是空的,因为x210的网卡初始化在board_init函数中,而网卡芯片的初始化在驱动中。

12.x210_preboot_init
x210开发板在启动起来之前的一些初始化,以及LCD屏幕上的logo显示。

13.自动更新功能

/* check menukey to update from sd */    extern void update_all(void);    if(check_menu_update_from_sd()==0)//update mode    {        puts ("[LEFT DOWN] update mode\n");        run_command("fdisk -c 0",0);        update_all();    }    else        puts ("[LEFT UP] boot mode\n");

(1)uboot启动的最后阶段设计了一个自动更新的功能。就是:我们可以将要升级的镜像放到SD卡的固定目录中,然后开机时在uboot启动的最后阶段检查升级标志(是一个按键。按键中标志为”LEFT”的那个按键,这个按键如果按下则表示update mode,如果启动时未按下则表示boot mode)。如果进入update mode则uboot会自动从SD卡中读取镜像文件然后烧录到iNand中;如果进入boot mode则uboot不执行update,直接启动正常运行。
(2)这种机制能够帮助我们快速烧录系统,常用于量产时用SD卡进行系统烧录部署。

14.死循环

for (;;) {        main_loop ();    }

在uboot启动倒计时按下回车键,进入死循环:输入命令、解析命令以及执行命令。

总结:
第二阶段主要是对开发板级别的硬件、软件数据结构进行初始化:
init_sequence
cpu_init : 空的
board_init : 网卡、机器码、内存传参地址
(1)dm9000_pre_init : 网卡
(2)gd->bd->bi_arch_number : 机器码
(3)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内部

移植时的注意点:
(1)x210_sd.h头文件中的宏定义
(2)特定硬件的初始化函数位置(譬如网卡)

原创粉丝点击